Ankit Vijay
Ankit Vijay

Reputation: 4118

Handling Domain events within a single aggregate?

I'm working on a Domain-Driven-Design implementation, where we have some of the operations within the same aggregate that need to happen in conjunction with the other operation. The two operations are unrelated to each other.

Here is a sample code.

Note that this code only for an illustrative purpose and not a real example.

public ProcessOrderCommandHandler : IHandler<ProcessOrderCommand>
{
     public async Task Handle(ProcessOrderCommand orderCommand)
     {
            var order = _repository.LoadOrder(orderCommand.Id);

            // Operation - 1
            order.AddToCart(orderCommand.Item);

            // Operation - 2
            order.ProcessOrder();
     }
}

public class Order : Aggregate
{
     public void AddToCart(Item item)
     { ... }

     public void ProcessOrder()
     { ... }       
}

ProcessOrder and AddToCart operations are not related and I have many such CommandHandlers which are independent of each other but still need to be called in conjunction.

I see there are three options to solve this issue:

Again, not 100% convenience if this the right way since I would need to do this in all relevant operations.

I feel this approach is the cleanest among all as it helps to keep separation of concerns. However, here, I'm handling the two domain operations within the same aggregate through Domain-Event, which is not how Domain-Events usually are used. As per Domain-Event definition from Microsoft Docs:

Use domain events to explicitly implement side effects of changes within your domain.

In other words, and using DDD lingo, use domain events to explicitly implement side effects across multiple aggregates.

Question: Is Option 3 is an acceptable solution in Domain-Driven-Design?

Upvotes: 1

Views: 1590

Answers (3)

Arley P&#225;dua
Arley P&#225;dua

Reputation: 156

Without too much context of your domain it is hard to tell, but this is an answer you should get from your product expert.

It seems that you have an Order aggregate that exposes a public interface to do operations on a Shopping Cart.

  1. Is this aggregate really an OrderAggregate or a ShoppingCartAggregate?
  2. I assume that "process order" means in fact place the order (in my mind as a side effect of this operation I see payment happening, stock management and so on..). If that's the case, it seems that you can improve the design of your domain by having some steps:
  • Buyer adds products in a Shopping Cart
  • Buyer proceeds to the Cashier and checks out the products in the Shopping Cart
  • Buyer pays
  • Buyer received the purchase receipt

(This is a description of what happens in reality in any store to help on illustrating my idea)

In this process we have:

  • A Buyer being the persona that is evolved in the use case
  • A Shopping Cart, that is a list of products that the buyer has the intention to buy. But as they are representing an intention, putting items or removing them is quite easy
  • Checkout the items in the shop cart is literally taking a snapshot of the last state of the cart
  • As a result of this check out (which I assume in your case is the Process Order), the buyer is prompted to pay and any other side effect that may happen after the checkout.

Again, this was an assumption that I made based on your description, but I used it to show that concepts can be extracted fr what really happens in a business process and domain experts should help you on getting them right.

Upvotes: 1

Andreas H&#252;tter
Andreas H&#252;tter

Reputation: 3929

Does the ProcessOrder() method only reflect changes inside the aggregate that should be performed on the same aggregate to react on adding some item to the cart? If yes, I would move the call to that operation into the AddToCart() method. If it's some business invariant that has to happen in the same aggregate whenever an item is added why put that responsibility on the application layer? This would only allow your business logic to leak outside the domain layer. If you want to use an event-based approach to implement these actions cohesively on the same aggregate that's a design decision of your choice which might or might not be more suited. But still it is no longer the responsibility of the application layer that this processing has to happen after adding an item.

But if the ProcessOrder() method rather represents the next step in the user's workflow after he added something to the cart (like submitting the order) you should ask yourself, Is my interaction with the domain model designed too CRUD based?

So from my experience DDD is usually more suited or let's say easier applicable for task-based User Interfaces. That means that the client (e.g. Web Browser) interacts with the system by performing smaller well-defined tasks rather than sending a bigger bunch of data which is than translated to several tasks performed on the domain model (here the aggregates) in sequence.

So in your example, if ProcessOrder() means "Submit the order" I would personally have two interactions with the back end. One for adding something to the cart - Use Case A - and a second one for submitting the order - Use Case B.

To me, in this case this also feels more natural from the customer's point-of-view, as I would expect to perform these tasks in separate steps. Also, if you consider this approach you not have to handle events especially as you operating on the same aggregate.

Upvotes: 1

Francesc Castells
Francesc Castells

Reputation: 2857

ProcessOrder and AddToCart operations are not related and I have many such CommandHandlers which are independent of each other but still need to be called in conjunction.

Before directly answering your question, I would suggest reevaluating this statement. If the operations are not related to each other, why do they belong to the same aggregate? Also, are these many CommandHandlers operating on the same aggregate?

In short, are you sure your aggregates are correct? they should have a single and clear responsibility. If there are unrelated operations or too many operations, they might be serving several responsibilities.

Now, regarding your three options. In my opinion CommandHandlers are what is called Use Cases in Clean Architecture. A use case, in your scenario, basically loads the aggregate from the DB, calls the business operations, and stores the updated aggregate back to the DB (plus potentially publishing events, talk to third parties, etc). So, if you see it this way, the fact that your use case requires to call 2 business operations in the same aggregate is not a problem, because that's what your use case specifies.

You would combine the two operations into one if the two operations separately didn't make sense.

Option 3 is the only one I wouldn't go to if we assume that the aggregates are correct. In my opinion, it would be abusing a pattern for something it wasn't meant to be used.

But, if you figure that, in fact, these operations belong to different aggregates, then definitely using events would be the best option.

Consider the following requirements:

  1. When the user clicks Add To Cart, the selected product should be added to the cart
  2. When a Product is added to the Cart, the shipping cost should be recalculated

In this scenario, you clearly have two aggregates (Cart and Shipping). Adding to Cart and recalculating the Shipping cost are two independent operations and coordinating them with events is what makes more sense and aligns with the business specification ("When X happens then...").

Now consider another use case for the previous example:

  1. When the shipping address is updated, the shipping cost should be recalculated

Now, Update Shipping Address and Recalculate the shipping cost are two operations on the same aggregate, but in this case, it wouldn't make sense to call them separately from the use case, because changing the address without recalculating the cost would leave the aggregate in an inconsistent state, so the aggregate itself could automatically recalculate the cost as soon as the address is changed.

Upvotes: 3

Related Questions