I have a subdomain which involves tracking user financial data across different financial account types.
For example, users can input data for their:
Now within each individual type, there are more subtypes.
For instance, under loans:
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:
loan
aggregate, and conditional logic based off the type.SecuredLoan
and UnsecuredLoan aggregates
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?
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.