Share via



December 2014

Volume 29 Number 12

Data Points : A Pattern for Sharing Data Across Domain-Driven Design Bounded Contexts, Part 2

Julie Lerman

Julie LermanIn the October 2014 Data Points (msdn.microsoft.com/magazine/dn802601), I wrote about a pattern for mirroring data from one database to another when you’re using multiple Domain-Driven Design (DDD) bounded contexts (BC), with each BC isolated to its own database. The scenario was that the Customer Management BC allows users to manage customer data, inserting, updating and deleting customer details. The second BC is for an ordering system that needs access to two critical pieces of customer information: the customer’s identifier key and name. Because these systems are in two separate BCs, you can’t reach from one into the other to share data.

DDD is for solving complex problems, and simplifying problems in the domain often means moving the complexity outside of the domain. So the Customer Management BC need not be aware of this subsequent sharing of data. In my previous column, I employed the publish/subscribe pattern to solve the problem by leveraging Domain Events, a message queue (RabbitMQ) and a service. There were a lot of moving parts.

One shortcut I intentionally took to avoid overwhelming the explanation with too many concepts was that of triggering the series of events directly from the customer class by publishing the event to a message queue.

This month, I want to enhance the solution in two ways. First, I want to publish the message from a more logical spot in the workflow: after first confirming that the customer (whether new or modified) has been successfully persisted into the customer system’s database. I also want to make sure the event is published only in response to relevant events. Publishing the event after a new customer is created makes sense. But I’d also like to tackle the hypothetical condition where I might need to be more discriminating about when updates are published to the message queue. In this case, I’d like to ensure the relevant message is published only when the customer’s name has changed. If other data is modified that doesn’t impact the customer name, I won’t publish the message.

These two changes will make the solution more applicable to real-world scenarios.

Customer Created or Updated Becomes Customer Persisted

The current solution raises a notification when a customer is created or when the customer’s name is fixed. The constructor and this FixName method both call PublishEvent:

public void FixName(string newName){
    Name = newName;
    ModifiedDate = DateTime.UtcNow;
    PublishEvent(false);
  }

PublishEvent triggers the workflow that results in the message being published to the queue:

private void PublishEvent(bool isNew){
    var dto = CustomerDto.Create(Id, Name);
    DomainEvents.Raise(new CustomerUpdatedEvent(dto, isNew));
  }

You can check the October column for details about that solution. Rather than raising the events from the class, I want to raise the events after I know either the new customer instance or the fix to the customer name has been successfully persisted into the database.

This means removing the PublishEvent method and the calls to it from the Customer class.

My data layer has a class containing the data access logic for the customer aggregate. I’ve moved the PublishEvent method into this class, renaming it to PublishCustomerPersistedEvent. In my methods that persist customers to the database, I call the new event after SaveChanges is complete (see Figure 1).

Figure 1 Persistence Class Raises Events After Data Is Persisted

public class CustomerAggregateRepository {
public bool PersistNewCustomer(Customer customer) {
  using (var context = new CustomerAggregateContext()) {
    context.Customers.Add(customer);
    int response = context.SaveChanges();
    if (response > 0) {
      PublishCustomerPersistedEvent(customer, true);
      return true;
    }
    return false;
  }
}
public bool PersistChangeToCustomer(Customer customer) {
  using (var context = new CustomerAggregateContext()) {
    context.Customers.Attach(customer);
    context.Entry(customer).State = EntityState.Modified;
    int response = context.SaveChanges();
    if (response > 0) {
      PublishCustomerPersistedEvent(customer, false);
      return true;
    }
    return false;
  }
}
   private void PublishCustomerPersistedEvent(Customer customer, 
    bool isNew) {
     CustomerDto dto = CustomerDto.Create(customer.Id, customer.Name);
     DomainEvents.Raise(new CustomerUpdatedEvent(dto, isNew));
   }
 }

With this move, I also needed to move the infrastructure for publishing messages into the data layer project. Figure 2 shows the relevant projects (Customer­Management.Core and Customer­Management.Infastructure) I created for the previous column, alongside the projects after I moved this event into the data layer. The CustomerUpdatedEvent, DTO and Service are now in the Infrastructure project. It was satisfying to move infrastructure logic outside of the domain. I had been bothered by the code smell of needing it in the core domain.

Project Structure Before and After Moving Event Publishing into the Data Layer
Figure 2 Project Structure Before and After Moving Event Publishing into the Data Layer

I have two tests I use to verify that successful inserts and updates do, in fact, publish the correct messages into the queue. A third test verifies that a failed update doesn’t attempt to publish any messages to the queue. You can see these tests in the download solution that accompanies this article.

In the Data Layer, Not in the Domain Model

It’s a really simple change, but I did struggle with this move—not the technical aspect—but in justifying moving the event out of my BC and into my data layer. I had a debate about this on a dog walk. (It is not abnormal for me to be walking through my woods, or up and down my road, talking to myself. Fortu­nately, I live in a quiet place where nobody will question my sanity.) The debate ended with the following conclusion: Publishing the event does not belong in the BC. Why? Because the BC cares only about itself. The BC does not care what other BCs or services or applications want or need. So, “I need to share my persisted data with the Ordering System,” is not a valid concern of the BC. It isn’t a domain event, but an event related to persistence, which I’ll put in the Application Event bucket.

Fixing a New Problem Created by the Move

There is one issue caused by moving the publish event into my persistence layer. The PersistChangeToCustomer method is used to persist other edits to the Customer entity, as well. For example, the Customer entity also has the ability to add or update the customer’s shipping and billing addresses. The addresses are value objects and creating or replacing them with a new set of values reflects a change to the customer.

I need to call PersistChangeToCustomer when either of these addresses change. But in that case, there’s no point in sending a message to the queue saying that the customer name has changed.

So how do you let this persistence layer know the customer name didn’t change? An instinctive solution is to add a flag property such as NameChanged. But I don’t want to have to rely on adding Booleans as needed to track the detailed state. I considered raising an event from the customer class, but not one that would trigger another message to a queue. I don’t want a message that just says, “Don’t send a message.” But how to capture the event?

Once again, Jimmy Bogard comes to the rescue with another brilliant solution. His May 2014 blog post, “A Better Domain Events Pattern” (bit.ly/1vUG3sV), suggests collecting events rather than raising them immediately, then letting the persistence layer grab that collection and handle the events as needed. The point of his pattern is to remove the static DomainEvents class, which doesn’t allow you to control when events are raised and can therefore cause side effects. This is a newer line of thinking with regard to domain events. My refactoring will coincidentally avoid that problem, but I admittedly am still tied to the static DomainEvents class. As always, I will continue to learn and evolve my practices.

I love Bogard’s approach, but I’m going to steal the idea and use it a little differently than his implementation. I don’t need to send a message to the queue; I only need to read the event. And it’s a great way to capture this event in the customer object without creating various random state flags. For example, I can avoid the awkwardness of having to include a Boolean that says, “The name was fixed,” to be set to true or false, as needed.

Bogard uses an ICollection<IDomainEvent> property called Events property in an IEntity interface. If I had more than one entity in my domain, I’d do the same or perhaps add it to an Entity base class. But in this demo, I’ll just put the new property directly into my Customer object. I’ve created a private field and exposed the Events as read-only so only the Customer can modify the collection:

private readonly ICollection<IDomainEvent> _events;
public ICollection<IDomainEvent> Events {
  get { return _events.ToList().AsReadOnly(); }
}

Next, I’ll define the relevant event: CustomerNameFixedEvent, which implements the IDomainEvent interface I used in Part 1 of this column. CustomerNameFixedEvent doesn’t need much. It will set the DateTimeEventOccurred property that’s part of the interface:

public class CustomerNameFixedEvent : IDomainEvent{
  public CustomerNameFixedEvent(){
    DateTimeEventOccurred = DateTime.Now;
  }
  public DateTime DateTimeEventOccurred { get; private set; }   }
}

Now, whenever I call the Customer.FixName method, I can add an instance of this event to the Events collection:

public void FixName(string newName){
  Name = newName;
  ModifiedDate = DateTime.UtcNow;
  _events.Add(new CustomerNameFixedEvent());
}

This gives me something that’s much more loosely coupled than a state property. Additionally, I can add logic to it in the future as my domain evolves without modifying the schema of my Customer class. And I can take advantage of it in my persistence method.

The PersistChangeToCustomer method now has new logic. It will check for this event in the incoming Customer. If that event exists, it will fire off the message to the queue. Figure 3 shows the full method again with the new bit of logic in it—a check for event type before publishing.

Figure 3 PersistChangeToCustomer Method Checking Event Type

public bool PersistChangeToCustomer(Customer customer) {
    using (var context = new CustomerAggregateContext()) {
      context.Customers.Attach(customer);
      context.Entry(customer).State = EntityState.Modified;
      int response = context.SaveChanges();
      if (response > 0) {
        if (customer.Events.OfType<CustomerNameFixedEvent>().Any()) {
          PublishCustomerPersistedEvent(customer, false);
        }
        return true;
      }
      return false;
    }
  }

The method still returns a Boolean showing whether the Customer was successfully saved, indicated by the response to SaveChanges being greater than 0. If that’s the case, then the method will check for any CustomerNameFixedEvents in the Customer.Events and publish the message about the customer being persisted as it did earlier. If for some reason the SaveChanges fails, most likely that will be indicated by an exception. However, I’m also looking at the value of response, so I can return false from the method. The calling logic will decide what to do about a failure—perhaps trying the save again or sending a notification to the end user or some other system.

Because I’m using Entity Framework (EF), it’s worth noting that you can configure EF6 to retry SaveChanges on transient connection errors. But that will have already played out by the time I get a response from SaveChanges. Check my December 2013 article, “Entity Framework 6, Ninja Edition” (msdn.microsoft.com/magazine/dn532202), for more information about DbExecutionStrategy.

I added a new test to verify the new logic. This test creates and persists a new customer, then edits the customer’s BillingAddress property and persists that change. My queue receives a message that the new Customer was created, but it gets no message in response to the update that changes the address:

[TestMethod]
public void WillNotSendMessageToQueueOnSuccessfulCustomerAddressUpdate() {
  Customer customer = Customer.Create("George Jetson", "Friend Referral");
  var repo = new CustomerAggregateRepository();
  repo.PersistNewCustomer(customer);
  customer.CreateNewBillingAddress
    ("123 SkyPad Apartments", "", "Orbit City", "Orbit", "n/a", "");
  repo.PersistChangeToCustomer(customer);
  Assert.Inconclusive(@"Check status of RabbitMQ Manager for a create message,
    but no update message");
}

Stephen Bohlen, who reviewed this article, suggests the “test spy pattern” (xunitpatterns.com/Test Spy.html) as an alternative way of verifying that the messages did make it into the queue.

A Solution in Two Parts

How to share data across bounded contexts is a question many developers learning about DDD ask. Steve Smith and I hinted at this capability in our Pluralsight course, Domain-Driven Design Fundamentals (bit.ly/PS-DDD), but didn’t demonstrate it. We’ve been asked numerous times to elaborate on how to pull this off. In the first article in this little series, I leveraged many tools to construct a workflow that would allow data stored in one BC’s database to be shared with a database used by a different BC. Orchestrating a publish-subscribe pattern using message queues, events and an Inversion of Control container let me achieve the pattern in a very loosely coupled fashion.

This month, I expanded the sample in response to the question: When does it make sense, with a DDD focus, to publish the message? Originally, I triggered the data-sharing workflow by publishing the event from the Customer class as it created new customers or updated customer names. Because all of the mechanics were in place, it was easy in this article to move that logic to a repository, allowing me to delay publishing the message until I was sure the customer data had been successfully persisted into the database.

There’s always more fine-tuning to be done, and perhaps different tools you might prefer to use. But you should now have a handle (no pun intended, but I have to leave it here now) on approaching the DDD pattern of allowing bounded contexts to work with their own independent databases.


Julie Lerman is a Microsoft MVP, .NET mentor and consultant who lives in the hills of Vermont. You can find her presenting on data access and other.NET topics at user groups and conferences around the world. She blogs at thedatafarm.com/blog and is the author of “Programming Entity Framework” (2010), as well as a Code First edition (2011) and a DbContext edition (2012), all from O’Reilly Media. Follow her on Twitter at twitter.com/julielerman and see her Pluralsight courses at juliel.me/PS-Videos.

Thanks to the following Microsoft technical expert for reviewing this article: Stephen Bohlen
Currently a Principal Software Engineer for the Technical Evangelism and Development (TED) Team within Microsoft Corporation, Stephen brings his varied 20-plus-years of experience in software and technology to assist select Microsoft Partner organizations in their adoption of cutting-edge and pre-release Microsoft developer products and technologies.