Domain Event

4 min read

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. Form Aggregate Root’s point of view it doesn’t matter if someone listens for DE it fires, 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 any number of systems without impacting core of e-commerce module, its both complexity and time-space usage.

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 firing. 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 supposed to be released after the successful transaction, so when DBMS confirms that all went good, this is the moment to massively emit 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 every Event Listener is a separate, independent process having its own DBMS transaction, you must ba 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 undo 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).