Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Domain Events without Service Locator

Giving the default implementation of Domain Events:

Interface that represents an domain event:

public interface IDomainEvent { }

Interface that represents a generic domain event handler:

public interface IEventHandler<T> where T : IDomainEvent

Central access point to raise new events:

public static class DomainEvents
{
    public static void Raise<T>(T event) where T : IDomainEvent
    {
        //Factory is a IoC container like Ninject. (Service Location/Bad thing)
        var eventHandlers = Factory.GetAll<IEventHandler<T>>();

        foreach (var handler in eventHandlers )
        {
            handler.Handle(event);
        }
    }
}

Consuming:

public class SaleCanceled : IDomainEvent
{
    private readonly Sale sale;

    public SaleCanceled(Sale sale)
    {
        this.sale = sale;
    }

    public Sale Sale
    {
        get{ return sale; }
    }
}

Service that raises the event:

public class SalesService
{
     public void CancelSale(int saleId)
     {
          // do cancel operation

          // creates an instance of SaleCanceled event

          // raises the event
          DomainEvents.Raise(instanceOfSaleCanceledEvent);
     } 
}

Is there another approach to use Domain Events without use of Service Location anti-pattern?

like image 899
Vinicius Gonçalves Avatar asked Mar 10 '15 16:03

Vinicius Gonçalves


People also ask

When to use domain events?

Use domain events to explicitly implement side effects of changes within your domain. In other words, and using DDD terminology, use domain events to explicitly implement side effects across multiple aggregates.


1 Answers

I guess in your case you really do not have to. Using dependency injection you could inject a IDomainEventDispatcher implementation into your service.

The reason why I think a singleton like this has made it into the mainstream it was one of the first implementations to be proposed by some prominent developers and at first it doesn't feel too wrong. The other reason is that events may need to be raised from within the domain:

public class Customer
{
    public void Enable()
    {
        _enabled = true;

        DomainEvents.Raise(new CustomerEnabledEvent(_id));
    }
}

At some stage I came across this post by Jan Kronquist: http://www.jayway.com/2013/06/20/dont-publish-domain-events-return-them/

This is the third time I have added that link to my answers since I have to give credit to this for changing my thinking. However, I think I'll stop doing that now. Sorry Jan :)

So the point is that we can change our implementation to the following:

public class Customer
{
    public CustomerEnabledEvent Enable()
    {
        _enabled = true;

        return new CustomerEnabledEvent(_id);
    }
}

Now our service can be changed to use an injected dispatcher:

public class CustomerService
{
    private IDomainEventDispatch _dispatcher;
    private ICustomerRepository _customerRepository;

    public CustomerService(ICustomerRepository customerRepository, IDomainEventDispatch dispatcher)
    {
        _customerRepository = customerRepository;
        _dispatcher = dispatcher;
    }

    public void Enable(Guid customerId)
    {
        _dispatcher.Raise(_customerRepository.Get(customerId).Enable());
    }
}

So no singleton is required and you can happily inject the dependency.

like image 150
Eben Roux Avatar answered Sep 21 '22 09:09

Eben Roux