18
Domain Events

WHAT’S IN THIS CHAPTER?

  • An introduction to the domain events design pattern
  • An explanation of domain events’ core concepts
  • Multiple domain events’ implementation patterns
  • Testing applications that use domain events

Wrox.com Code Downloads for This Chapter

The wrox.com code downloads for this chapter are found at www.wrox.com/go/domaindrivendesign on the Download Code tab. The code is in the Chapter 18 download and individually named according to the names throughout the chapter.

Domain-Driven Design (DDD) practitioners have found that they can better understand the problem domain by learning about the events that occur within it—not just the entities. These events, known as domain events, will be uncovered during knowledge-crunching sessions with domain experts. Uncovering domain events is so valuable that DDD practitioners have innovated knowledge-crunching techniques to make them more event-focused using practices such as event storming (http://ziobrando.blogspot.co.uk/2013/11/introducing-event-storming.html#.U5YPynU-mHs). With these innovations, though, come new challenges. Now that conceptual models are event-centric, the code also needs to be event-centric so it can express the conceptual model. This is where the domain events design pattern adds value.

Part II focused on events that are sent as asynchronous messages between bounded contexts using transports like a message bus or REST. However, the domain events design pattern is generally used as a single-threaded pattern inside a domain model within a bounded context. In this chapter you will see examples that explore the different implementation options and overall trade-offs involved when using domain events.

Before getting started, it’s important to acknowledge that using domain events does not necessitate using event sourcing. Unfortunately, this is a common misconception; it is entirely possible to use traditional persistence options like a SQL database. However, applying event sourcing or asynchronous messaging in combination with a domain model that uses domain events is often a good combination. Chapter 22, “Event Sourcing,” will clarify this relationship.

Throughout this chapter, each implementation pattern for domain events will be used to implement a single use case from an online pizza delivery service. This way you can see direct comparisons. The use case being modeled is the delivery guarantee, where a customer will receive a discount if their pizza is not delivered in a certain timeframe.

Essence of the Domain Events Pattern

If you understand the publish-subscribe pattern or C# events, you will grasp the domain events pattern very quickly. Essentially, you use an infrastructural component, sometimes from within your domain model, to publish events. By default, events are then processed synchronously inside the same thread by each event handler that has been registered for that type of event. Asynchronous event handling is also possible with this pattern.

Important Domain Occurrences That Have Already Happened

Events are just immutable classes with public properties (data objects, Plain Old C# Objects [POCOs], Plain Old Java Objects [POJOs]) representing important events in the problem domain. Listing 18-1 shows a DeliveryGuaranteeFailed event, which is fired in the domain when a pizza delivery took longer than promised.

Notice in Listing 18-1 that the DeliveryGuaranteeFailed event is just a class with properties; this is all that your events need to be.

Reacting to Events

In response to events, event handlers are executed. Listing 18-2 shows how you can register an event handler as a method to be called (onDeliveryFailure) when the event (DeliveryGuaranteeFailed) is raised.

Optional Asynchrony

Using the domain events pattern can be synergistic with asynchronous workflows, including communication between bounded contexts. As you saw in Part II on bounded context integration, asynchronous messages between bounded contexts are a modern solution that help with reliability and scalability. You can trigger these processes from within your domain events event handlers. Sometimes you may even want asynchronous, reliable communication within a bounded context, for scenarios like implementing eventually consistent aggregates. Using the decoupled nature of domain events can help you in both scenarios.

Importantly, when creating event handlers that trigger asynchronous workflows, you need to be clear about transactional boundaries. For example, if one event handler updates the database, and another publishes a message, you would want both operations to roll back if either of them failed, as Figure 18.1 shows.

images

FIGURE 18.1 Ensuring correct transactional behavior

Internal vs External Events

An important distinction needs to be made when using the domain events pattern to avoid confusion that can lead to poor technical implementations. It is crucial that you are aware of the difference between internal and external events. Internal events are internal to a domain model–they are not shared between bounded contexts. In this chapter, you will see how the domain events pattern uses internal events, whereas you saw external events in Part II of this book.

Differentiating internal and external events is important because they have different characteristics. Because internal events are limited in scope to a single bounded context, it is ok to put domain objects on them, as the example in Listing 18-1 showed. This poses no risk, because other bounded contexts cannot become coupled to these domain objects. Conversely, external events tend to be flat in structure, exposing just a few properties—most of the time just correlational IDs, as typified in Listing 18-3.

You learned in Part II that external events need to be versioned to avoid breaking changes. This is another differentiator with internal events, because if you make breaking changes to an internal event your code will not compile (if using a compiled programming language). So there’s no need to version internal events.

As you start to implement domain events, you will see that in a typical business use case there may be a number of internal events raised, and just one or two external events that are raised by the service layer. Figure 18.2 illustrates how the sequence of events may occur in a typical use case.

images

FIGURE 18.2 Flow of internal and external events in a typical business use case

With all of these differences in mind, it makes sense to put your events in different namespaces to accentuate those that are internal from those that are external.

Event Handling Actions

Before seeing concrete examples of the domain events pattern it’s important to have an understanding of the distinction between handlers in the domain and handlers in the application service layer. Even though they look the same, their responsibilities are significantly different. Domain event handlers invoke domain logic, such as invoking a domain service, whereas application service layer event handlers are more infrastructural in nature, carrying out tasks like sending e-mails and publishing events to other bounded contexts.

Invoke Domain Logic

Event handlers that exist within the domain model can handle events that occur there. These scenarios are modeling sequences of interaction that occur in the problem domain. For example, in the online takeaway store, an order validator validates an order, verifying that the customer is not blacklisted after failing to pay for previous orders. The validator then raises events of its own to trigger the next part of the business process. It’s common to see domain event handlers delegating to a domain service.

Invoke Application Logic

There is a benefit to having event handlers that live in the application service layer in addition to those that live in the domain. These event handlers tend to carry out infrastructural tasks like sending e-mails. Note that these handlers are not part of UL or the domain.

One important responsibility of application service layer handlers is that they trigger communication with external bounded contexts, using the techniques presented in Part II. They also manage communication with external services, like payment gateways.

Domain Events’ Implementation Patterns

Domain events is a generic pattern that basically adds domain semantics to the publish-subscribe pattern. This gives you a lot of freedom to implement the solution. You’ll see a variety of options in the following examples that range from synchronous use of native language constructs, to message bus-based alternatives.

Use the .Net Framework’s Events Model

An easy way to get started with domain events is to rely on the native capabilities provided by the language or platform you are using, if applicable. In C# this means using the event keyword.

To define a domain event using the event keyword, you can create a public field on an entity named after the domain event. You also need to create a delegate that represents the contract of the event. Both of these details are shown in Listing 18-4.

In Listing 18-4 an event called DeliveryGuaranteeFailed is created using the event keyword. You can see that this event has a type of DeliveryGuaranteeFailedHandler signifying that all event handlers of the DeliveryGauranteeFailed event must have the same signature as the DeliveryGuaranteeFailedHandler. You can see the signature of the DeliveryGuaranteeFailedHandler on the line above where it is declared using the delegate keyword. Its signature is a void return type with a single parameter of type DeliveryGuaranteeFailed. You can see a handler with this signature being registered in Listing 18-5.

onDeliveryGuaranteeFailed is a method with a void return type that takes a single parameter of type DeliveryGuaranteeFailed. Therefore it can successfully be registered as a handler for the DeliveryGuaranteeFailed event, as shown in Listing 18-5. It is registered using C#’s special syntax for registering event handlers: +=.

To raise C# events you invoke them like methods, as shown in Listing 18-6, where the DeliveryGuaranteeFailed event is raised. It’s important to check if the event is null first, because if an event has no subscribers it will be null, causing a null reference exception to be thrown.

To trigger an asynchronous process you can create, and register, an event handler that publishes an asynchronous message, writes to a queue, or updates a database. You need to ensure that the action is enlisted as part of the current transaction, so that if the transaction rolls back, so will the action that triggers the asynchronous process. When using C# and the event keyword, it’s likely you will want to configure the Distributed Transaction Coordinator (DTC).

As you can see from Listings 18-4 to 18-6, the up-front investment when using native events can be minimal by just using the event keyword. However, this approach has a cost; there is a tight coupling between publishers of an event and subscribers. You can see this in Listing 18-5, where the handler of an event must know which object is publishing the event. Alternative implementations of the domain events pattern have a looser coupling of publishers and subscribers, as shown in the next section.

Use an In-Memory Bus

To decouple publishers and subscribers you can implement the domain events pattern using an in-memory message bus. This gives you the option to easily make some aspects of the system asynchronous when they can benefit from it, assuming the message bus supports both synchronous and asynchronous transit. NServiceBus is one message bus that does provide both options.

With a message bus you again focus on events and handlers. But instead of wiring them up with each other, you publish events through the bus, and register handlers through the bus. With NServiceBus there is no need to actually register subscribers, though, because all event handlers implement the NServiceBus IHandleMessages<T> interface, where T is the type of event being handled, so NServiceBus can easily find event handlers automatically. All NServiceBus requires is to be told which classes are the events, as shown in Listing 18-7.

Listing 18-7 uses the convention that any class inside a namespace called Events will be considered an event by NServiceBus. Therefore, if the event in Listing 18-8 was published (notice its namespace contains Events), NServiceBus would invoke the handler shown in Listing 18-9.

Listing 18-10 then shows how this message is published using NServiceBus’s in-memory bus. Notice how the subscriber does not know, or care, who the publisher of the event is and vice-versa.

One of the tricky details with using an in-memory bus is passing it around the domain model. Ideally you don’t want technical concerns, like a message bus, cluttering the domain model at all. On the other hand, you do need some way of publishing events. For some teams, this is a situation where it is an acceptable trade-off to allow technical concerns into the domain because it allows the model to be more expressive of domain concepts overall.

NServiceBus’s default mode of operation is sending asynchronous messages, so if you decide that you need to fire some domain events asynchronously, the transition is relatively easy. You add a new event handler that subscribes to an in-memory event and publishes asynchronous events. Or, you could just publish the initial event asynchronously using Bus.Publish() instead of Bus.InMemory.Raise(), in which case all subscribers would execute asynchronously on a different thread.

It’s important to be aware of transactional requirements when deciding to publish events asynchronously. If you have two event handlers that need to atomically succeed or fail, then you will need to ensure they execute synchronously. Otherwise you will be open to bugs arising from inconsistency.

Udi Dahan’s Static DomainEvents Class

Traditional DDD advice has been to remove all infrastructural concerns from the domain model. Traditional object-oriented guidelines warn that you should avoid relying on static methods because of coupling. You might be surprised to hear then that another popular version of the domain events pattern relies on a static class called DomainEvents that deals with infrastructural concerns and lives in the domain model. Listing 18-11 shows the implementation of the static DomainEvents class, which builds on the work of DDD expert Udi Dahan (http://www.udidahan.com/2008/08/25/domain-events-take-2/).

DomainEvents has two important methods: Register<T> will register a callback that is executed when an event of type T is raised by the other important method: Raise<T>.

Another detail to discern in Listing 18-11 is the use of the private DomainEventsRegistrationRemover class. You can see that Raise<T> uses an instance of this class to automatically unregister event handlers once they have executed a single time.

Listing 18-12 shows how you can register an event handler with DomainEvents while Listing 18-13 then shows how you can publish an event.

Handling Threading Issues

One of the big challenges involved with using the static DomainEvents class is managing threading issues. This is because any handler registered in one thread is executed when an event of the type it handles is published by any other thread. As alluded to, this is a result of DomainEvents being static; every thread uses the same instance.

A technique that can help to avoid threading issues is to apply the ThreadStatic attribute to the collection of handlers and collection of callbacks on the DomainEvents class. Using this attribute, each thread gets its own collection, meaning that handlers registered on one thread are not visible to another thread. Listing 18-14 shows how to use the ThreadStatic attribute with domain events.

However, there is cause for concern when using this approach in ASP.NET web applications. Scott Hanselman explains on his blog (http://www.hanselman.com/blog/ATaleOfTwoTechniquesTheThreadStaticAttributeAndSystemWebHttpContextCurrentItems.aspx) that ThreadStatic should, in fact, never be used in ASP.NET applications.

Avoid a Static Class by Using Method Injection

If you’re not prepared to couple your domain to a static event publisher, as many DDD practitioners aren’t, you can instead supply one as an argument to methods in your domain model. This pattern is also a good consideration when you don’t want the previously mentioned problems associated with the ThreadStatic attribute. Listing 18-15 shows an instance of an event dispatcher being passed into a domain method and invoked.

Return Domain Events

Another take on the domain events pattern is to decouple the publishing and handling of events, so that side effects are isolated. This approach is implemented by storing a collection of events on the aggregate root and publishing them once the aggregate root has completed its task. Significantly, a dispatcher is called from the service layer to publish the events, keeping the technical concern out of the domain model.

Listing 18-16 shows an alternative version of the OrderForDelivery entity that keeps a collection of events (its RecordedEvents property). Each time the entity is loaded from persistence, this collection will be empty. It only contains events that have occurred inside the current transaction.

When the ConfirmReceipt behavior is triggered, instead of publishing an event (as per Listing 18-16), an event is added to the OrderForDelivery entity’s RecordedEvents collection, as shown in Listing 18-16. Note how the collection is public so that it can be accessed outside of the domain model.

Any side effects that arise from handling the event will not immediately occur, as is the case when events are simply published. This is seen as desirable by some developers because the side effects aren’t interleaved with the execution of the current method.

When the entity has finished carrying out its logic, the thread of execution will return to the application service that is managing the business use case. It is at this point that the events from the entity can be fed into a dispatcher, as shown in Listing 18-17.

It is also possible to use method injection if you do not want to pass an event dispatcher into the constructor of your domain objects. Listing 18-14 shows how an event dispatcher is passed into ConfirmReceipt(), where domain events are recorded on the dispatcher rather than the entity. Note that the dispatcher will not dispatch the events immediately. Instead, it will record them and will dispatch them once instructed to from the application service layer, as previously shown in Listing 18-18.

Clearly, passing a dispatcher into methods is going to pollute the signature of your domain model, so you’ll want to be extra careful about using this pattern. You may want to store events on an entity in the majority of use cases, and reserve passing around a dispatcher for when there is a clear benefit.

To implement a dispatcher you can copy most of the details from Listing 18-18, except you won’t need to make the class static. Or you can use an IoC container, as the next example demonstrates.

Use an IoC Container as an Event Dispatcher

If you are using an IoC container you can use it as an event dispatcher. This can be a good choice when you are already using an IoC container or don’t want to build your own event dispatcher. It can also be a good choice when you want advanced features provided by IoC containers, including lifecycle management, or convention-based handler registrations. Convention-based handler registration is shown in Listing 18-19 where a StructureMap convention scans all assemblies in a project and automatically registers each event handler with its associated event. For this pattern, though, you will need to create an interface to represent event handlers. Listing 18-19 shows an IHandles<T> interface being used.

You can see in Listing 18-20 how once all your event handlers are registered, you can use a container from within an application service to dispatch all events recorded during the current transaction.

Testing Domain Events

Testing code that uses the domain events pattern is no more complicated than traditional object-oriented code. In places it can even be simplified because collaborators do not need to be mocked. The testing patterns are a little different, though, so this section provides a short guide with examples.

Unit Testing

Many of your unit tests want to verify that an entity or domain service raised an event to signify a change occurring. In the online takeaway store scenario, this could be demonstrated with the previously discussed DeliveryGuaranteeFailed event. Listing 18-21 shows a unit test that validates the event is raised using the static DomainEvents version of the pattern.

Having a static DomainEvents class doesn’t actually make testing difficult, as Listing 18-21 illustrates. When using the domain events pattern, you don’t want to mock the DomainEvents class, meaning the tight coupling is not really a problem. This means that in a unit test, you can verify the appropriate event was raised by registering a callback for it that sets a flag. In Listing 18-21, this flag is the eventWasRaised variable. If eventWasRaised is true at the end of the test, it’s clear that the event was raised.

In a similar fashion, when recording domain events, you again invoke the desired domain behavior, but instead verify that the event was recorded as opposed to being published. This is shown in Listing 18-22.

Application Service Layer Testing

Integration testing at the application service-layer level is also possible when using domain events. In fact, your tests probably look very similar to when you’re not using domain events. This is because events and event handlers are just implementation details, as the example in Listing 18-23 clarifies.

Nowhere in Listing 18-23 is there a hint of the domain events pattern. Test data and services are set up, the ConfirmDeliveryOfOrder application service is instantiated and invoked, and finally the assertion is made that NServiceBus published an external RefundDueToLateDelivery command to be handled by another bounded context. This test would be the same whether or not the domain events pattern was used as the implementation for the domain model. This shows that testing at the application service layer adds no additional complexity when using domain events. Comfortingly, it also means that you can refactor existing domain models to use the domain events pattern without having to change any of your application service layer tests. And they can also reassure you that you haven’t broken anything.

The Salient Points

  • Domain events are significant occurrences in the real-world problem domain; they are part of the ubiquitous language (UL).
  • Domain events is also a design pattern that makes domain events more explicit in code.
  • Domain events is akin to publish-subscribe, where events are raised and event handlers handle them.
  • Unlike messaging between bounded contexts (Chapters 11 through 13), domain events are applied inside a single domain model and are usually synchronous.
  • Using domain events can make it easier to transition certain operations or use cases into asynchronous processes.
  • Event handlers can be classes or anonymous callbacks/lambdas.
  • Some handlers live in the domain, and some live in the service layer.
  • Handlers in the domain carry out domain logic.
  • Handlers in the service layer normally take some of the responsibilities otherwise handled by application services.
  • There are multiple versions of the domain events pattern.
  • One approach is to use native language constructs, like C#’s event keyword.
  • C# events have a tight coupling between publishers and subscribers. For looser coupling you can use an in-memory bus, such as NServiceBus.
  • An in-memory bus causes immediate execution of events and their side effects. If this is undesirable, you can store events and dispatch them later with a dispatcher.
  • Another version relies on a static DomainEvents class, which is similar to an in-memory message bus but much more compact.
  • Using domain events does not mean you have to use event sourcing, although a lot of developers find the combination highly effective.
  • Ultimately, the pattern is about trade-offs—an extra layer of indirection that distributes code across more files but has a fully encapsulated domain model that accentuates real-world domain events.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.158.151