cbonami (3) [Avatar] Offline
#1
I think I cannot agree (and I think Eric Evans would support me in this), that an aggregate is in the first place an interface and a means to hide internals of the other objects that participate in the aggregate. This is fundamentally wrong.

An aggregate consists of at least on entity and one or more immutable value objects.
Entity has and id and is mutable. ValueObject cannot be identified with an id, and is immutable. The aggregate(-root) is the guardian of 1 or more so called 'true invariants' i.e. business rule(s) that must be satisfied immediate and at all times. There shouldn't be a millisecond in the lifecycle of an aggregate in the system (memory, db, etc) that the aggregate's true invariant(s) are not satisfied. This is called 'immediate consistency'. On the other side, there's a concept of 'eventual consistency' i.e. business rules that should be satisfied at a certain moment in time ('eventually'), but not necessarily immediate. If for 2 entities such a 'true invariant' rule cannot be found (or is simply not required), these 2 entities will not participate in the same aggregate. They will most likely be 2 separate aggregates. And that is a good thing, as aggregates must be kept as small as possible.
This means that an aggregate is in the first place a transactional concept. It has far less envolvement with encapsulation and information hiding, although, of course, this can be one of the purposes of an aggregate too.

Also, an aggregate cannot contain other aggregate(s). That is a logical consequence of the 'true invariant'
If you do not agree with me, I highly suggest that you read the excellent book 'Implementing Domain Driven Design' (http://www.amazon.co.uk/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577).

To be completely honest to you, I bought your book because I think it started excellently, but - with all respect - I was appalled by some of the statements you make in your first chapter. I am not the only person that read this chapter, and other people like me (that, like me, work with DDD and the Axon framework on a daily basis), had the same reflexes and remarks.
debasish.ghosh (44) [Avatar] Offline
#2
Re: Aggregates are not interfaces but immediate consistency enforcers
Thanks for the post. Let me clarify my stand on this and then we can of course discuss more.

I agree that an aggregate defines a consistency boundary. Eric Evans calls it "a cluster of associated objects that we treat as a unit for the purpose of data changes". However he goes on to say "Each AGGREGATE has a root and a boundary. The boundary defines what is inside the AGGREGATE. The root is a single, specific entity contained in the AGGREGATE. The root is the only member of the AGGREGATE that outside objects are allowed to hold references to, although objects within the boundary may hold references to each other."

The last part of his statement clearly makes an AGGREGATE a single point window to its clients that encapsulates the internals from the external world. What's wrong in this interpretation? You can of course say that I haven't put enough emphasis on the consistency boundary part. I will make that change and do so. Thanks for pointing this out.

Regarding the other part "an aggregate cannot contain other aggregate(s)". In a typical mutable model of the world, we need to have 1 aggregate and have the associations to others using identities (as Vaughn Vernon describes in http://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_2.pdf. But I think there can be instances where you can have one aggregate root within another so long one doesn't modify the other. As an example have a look at Page 127 of the DDD book by Eric Evans - he demonstrates how we can have Car and Engine both as aggregate roots, but there's no way you can change one from the other.

In the approach that I describe in the book, aggregates and entities will be modeled as immutable objects - in place mutation is not done. So we don't have mutable state. Hence consistency boundary is more easily defined. And one aggregate root cannot modify the state of another. All changes to aggregates are done through Commands (the C in the CQRS) and it's the command's responsibility to ensure that we get back a consistent aggregate on instantiation.

Feel free to discuss your views. I am trying to look at DDD from the functional perspective where immutability is the basis of the model, events are the main agents that drive the changes in the model.

Thanks.

Message was edited by:
debasish.ghosh
debasish.ghosh (44) [Avatar] Offline
#3
Re: Aggregates are not interfaces but immediate consistency enforcers
Also in continuation of my last post ..

I admit that aggregate root within an aggregate root (a la Car Engine example in DDD book that I cited in my last post) is not a very common modeling occurrence. In fact in the MEAP did I mention anywhere that an aggregate can contain another aggregate ? I just did a quick search in chapter 1 and couldn't find any place. I did mention though that an aggregate can contain other entities, which, I think makes sense. WDYT ?

Also it will help a lot if u give some pointers to the DDD statements in the book which you found "appalling" ..

Thanks.
cbonami (3) [Avatar] Offline
#4
Re: Aggregates are not interfaces but immediate consistency enforcers
Hi again. Sorry for my strong wording, I'll try to keep more polite from now on smilie

First my reply on the first part about consistency boundary versus encapsulation.
I will be the last person on earth to deny that the AR is the one-and-only window/interface to the services of the Aggregate (encapsulation). That's for sure istrue. BUT .... that is NOT the first/main reason why you put the AR in an aggregate structure in which 0/more other entities and 0/more value objects participate. The main reason is that they are bound by at least one "true invariant" (this is a business concept and has nothing to do with mutability or immutability) and hence require immediate consistency among all parts of the aggregate. Thàt and only that is the criterion that functional analysts and designers use to determine whether two distinct objects go in the same aggregate or not. This might lead to situations where:
- seen from a data-, ERD- or ORM- point of view you see a heavy coupling between A and B and hence would be inclined to put them together in the same aggregate
- but from a true invariant perspective you still put them in separate aggregates; which is good because you strive for small aggregates: the smaller they are, the more decoupled they are from other aggregates, the smaller/shorter the ACID-transactions that need to interact with and 'change' them, the higher the throughput of your system; also -as there are only events between different aggregates - your loose-coupled aggregate can easily be discarded from your domain model and replaced by something else, or be physically moved to another platform/location/system without the rest of the application being impacted by such a migration. Thìs is where the power of DDD lies, this is what DDD is all about.

Example: Order - OrderItem
Or: Shipping - ShippedGood

First reaction: same aggregate.
Now look closer and ask yourself : can I find a business(!) rule (<> referential data integrity) that states that Order and OrderItem should be always treated in one and the same, single, ACID transaction (-> immediate consistency) ?
In many cases the answer will be : no. It is possible to modify, add, delete OrderItem-s, and modify Order-data, and all this concurrently (multiple users, multiple execution threads).
Unless there is a IMMEDIATE consistency rule that says, for example, the Order keeps a counter, or a total amount, and AT ALL TIMES this count should be 100% right. From a business(!) point of view it is a hard requirement that this is the case.
But in most cases such a demanding business rule simply is not needed or required by the business. For the business it is quite acceptable that, as orderitems or shipped items are created, changed, removed, and orders themselves are modified by many many users (imagine that is the case for the purpose of this example smilie ), there might be a moment of time that 'the total amount' or whatever that is kept or calculated by the order is not in sync with reality. Let's say, for example, that the max amount is capped - i.e. one should not order more than 100 items. So it might be that - due to latency etc etc- two people add an orderitem at the same time and their immediate consistent transactions succeed. However, asynchronously and EVENTUALLY, the - I guess - OrderSaga that has been listening to and monitoring the events coming from Order and OrderItem sees/calculates that the total is 101, instead of the max 100.
What then? As the business rule 'max 100 items' is NOT considered as a true invariant, and -hence- we expect that this situation actually is very unlikely to happen (an 'edge' or 'corner' case), we will COMPENSATE the violation of the rule: we will send one of the 2 people an email: "sorry, after checking our total order or shipping, we saw that there was no room for your last orderitem anymore. We will schedule it for the next shipping. Please send us an email if you do not agree, and then we can find for another solution".
So: the transaction 'add orderitem' could successfully succeed with blocking/locking Order i.e. without inducing wait-times for other users that also register their orderItems. By loosening the CAP theorem at the C(onsistency(-side, we gained P(erformance).

The notion of true invariancy is of course transitive. If A and B are bound by a true invariant, and B and C are too, then A, B ànd C are in the same aggregate. That's why aggregates containing other aggregates is meaningless from a transactional point of view.

On your blog you state : "Note an aggregate can consist of other aggregates - e.g. we have a Customer instance within an Order. "
I think this is a) not true, and b) if it were true, I would find it very unlikely that those 2 entities show up in the same quite coarse aggregate. It would mean that Customer (responsible for identity services like getFirstName(), getName(), getEmail() etc) and Order (x units of some product) are bound by a true invariant that states that they always need to be locked by the same ACID transaction whenever one or the other receives a instruction/update. On the command-side, these 2 aggregates would be completely unaware of eachother, I guess. The only coupling would come from the fact that an Order 'knows' the id (uuid) of the Customer. That's it. Further than that the awareness of Customer-s by Order-s doesn't go and shouldn't go. That is the minimal coupling needed to get things going.
On the query-side (at least, if you're doing CQRS there is a high probability that the 2 concepts are linked by a ForeignKey relationship, or some denormalized view where you put them together; because there, you want to 'navigate' (ORM-wise, REST-wise) from one to the other.

We think - but we have to check and read the page of his book- that the example of Evans where he puts Engine-aggregate inside the Car-aggregate, is based on considerations other than transactionality, but rather due to technical ORM-restrictions (Engine is 'embedded' in Car (traditional UML-composition) from a data-oriented point-of-view, or so, we will check).

One last remark: the example with the Balance and the credit() and debit() methods. We have difficulties getting our heads around that. Seen through OO-glasses, these 2 methods should never be on Balance. From a Real World OO model perspective, one does not credit a balance. One credits/debits an Account. The balance is the result of such operation. What is the identity of a Balance ?? The best response to this is: the Account. Balance is a ValueObject at most, inside the Account aggregate. Maybe we are paralysed by an obsolete OO mindset (are we ?) but debiting/crediting balances sounds absurd and wrong to us.

Message was edited by:
cbonami

Message was edited by:
cbonami
debasish.ghosh (44) [Avatar] Offline
#5
Re: Aggregates are not interfaces but immediate consistency enforcers
Thanks a ton for the detailed response. Very much appreciated.

I will change the next MEAP keeping in mind the suggestions that you have made here ..

(1) More emphasis on the consistency boundary of aggregates
(2) Changing the example of Balance and making Balance a VO of the Account aggregate. I have already thought about it and it makes a nice fit in the 3 cases that I present in the book - (a) mutable model (b) immutable but still with state and behavior coupled (c) immutable Account with only the state and a domain service that contains behaviors like debit and credit.

Hope you like it better ..

Thanks again for the detailed feedback.
cbonami (3) [Avatar] Offline
#6
Re: Aggregates are not interfaces but immediate consistency enforcers
My pleasure.