Skip to content

Transitioning to Event Sourcing, part 5: use events for updating your domain database

by Julien on October 24th, 2010

Une version française de ce billet est disponible ici.

Overview

The previous step brought a problem: events generated by our aggregates form a model, a representation of our domain. The transactional database, maintained by NHibernate as well. How can we be sure that the 2 models are in sync? This is critical if we want to later switch to event sourcing. We want to be 100% certain that our events are tracking all what is needed to rebuild our aggregates. We also want to make sure that nothing is written in the database that should not. Can we prove that what NHibernates persist all what’s in the events, and only that? Of course not, because there is no relationship between the events and the persistence mechanism:

What we could do however is using the events to persist our aggregate changes:

This is the object of this post.

Benefits

Consistency

The events are now triggering state changes within our aggregate as well as persistence in our database.

Performance

We don’t rely on NHibernate’s session to track changes within our aggregates anymore. Flushing an NHibernate session is an expensive operation: NHibernate must compare state of all loaded entities with their initial values, and generate corresponding SQL statement.

When using event handlers, though, we are never flushing. We execute custom built SQL statements, based on our events. We can do that, because our events are already describing changes within our aggregates. We don’t need to calculate what has changed, it is already done.

Implementation

What changed

The persisting event handlers have their own project:

Custom unit of work

The first element that will be necessary is the repository must now save the list of aggregates being created and loaded:

public T ById(Guid key)
{
    var resVal =  persistenceManager.CurrentSession.Get<T>(key);
    AddToContext(resVal);
    return resVal;
}

public void Add(T toAdd)
{
    AddToContext(toAdd);
}

private void AddToContext(T toAdd)
{
    HashSet<IAggregateRoot> aggregates = context[NHibernatePersistenceManager.AGGREGATE_KEY] as HashSet<IAggregateRoot>;

    if (aggregates == null)
    {
        aggregates = new HashSet<IAggregateRoot>();
        context[NHibernatePersistenceManager.AGGREGATE_KEY] = aggregates;
    }

    aggregates.Add(toAdd);
}

Which means when the persistence manager is commited, we can call the event handlers:

public void Commit()
{
    var aggregates = context[AGGREGATE_KEY] as HashSet<IAggregateRoot>;

    if (aggregates != null && aggregates.Count > 0)
    {
        var session = EnsureOpened();

        using (var tx = session.Connection.BeginTransaction(IsolationLevel.ReadCommitted))
        {
            context[TRANSACTION_KEY] = tx;

            foreach (var ar in aggregates)
            {
                foreach (var evt in ar.UncommitedEvents)
                {
                    eventBus.Publish(evt);
                }
            }

            context[TRANSACTION_KEY] = null;
            tx.Commit();

            context[AGGREGATE_KEY] = null;
        }
    }
}

Event handlers

Persistence event handlers implementation is trivial. For example:

public void Handle(StudentNameCorrectedEvent evt)
{
    persistenceManager.ExecuteNonQuery(
        "UPDATE [Student] SET firstName = @FirstName, lastName = @LastName WHERE Id = @Id",
        new
        {
            Id = evt.StudentId,
            FirstName = evt.FirstName,
            LastName = evt.LastName
        });
}

Running the sample

The sources for the entire serie is available on GitHub at http://github.com/jletroui/TransitioningToEventSourcing
.

To run this sample, simply create a new “DDDPart5″ database in SQLExpress before launching it.

Transitioning to Event Sourcing posts:

From → Event Sourcing

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS