208
CHAPTER 6 | Tackle Business Complexity in a Microservice with DDD and CQRS Patterns
For example, following DDD patterns,
you should
not
do the following
from any command handler
method or application layer class (actually, it should be impossible for you to do so):
// WRONG ACCORDING TO DDD PATTERNS
–
CODE AT THE APPLICATION LAYER OR
// COMMAND HANDLERS
// Code in command handler methods or Web API controllers
//... (WRONG) Some code with business logic out of the domain classes ...
OrderItem myNewOrderItem =
new
OrderItem
(orderId, productId, productName,
pictureUrl, unitPrice, discount, units);
//... (WRONG) Accessing the OrderItems collection directly from the application layer // or
command handlers
myOrder.
OrderItems
.
Add
(myNewOrderItem);
//...
In
this case, the Add method is purely an operation to add data, with direct access to the OrderItems
collection. Therefore, most of the domain logic, rules, or validations related to that operation with the
child entities will be spread across the application layer (command handlers and Web API controllers).
If you go around the aggregate root, the aggregate root cannot
guarantee its invariants, its validity, or
its consistency. Eventually you will have spaghetti code or transactional script code.
To follow DDD patterns, entities must not have public setters in any entity property. Changes in an
entity should be driven by explicit methods with explicit ubiquitous language about the change they
are performing in the entity.
Furthermore, collections within the entity (like the order items) should be read-only properties (the
AsReadOnly method explained later). You should be able to update it only
from within the aggregate
root class methods or the child entity methods.
As you can see in the code for the Order aggregate root, all setters should be private or at least read-
only externally, so that any operation against the entity’s data or its child entities has to be performed
through methods in the entity class. This maintains consistency in a controlled and object-oriented
way instead of implementing transactional script code.
The following code snippet shows the proper way to code the task of adding an
OrderItem object to
the Order aggregate.
// RIGHT ACCORDING TO DDD--CODE AT THE APPLICATION LAYER OR COMMAND HANDLERS
// The code in command handlers or WebAPI controllers, related only to application stuff
// There is NO code here related to OrderItem object's business logic
myOrder.
AddOrderItem
(productId, productName, pictureUrl, unitPrice, discount, units);
// The code related to OrderItem params validations or domain rules should
// be WITHIN the AddOrderItem method.
//...
In this snippet, most of the validations or logic related to the creation of an OrderItem object will be
under the control of the Order aggregate root
—
in the AddOrderItem method
—
especially validations
and logic related to other elements in the aggregate.
For instance, you might get the same product
item as the result of multiple calls to AddOrderItem. In that method, you could examine the product
items and consolidate the same product items into a single OrderItem object with several units.
209
CHAPTER 6 | Tackle Business Complexity in a Microservice with DDD and CQRS Patterns
Additionally, if there are different discount amounts but the product ID is the same, you would likely
apply the higher discount. This principle applies to any other domain logic for the OrderItem object.
In addition, the new OrderItem(params) operation will also be controlled and
performed by the
AddOrderItem method from the Order aggregate root. Therefore, most of the logic or validations
related to that operation (especially anything that impacts the consistency between other child
entities) will be in a single place within the aggregate root. That is the ultimate purpose of the
aggregate root pattern.
When you use Entity Framework Core 1.1 or later, a DDD entity can be better expressed because it
allows
mapping to fields
in addition to properties. This is useful when protecting collections of child
entities or value objects. With this enhancement, you can use simple private fields instead of
properties and you can implement any update to the field collection in
public methods and provide
read-only access through the AsReadOnly method.
In DDD, you want to update the entity only through methods in the entity (or the constructor) in order
to control any invariant and the consistency of the data, so properties are defined only with a get
accessor. The properties are backed by private fields. Private members can only be accessed from
within the class. However, there is one exception: EF Core needs to set these fields as well (so it can
return the object with the proper values).
Dostları ilə paylaş: