Splitting nested entities keeping invariant within aggregates in DDD

473 Views Asked by At

I'm currently developing message imap-based module in crm app and i'm trying to connect dots using DDD principals.

I've started with three main entities, all bound under Account Aggregate:

  • Account - Email account. Has multiple Folders.
  • Folder - Folder in email account (ex. Inbox, Sent, Draft). Has multiple Messages.
  • Message - Email message.

In this case invariant is on the actual collections, mostly between Account and Folder:

  • Account can't have two folders with the same name,

  • Account can't have multiple special folders (like Inbox or Sent),

  • Mailbox should have actual message count.

    Initial aggregate here

This design was nothing less then flawless. It keeps invariant, but almost every account has around six folders, that can contains thousands of messages. The amount of data loaded to memory, that was almost never used forced idea to split Account Aggregate.

Now, I've got options:

  1. Split it to two aggregates: Account (with Folder as local entity) and Message.

    Two aggregate split here

In this case I'm violating rule of referencing local entity (Folder) to another aggregate (Message), because message is bounded to specific folder. Yet I can keep most invariant between Account and Folder intact.

  1. Split them into three separate aggregates: Account, Folder and Message.

    Three aggregate split here

In that case, no aggregate rules are broken, but I'm breaking invariant between Account and Folder. As a result there is need of adding another layer of complexity using domain services and threat of most likely updating at least two aggregates in one transaction. Also this forces Folder, as it's entity between aggregates to become aggregate root while it never meant be one.

  1. Something that I possibly miss.

At this point I would go with second solution, but what is the best way to split this entities in this case?

1

There are 1 best solutions below

0
Levi Ramsey On

I'm not sure that there's necessarily a requirement in DDD that every entity in an aggregate has to be loaded as a unit (though it's quite possible that many DDD-oriented frameworks enforce such a constraint). As long as any access is through the aggregate root and the invariants are consistently maintained, no DDD guidelines are actually being violated.

This may require an encoding of the model which permits lazy-loading of entities; coming at this from Scala, Future seems like a reasonable encoding of this "might-not-yet-be-here" aspect.

Absent the ability to easily account for lazy-loading, I'd probably choose a variation of the first approach but with Message not having a direct reference to a Folder. One could implement Folder in such a way (e.g. with a Bloom filter) that the search for the appropriate Folder is quick. I'm not sure what invariants Message would need to keep around Folder membership (and you don't seem to be expressing any).