Skip to content

Instantly share code, notes, and snippets.

@vkhorikov
Last active June 6, 2019 18:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vkhorikov/ee12c0982bf177db155c12621cafc58d to your computer and use it in GitHub Desktop.
Save vkhorikov/ee12c0982bf177db155c12621cafc58d to your computer and use it in GitHub Desktop.
Merging domain events
public sealed class Messages
{
private readonly IServiceProvider _provider;
public Messages(IServiceProvider provider)
{
_provider = provider;
}
public void Dispatch(IDomainEvent domainEvent)
{
Type type = typeof(IDomainEventHandler<>);
Type[] typeArgs = { domainEvent.GetType() };
Type handlerType = type.MakeGenericType(typeArgs);
dynamic handler = _provider.GetService(handlerType);
handler.Handle((dynamic)domainEvent);
}
}
public class Order
{
public void Cancel()
{
/* ... */
AddDomainEvent(new OrderCanceledEvent(Id));
}
}
public class OrderCanceledEvent : IDomainEvent
{
public int OrderId { get; }
public OrderCanceledEvent(int orderId)
{
OrderId = orderId;
}
}
public class OrderCanceledEventHandler : IDomainEventHandler<OrderCanceledEvent>
{
private readonly MessageBusGateway _gateway;
public OrderCanceledEventHandler(MessageBusGateway gateway)
{
_gateway = gateway;
}
public void Handle(OrderCanceledEvent domainEvent)
{
_gateway.SendOrderCanceledMessage(domainEvent.OrderId);
}
}
public class Customer
{
public void Deactivate()
{
foreach (Order order in Orders)
{
order.Cancel();
}
AddDomainEvent(new CustomerDeactivatedEvent(Id));
}
}
public class CustomerDeactivatedEventHandler : IDomainEventHandler<CustomerDeactivatedEvent>
{
public void Handle(CustomerDeactivatedEvent domainEvent)
{
_gateway.SendCustomerDeactivatedMessage(domainEvent.CustomerId);
}
}
public class Order
{
public void Cancel(bool generateEvent)
{
/* ... */
if (generateEvent)
{
AddDomainEvent(new OrderCanceledEvent(Id));
}
}
}
public abstract class DomainEvent
{
protected virtual Type SupersetFor => null;
public bool IsSupersetFor(DomainEvent domainEvent)
{
return domainEvent.GetType() == SupersetFor;
}
}
public class CustomerDeactivatedEvent : DomainEvent
{
protected override Type SupersetFor => typeof(OrderCanceledEvent);
}
public abstract class AggregateRoot
{
private readonly List<DomainEvent> _domainEvents = new List<DomainEvent>();
public IReadOnlyList<DomainEvent> DomainEvents => _domainEvents;
protected virtual void AddDomainEvent(DomainEvent newEvent)
{
if (_domainEvents.Any(existing => existing.IsSupersetFor(newEvent)))
return;
_domainEvents.Add(newEvent);
}
}
internal class EventDispatcher :
IPostInsertEventListener,
IPostDeleteEventListener,
IPostUpdateEventListener,
IPostCollectionUpdateEventListener
{
public void OnPostUpdate(PostUpdateEvent ev)
{
DispatchEvents(ev.Entity as AggregateRoot);
}
public void OnPostDelete(PostDeleteEvent ev)
{
DispatchEvents(ev.Entity as AggregateRoot);
}
public void OnPostInsert(PostInsertEvent ev)
{
DispatchEvents(ev.Entity as AggregateRoot);
}
public void OnPostUpdateCollection(PostCollectionUpdateEvent ev)
{
DispatchEvents(ev.AffectedOwnerOrNull as AggregateRoot);
}
private void DispatchEvents(AggregateRoot aggregateRoot)
{
if (aggregateRoot == null)
return;
// New functionality
IDomainEvent[] reduced = EventReducer.ReduceEvents(aggregateRoot.DomainEvents);
foreach (IDomainEvent domainEvent in reduced)
{
Messages.Dispatch(domainEvent);
}
aggregateRoot.ClearEvents();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment