I'm working on an e-commerce solution and as part of this we have concepts of things like Stores, Currencies, Payment Methods, Shipping Methods to name a few. For each of these, we have an admin interface so developers can setup a new currency or payment method and configure it's settings. For example, for a currency they could configure to say use a specific symbol for price formatting.
Because these things require direct administration like this, I've understood this to mean that each of these things are DDD aggregate roots and so this is how I've implemented them.
Now, when we come on to the Order aggregate, this then needs to hold reference to some of these other aggregates and also use their values, for example, an order has a Currency property and that Currency needs to be used to format prices in the order.
The thing I'm struggling with is that as I understand it, it's recommended not to reference aggregates directly, instead only reference it's ID. If that is the case, how would I then be able to use the Currency values to format prices without performing constant lookups?
Is my understanding of aggregates correct here? and if so, how should I make reference to the other aggregates without providing folks with the ability to perform actions on those aggregates? (ie, yes an Order has a Currency, but people shouldn't be able to do order.Currency.DoSomething()
).
My current thought is that I pass the other aggregates into the order like order.ChangeCurrency(currency);
but inside this method convert CurrencyAggregate
into some kind of CurrencyReferenceEntity
which holds the info I will need from the aggregate. Does this sound logical? Or I'm not understanding a key element?
Currency
an entity? Most domains would treatCurrency
as a value object. Following suit would alleviate your problem. FWIW the difference between and entity and an aggregate is conceptual semantics. This "label" can be useful to help understand how a domain works, but from the outside a client (i.e service layer) needn't know the difference. They are both single objects with a unique identity/life-cycle that enforce invariants. The inner-workings are (and should be) a black box.