Behavior of aggregate roots in DDD should be as actor or not?

1.5k views Asked by At

My english bad, so i will give two examples. Let's drop some even important things from DDD in this examples, the main thing is the essence of the matter.

How to do it right from the point of view of DDD?

We have two aggregate roots, the Seller and the Advert. The Seller can edit the Advert in these examples:

1.

If the models should reflect the real business logiс. Then it is the Seller who must change Adverts. I.e client layer invoke methods changeAdvertName() and changeAdvertCost() of aggregate Seller. By the way this gives such an advantage as access check. As we can see Seller can modify only own Adverts. This is the first option as possible.

    //Client layer call seller.changeAdvertName(name)

    //AR Seller
    class Seller{
        adverts
        changeAdvertName(advertId, name){
            adverts[advertId].changeName(name)
        }
        changeAdvertCost(advertId, cost){
            adverts[advertId].changeCost(cost)
        }
    }

    //AR Advert
    class Advert{
        name
        cost
        changeName(name){
            this.name = name
        }
        changeCost(cost){
            this.cost = cost
        }
    }

2.

Another variant, client layer can invoke directly methods changeName and changeCost from aggregate Advert. I saw this implementation many times.

    //Client layer call advert.changeName(name)

    //AR Advert
    class Advert{
        name
        cost
        changeName(name){
            this.name = name
        }
        changeCost(cost){
            this.cost = cost
        }
    }

What do you think about these options? Are they both valid for DDD
implementation? Which one is more correct and logical from the point of view of DDD?

Thank you!

2

There are 2 answers

5
VoiceOfUnreason On BEST ANSWER

Are they both valid for DDD implementation?

An important idea in domain driven design is the notion of a consistency boundary - an aggregate is a boundary around state that can be modified in isolation - without looking at any state outside of the aggregate.

The main benefit is that the client code doesn't need to worry about managing the consistency rules; that responsibility lives in the aggregate.

An additional benefit is that modifications to one aggregate don't need to block on modifications to another.

Nesting aggregate roots, by having one aggregate hold a reference to another, makes something of a mess of that idea; a thread that is trying to modify the Advert may interfere with a different thread that is trying to modify the Seller of the Advert.

There's fundamentally nothing wrong with having multiple entities within a single aggregate. For example, you could reasonably combine the Seller entity and the Advert entity into a single Seller aggregate, and enforce your consistency guarantees by making all changes to an Advert go through the Seller. It's important, however, to recognize that in this design the Advert is not, itself, an aggregate root.

There's also nothing wrong with having the Advert be its own aggregate root, handling its own consistency rules, while the Seller lives in a different aggregate.

In this simple example, where the seller is just deferring changes to the advert, it makes sense to keep them separate from one another, so that different Adverts of the same seller can be modified concurrently.

If there was some critical domain invariant that spanned multiple adverts, then you might need to pull them all into a single collection, which might live within the seller aggregate.

My idea was that, in the real business process specifically the Seller create Advert and change Advert. But not an abstract client layer create and change Advert. So, could you please help to understand?

It turns out that, in the real business world Seller can create(),drop(),modify()... -> Advert. But in DDD aggregate Seller can only implement create(Advert) behavior?

This really isn't specific to DDD; it's more a reflection of "object oriented" programming (as understood by Java, etc). Behavior - which is to say, changes of state - occur by sending a message to the entity that manages that state.

The object oriented idiom is not actually a good match with English grammar. We normally write "Seller modifies advert" -- a subject-verb-object form. But in the grammar of object oriented programming, objects change their own state in response to imperative tense messages (commands).

List.addItem(...) - we aren't modifying the list, we are sending the list a command that says: "modify your own state".

Similarly, the seller isn't modifying the state of the advert; she is sending a message describing the way the advert should change, and it's up to the advert to do that.

And that's deliberate: it means that the Seller can collaborate with the Advert without needed to know anything about the Advert's implementation, which means that we can replace that implementation any time we want without breaking Seller.

0
giorgi dvalishvili On

Aggregate roots are consistency boundary to ensure the domain model is kept in a reliable state. From many DDD Practitioners we know that.

Transactions should, not cross aggregate boundaries. To Update another aggregate use domain events in a separate transaction. more

from this perspective Second option would be more valid, since you have 2 aggregate root.