Search code examples
domain-driven-designcqrsncqrs

Ncqrs: How to raise an Event without having an Aggregate Root


Given I have two Bounded Contexts:

  1. Fleet Mgt - simple CRUD-based supporting sub-domain
  2. Sales - which is my CQRS-based Core Domain

When a CRUD operation occurs in the fleet management, an event reflecting the operation should be published:

  • AircraftCreated
  • AircraftUpdated
  • AircraftDeleted
  • etc.

These events are required a) to update various index tables that are needed in the Sales domain and b) to provide a unified audit log.

Question: Is there an easy way to store and publish these events (to the InProcessEventBus, I'm not using NSB here) without going through an AggregateRoot, which I wouldn't need in a simple CRUD context.


Solution

  • According to Pieter, the main contributor of Ncqrs, there is no way to do this out of the box.

    In this scenario I don't want to go through the whole ceremony of creating and executing a command, then loading an aggregate root from the event store just to have it emit the event.

    The behavior is simple CRUD, implemented using the simplest possible solution, which in this specific case is forms-over-data using Entity Framework. The only thing I need is an event being published once a transaction occurred.

    My solution looks like this:

    // Abstract base class that provides a Unit Of Work
    
    public abstract class EventPublisherMappedByConvention 
        : AggregateRootMappedByConvention
    {
        public void Raise(ISourcedEvent e)
        {
            var context = NcqrsEnvironment.Get<IUnitOfWorkFactory>()
                .CreateUnitOfWork(e.EventIdentifier);
            ApplyEvent(e);
            context.Accept();
        }
    }
    
    // Concrete implementation for my specific domain
    // Note: The events only reflect the CRUD that's happened.
    // The methods themselves can stay empty, state has been persisted through
    // other means anyway.
    
    public class FleetManagementEventSource : EventPublisherMappedByConvention
    {
        protected void OnAircraftTypeCreated(AircraftTypeCreated e) { }
        protected void OnAircraftTypeUpdated(AircraftTypeUpdated e) { }
        // ...
    }
    
    // This can be called from anywhere in my application, once the 
    // EF-based transaction has succeeded:
    
    new FleetManagementEventSource().Raise(new AircraftTypeUpdated { ... });