230
CHAPTER 6 | Tackle Business Complexity in a Microservice with DDD and CQRS Patterns
Figure 7-14 shows how consistency between aggregates is achieved by domain events. When the user
initiates an order, the Order Aggregate sends an
OrderStarted
domain event. The OrderStarted
domain event is handled by the Buyer Aggregate to create a Buyer object
in the ordering
microservice, based on the original user info from the identity microservice (with information provided
in the CreateOrder command).
Alternately, you can have the aggregate root subscribed for events raised by members of its
aggregates (child entities).
For instance, each OrderItem child entity can raise an event when the item
price is higher than a specific amount, or when the product item amount is too high. The aggregate
root can then receive those events and perform a global calculation or aggregation.
It’s important to understand that this event
-based communication is not implemented directly within
the aggregates; you need to implement domain event handlers.
Handling the domain events is an application concern. The domain model
layer should only focus on
the domain logic
—
things that a domain expert would understand, not application infrastructure like
handlers and side-effect persistence actions using repositories. Therefore, the application layer level is
where you should have domain event handlers triggering actions when a domain event is raised.
Domain events can also be used to trigger any number of application actions, and what is more
important, must be open to increase that number in the future in a decoupled way. For instance, when
the order is started, you might want to publish a domain event to propagate that info to other
aggregates or even to raise application actions like notifications.
The key point is the open number of actions to be executed when a domain event occurs.
Eventually,
the actions and rules in the domain and application will grow. The complexity or number of side-
effect actions when something happens will grow, but if your code were coupled with “glue” (that is,
creating specific objects with
new
), then every time you needed to add a new action you would also
need to change working and tested code.
This change could result in new bugs and this approach also goes against the
Open/Closed principle
from
SOLID
. Not only that, the original class that was orchestrating the operations would grow and
grow, which goes against the
Single Responsibility Principle (SRP)
.
On the other hand,
if you use domain events, you can create a fine-grained and decoupled
implementation by segregating responsibilities using this approach:
1.
Send a command (for example, CreateOrder).
2.
Receive the command in a command handler.
–
Execute a single aggregate’s transaction.
–
(Optional) Raise domain events for side effects (for example, OrderStartedDomainEvent).
3.
Handle domain events (within the current process) that will execute an open number of side
effects in multiple aggregates or application actions. For example:
–
Verify or create buyer and payment method.
–
Create and send a related integration event to the event bus to propagate states across
microservices or trigger external actions like sending an email to the buyer.
–
Handle other side effects.
231
CHAPTER 6 | Tackle Business Complexity in a Microservice with DDD and CQRS Patterns
As shown in Figure 7-15, starting from the same domain event, you can handle multiple actions
related to other aggregates in the domain or additional application actions you need to perform
across microservices connecting with integration events and the event bus.
Figure 7-15. Handling multiple actions per domain
There can be several handlers for the same domain event in the Application Layer, one handler can
solve consistency between aggregates and another handler can
publish an integration event, so other
microservices can do something with it. The event handlers are typically in the application layer,
because you’ll use infrastructure objects like repositories or an application API for the microservice’s
behavior. In that sense, event handlers are similar to command handlers, so both are part of the
application layer. The important difference is that a command should be processed only once. A
domain event could be processed zero or
n
times, because it can be received by multiple receivers or
event handlers with a different purpose for each handler.
Having an open number of handlers per domain event allows you to add as many domain rules as
needed, without affecting current code. For instance, implementing the following business rule might
be as easy as adding a few event handlers (or even just one):
When the total amount purchased by a customer in the store, across any number of orders, exceeds
$6,000, apply a 10% off discount to every new order and notify the customer with an email about that
discount for future orders.
Dostları ilə paylaş: