szpak

Domain Event

Event informs other Aggregates about what have happened.


One day you have been asked to create a new functionality in existing e-commerce store. Client have made a feature request for detecting abandoned Carts. Everytime user adds or removes Cart Product, a proper Domain Event (DE) is dispatched what may take place in an Aggregate Root or a Domain Service.

System Which Is Easily Extendable

The header already tells us why we even reason about all those Events. From Aggregate Root’s point of view it doesn’t matter if someone listens for DE it records, or not. It doesn’t have to know about any other contexts relying on changes taking place inside it - it’s up to context to know when and what have happened on Cart Aggregate. Utilization of an Event idea lets us connect other parts of the system without putting their rules directly into the core of e-commerce module.

Event Technicals

From code’s point of view, some characteristic must be met for DE to be considered as a proper Event representation. Event is an immutable object which additionally contains an exact time of Event recording or publication. Immutable data contains Event name for Event Subscribers, so they know if a particular Event is their concern. Reason for immutability is that as you may already know, Event is a thing of the past, thus it cannot be changed. Lastly, it’s not so unusual for Events to carry an Identifier, it helps to find it later within logs or for dispatching purposes.

Aggregate Root As Event Dispatcher

For consistency-sake, we will continue with a Cart Aggregate concept introduced in the previous post. To equip an Entity in event-firing capabilities, we need some AggregateRoot parent class, which will be a base for Entities objects.

Two first classes: DomainEvent and AggregateRoot are base classes for every Domain Event in the system. Mind the function which is used to record an Event, record_event(). It just appends objects to the list but nothing seems to fire them. And this is a right behaviour. Domain Events are often released after the successful transaction, so when DBMS confirms that all went good, this is the moment to publish Events recorded within an Aggregate Root.

Event Listeners

So, the DBMS transaction is completed, Recorded Events were drained from the Aggregate Root, dispatched and HTTP response was sent back to the end user, allowing his application to update its views. But this is not an over yet. There is one Event Listener (EL) which is interested in all Cart Events. You have added it to implement client’s feature request I have mentioned earlier.

Everytime User is changing its Cart’s content, you communicate it to the specialized subsystem responsible for managing Abandoned Carts, and taking proper actions when detecting one. This is possible, that code inside EL will not run as expected. It may be due to the bug introduced by the programmer, misconfiguration, network failure - the list goes on and on.

Distributed Transactions

It is more than likely, that some business processes will be distributed among many listeners. Keeping in mind that an Event Listener may be a separate, independent process having its own DBMS transaction, you must be aware that one or more EL’s might not work as expected. Solution to this problem is to have some retry mechanism, which will attempt to redo necessary steps or gracefully compensate changes already made. Case with Abandoned Carts is not so crucial to implement any complex solutions, but what if we deal with let’s say Payment and Invoice?

After successful Payment process, which fires Payment Successful Event, the Invoice Payment Listener tries to generate document, but it’s unable to connect with an external system, which provides nicely formatted PDFs. What is even worse, the Accounting System where all transactions must be registered also doesn’t seem to be operative. It’s a total disaster, that’s why it should never happen. But when it does, you better be prepared :-)

Summary

Domain Event is a great concept, which helps us design loosely coupled and highly coherent system. These two characteristics are crucial when it comes to big and complex software. They enforce a programmer to write a clean code, by clean I mean consistent (each building block is done the same way, no matter the module), modularized (defined structure for files making a modularized components) and easy to grasp (Aggregate small enough to be figured out in less than 15 minutes).