Search code examples
domain-driven-designcqrs

Modelling aggregates with subtypes (DDD/CQRS)


I have a subdomain which involves tracking user financial data across different financial account types.

For example, users can input data for their:

  • bank accounts,
  • credit cards,
  • loans,
  • lines of credit,
  • real estate,
  • and more...

Now within each individual type, there are more subtypes.

For instance, under loans:

  • personal loans,
  • business loans,
  • mortgages,
  • car loans,
  • and more...

They would each have their own particular invariants, with some unique properties and functionality, and some shared properties and functionality.

I've been approaching this using composition, creating an aggregate for each subtype, and using interfaces and helper interface implementations to share similar logic between aggregates.

However, it appears as though I'm going to end up with dozens of different aggregates when modelling all these different account types. This doesn't feel right.

Alternatives I've considered:

  • have a type property on the loan aggregate, and conditional logic based off the type.
  • create different bounded contexts for each of these types: This feels like overkill, I believe this is all part of the same business subdomain.
  • create aggregates based off shared functionality - eg SecuredLoan and UnsecuredLoan aggregates
  • creating subclasses in the general aggregates to hold the subtype's unique functionality. get some encapsulation of subtype specific logic, with some conditional logic still (eg conditional properties on the aggregate). Not really sure the difference between this and just creating a separate aggregate for each subtype

Tradeoffs seem to be, the more general the implementation, there will end up being a ton of conditional logic, and conditional properties based off the subtype. Versus building specific aggregates for each subtype, the logic per aggregate is simplified, but there ends up being hundreds of commands in the application layer, a lot of them which are basically the same thing but to a different subtype. Additionally, there end up being dozens of repositories.

It feels like I either get an explosion of conditional logic complexity in a general aggregate, or an explosion of the number of aggregates (or contexts) if building one per subtype.

Question - is there a known pattern for dealing with this type of modelling problem? Or is it really just dealing with the above tradeoffs, and finding something which fits best? In that case, is there some precedent I can apply to the decision-making process, as I'm struggling to decide between the above approaches. And is it problematic if there end up being many dozens of aggregates within a given context?


Solution

  • Rather than starting with the data at rest to get your aggregates, consider instead what operations/changes ("commands" one might say) will be performed and how the results of those operations affect future operations is what leads to what the aggregates want to be. Event storming style approaches can be helpful for figuring out these relationships between state changes.

    For instance, each of these kinds of loans might have AccrueInterest, DrawPrincipal, and RecordPayment commands which operate on the balances identically (given perhaps configurable rate parameters etc.) and which don't affect and aren't affected by other commands. In that scenario, you can have a Loan aggregate which models the idea that there's a loan with interest and principal balances on which interest accrues and payments are made. An AutoLoan aggregate might then just be managing the collateralization of Loan ABC123 with VIN 1G1234567890.