1

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?

1
  • 1
    What makes Currency an entity? Most domains would treat Currency 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. Commented Aug 28, 2019 at 18:50

2 Answers 2

1

Whether you use DDD or not, your data is always going to be linked somehow. You can't avoid that. In your case, your order (specifically its price) must be linked to a specific currency. Without it, you're going to lose out on functionality that your business requires.

Question 1: Is Currency part of the Order aggregate?

It's not quite clear from your question but I infer that e.g. the Customer aggregate also has a reference to Currency. The customer's preferred currency is then used by the order to convert the price to the currency that the customer prefers.
I'm going to build my answer on top of that assumption. I suspect my assumption will be either correct or similar enough to answer the root of your question.

This directly proves that Currency is not uniquely part of an Order, but rather an independent concept (= aggregate) that is referenced in several independent concepts (Customer and Order).

So yes, Currency is an aggregate by itself, and should be referenced (by id) by both the Customer and Order aggregates. This does require an additional lookup, but that's just how it's supposed to work. The alternative would be adding it as part of the Customer (or Order) aggregate, at which point the other aggregate would need to load the entire aggregate when it wants to access the currency.

Question 2: In DDD can an aggregate also just be an entity?

It seems like you're asking if an aggregate could (correctly) be comprised of only its aggregate root, without any data. The short answer to that is yes. While DDD very much focuses on aggregates being collections of data (i.e. multiple tables/objects), that is not an inherent requirement of each and every aggregate.

Using multiple tables/objects only makes sense in cases where there is enough data to warrant subdividing it. I expect most (if not all) projects to have some sort of tiny data set that is necessary but just not big enough to warrant subdivision.

4
  • RE Question 2 answer, the question is more could the Currency be both an aggregate for the case of administration, but an entity within Order in order to allow price formatting within Order but not allow direct Currency manipulation FROM the order reference. But given answer 1, my assumption would be no and that it should hold an ID reference and then lookup the currency to perform formatting tasks. Commented Aug 28, 2019 at 8:28
  • RE Question 1, your assumptions are correct. A Store will hold a default Currency, as may a Customer, so there are a few cross references. Commented Aug 28, 2019 at 8:33
  • @MattBrailsford: But given answer 1, my assumption would be no and that it should hold an ID reference and then lookup the currency to perform formatting tasks. I agree with this. As a caveat, if your concern is for simply looking up which currency was used in the already calculated price, you could add a value to the order (stating the currency, e.g. "$") which is technically separate from the Currency aggregate (but essentially one-way maps from it). That way, you get to store your value in the order itself but it's not used for referential integrity's sake, only for future reporting.
    – Flater
    Commented Aug 28, 2019 at 8:40
  • @Flatter that makes sense. Thank you 👍 Commented Aug 28, 2019 at 8:48
0

Referring to objects in an object-oriented language by ID instead of reference seems like a bad idea. Why would business code even know whether something has an ID or not? Why would a Currency have an ID?

Is my understanding of aggregates correct here?

Yes, according to Vaughn Vernon at least.

I think this is a bad approach for a variety of reasons. Let's list a few:

  • You have to lookup objects all the time
  • Pushes your model to be data centric
  • Introduces IDs for things that not necessarily need them
  • Introduces a vocabulary / terms that are not actually business relevant
  • etc.

and if so, how should I make reference to the other aggregates without providing folks with the ability to perform actions on those aggregates?

The key to having an expressive model is to not have "properties". Don't publish what you have, but publish what you do. An Order does not "have" a Currency, it just uses it internally. An Order should have no publicly available data only methods like cancel(), submit() or whatever.

Similarly Currency should have no publicly available data either, but what you actually need for your logic, like format(int amount) or something similar.

With this you arrive at a model that is expressive (i.e. you see what it does instead of just what data it carries), can not be used to perform actions that are not supported.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.