Ddd where enforce aggregate invariant?

342 Views Asked by At

Given an aggregate order I would like to know how I can force invariance.


 

Public class Order(){}

AddOrderItem(){}

AddPaymentMethod(){}

}

 

Var order = new Order();

_orderRepository.Save(order); //This was bad.

The invariance is given by the fact that Order must have at least one OrderItem within the collection and PaymentMethod.

But that means that sometimes it doesn't have to be called AddOrderItem/AddPaymentMethod so everything would break.

I would not like to pass all the OrderItem through the constructor since the order has not been generated yet. I would also not like to have an order validation service as this can lead to bad development and someone can forget to call the validator.

Is it possible to create a factory? if so how do you add items making the order methods private (since no one can add items without it being through the factory)?

Or would it be a better option to create a validatorOrderCreatedDomainEventHandler after firing the domain event (OrderCreated) thus validating all the consistency before calling Save to the repository?

What is the best option to enforce this invariant?

1

There are 1 best solutions below

1
desertech On

There is a distinction between (A) keeping an entity in a valid state with every change (including when being created) and (B) preventing an entity from being persisted/processed.

(A) means for example having a property telling us whether or not an order is ready to be processed:

bool ReadyToBeProcessed => OrderItems.Count > 0 && PaymentMethod is not null

Now an empty order with no payment method is not considered to be broken and may be persisted.

(B) means performing a validation before processing an order. Whether or not you implement domain events is less important: calling validations can be forgotten in either way. The point is you always have a single flow (endpoint) to process an order, and it is your responsibility to perform necessary validations e.g. throwing an exception if ReadyToBeProcessed is false just before processing an order (rejecting an order if not yet ready to be processed is actually a business invariant hence should be explicitly presented).

Is it possible to create a factory? if so how do you add items making the order methods private (since no one can add items without it being through the factory)?

A factory hides implementation details related to the way an entity is being constructed. Associating an order-item to an order is not part of constructing an order, so a factory cannot be the solution; the two methods should remain public.