Double M
Double M

Reputation: 1503

How are domain events dispatched from within domain objects?

Domain objects shouldn't have any dependencies, hence no dependency injection either. However, when dispatching domain events from within domain objects, I'll likely want to use a centralised EventDispatcher. How could I get hold of one?

I do not want to return a list of events to the caller, as I'd like them to remain opaque and guarantee their dispatch. Those events should only be consumed by other domain objects and services that need to enforce an eventual consistent constraint.

Upvotes: 6

Views: 1376

Answers (3)

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57239

I'll likely want to use a centralised EventDispatcher. How could I get hold of one?

Pass it in as an argument.

It probably won't look like an EventDispatcher, but instead like some Domain Service that describes the required capability in domain specific terms. When composing the application, you choose which implementation of the service to use.

Upvotes: 3

Hooman Bahreini
Hooman Bahreini

Reputation: 15559

See Udi Dahan's domain events

Basically, you register one or more handlers for your domain events, then raise an event like this:

public class Customer
{
   public void DoSomething()
   {
      DomainEvents.Raise(new CustomerBecamePreferred() { Customer = this });
   }
}

And all the registered handler will be executed:

public void DoSomethingShouldMakeCustomerPreferred()
{
   var c = new Customer();
   Customer preferred = null;

   DomainEvents.Register<CustomerBecamePreferred>(p => preferred = p.Customer);

   c.DoSomething();
   Assert(preferred == c && c.IsPreferred);
}

This is basically implementing Hollywood Principle (Don't call us, we will call you), as you don't call the event handler directly - instead the event handler(s) get executed when the event is raised.

Upvotes: 2

CPerson
CPerson

Reputation: 1222

You are asking to have it both ways. You either need to inject the dependency or invert control and let another object manager the interaction between Aggregate and EventDispatcher. I recommend keeping your Aggregates as simple as possible so that they are free of dependencies and remain testable as well.

The following code sample is very simple and would not be what you put into production, but illustrates how to design Aggregates free of dependencies without passing around a list of events outside of a context that needs them.

If your Aggregate has a list of events within it:

class MyAggregate 
{
    private List<IEvent> events = new List<IEvent>();

    // ... Constructor and event sourcing?

    public IEnumerable<IEvent> Events => events;

    public string Name { get; private set; }

    public void ChangeName(string name)
    {
        if (Name != name) 
        { 
            events.Add(new NameChanged(name); 
        }
    }
}

Then you might have a handler that looks like:

public class MyHandler 
{
    private Repository repository;

    // ... Constructor and dependency injection

    public void Handle(object id, ChangeName cmd)
    {
        var agg = repository.Load(id);
        agg.ChangeName(cmd.Name);
        repository.Save(agg);
    }
}

And a repository that looks like:

class Repository
{
    private EventDispatcher dispatcher;

    // ... Constructor and dependency injection

    public void Save(MyAggregate agg)
    {
        foreach (var e in agg.Events)
        {
            dispatcher.Dispatch(e);                
        }
    }
}

Upvotes: 1

Related Questions