Last time I wrote a post about the commands. Events are quite similar to the commands – the main difference between these 2 patterns is that the events are related to the things that have actually happened to our entity e.g. by invoking the command. They’re also a core part of the DDD (Domain Driven Design) and can be easily implemented within our software solution.
Let’s define our event interface at first:
1 2 3 4 |
//Marker public interface IEvent { } |
Now, we can move further and take a look into the base Entity class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public interface IEntity { IEnumerable<IEvent> Events { get; } } public abstract class Entity : IEntity { private readonly IDictionary<Type, IEvent> _events = new Dictionary<Type, IEvent>(); public IEnumerable<IEvent> Events => _events.Values; protected void AddEvent(IEvent @event) { _events[@event.GetType()] = @event; } protected void ClearEvents() { _events.Clear(); } } |
In case you were wondering why I’m using a dictionary here – thanks to this type of a collection I can avoid adding multiple events of the same type e.g. UserEmailChanged and then invoking them multiple times. Yet, again – it’s totally up to you, so feel free to choose the preferable solution.
So far so good, eventually we can define our event once the user’s email was changed.
1 2 3 4 5 6 7 8 9 |
public class UserEmailChanged : IEvent { public Guid Id { get; } public UserEmailChanged(Guid id) { Id = id; } } |
Remember that you’d better keep your events very thin. Sure thing, you could pass the whole user object, but think outside of the box – what if you want to scale your application on multiple servers? By passing the minimum amount of the information required to identify the entity (e.g. it’s id and maybe an old value along with the new one) you will be able to push such event by message bus or any other communication protocol and handle easily by some external application, without worrying about (de)serialization issues and huge traffic due to the large objects.
And here’s our user:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class User : Entity { public string Email { get; protected set; } public string Password { get; protected set; } public DateTime UpdatedAt { get; protected set; } protected User() { } public User(string email) { SetEmail(email); } public void SetEmail(string email) { if (string.IsNullOrWhiteSpace(email)) throw new ArgumentException("Email can not be empty.", nameof(email)); if (Email.Equals(email)) return; Email = email; UpdatedAt = DateTime.UtcNow; AddEvent(new UserEmailChanged(Id)); } } |
As you can see, whenever the user changes the email address, we will store such event.
And here comes to the question, how to handle this event? Well, quite easily, so let’s start with the event handler definition:
1 2 3 4 5 6 7 8 9 10 11 12 |
public interface IEventHandler<in T> where T : IEvent { Task HandleAsync(T @event); } public class UserEmailChangedHandler : IEventHandler<UserEmailChanged> { public async Task HandleAsync(UserEmailChanged @event) { //Fetch the user from database by id, log this event, store some data etc. } } |
And the event dispatcher based on the Autofac IoC container:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public interface IEventDispatcher { Task DispatchAsync<T>(params T[] events) where T : IEvent; } public class EventDispatcher : IEventDispatcher { private readonly IComponentContext _context; public EventDispatcher(IComponentContext context) { _context = context; } //I need to find out how to get generic handler in Autofac public async Task DispatchAsync<T>(params T[] events) where T : IEvent { foreach (var @event in events) { if (@event == null) throw new ArgumentNullException(nameof(@event), "Event can not be null."); var eventType = @event.GetType(); var handlerType = typeof(IEventHandler<>).MakeGenericType(eventType); object handler; _context.TryResolve(handlerType, out handler); if (handler == null) return; //GetRuntimeMethods() works with .NET Core, otherwise simply use GetMethod() var method = handler.GetType() .GetRuntimeMethods() .First(x => x.Name.Equals("HandleAsync")); await (Task) ((dynamic) handler).HandleAsync(@event); } } } |
Speaking of the Autofac, we have to register our handlers and dispatcher, so that IoC magic will be able to happen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class EventModule : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType<EventDispatcher>() .As<IEventDispatcher>() .InstancePerLifetimeScope(); var assembly = Assembly.Load(new AssemblyName("MyAssemblyNamespace")); builder.RegisterAssemblyTypes(assembly).AsClosedTypesOf(typeof(IEventHandler<>)); } } //Remember to register the module within the IContainer public static class Container { public static IContainer Resolve() { var builder = new ContainerBuilder(); builder.RegisterModule<EventModule>(); return builder.Build(); } } |
And that’s pretty much it. What you can do now, is, for example, the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public interface IUserService { Task CreateAsync(string email); } public class UserService : IUserService { private readonly IEventDispatcher _eventDispatcher; public UserService(IEventDispatcher eventDispatcher) { _eventDispatcher = eventDispatcher; } public async Task CreateAsync(string email) { var user = new User(email); //Store the user somehwere safe using the repository etc. ... //Dispatch all of the user events await _eventDispatcher.DispatchAsync(user.Events.ToArray()); } } |
My approach to the events is mostly based on the Jimmy Bogard’s post which is an opposite to the static pattern described by the Udi Dahan here.
Pingback: Handling domain events - How to Code .NET
var method = handler.GetType()
.GetRuntimeMethods()
.First(x => x.Name.Equals(“HandleAsync”));
This will not work if a handler implements multiple IEventHandler interfaces. This can be a valid possibility. E.g. Sagas that mutate the state on multiple different events.
Its better to do it like this: ((dynamic)handler).HandleAsync(@event)
(because you get the handler using the interface you know that method exists)
Hi Philip,
Thanks, that’s a good remark, I’ve updated the code example.
Cheers
Pingback: Dew Drop - August 1, 2016 (#2299) - Morning Dew
I love Jimmy Bogard’s MediatR project for just this reason.
https://github.com/jbogard/MediatR/wiki
I’ve heard about this library but never actually tried it out yet, will definitely take a look.
Excellent article, but wouldn’t make more sense to use a ConcurrentDictionary instead of a plain Dictionary?
Thanks!
Sure, feel free to use ConcurrentDictionary in order to make this operation thread-safe.
Hi,
I also implemented domain events recently. Read a lot of articles online and decided to go with this approach https://www.jayway.com/2013/06/20/dont-publish-domain-events-return-them/.
I don’t understand Jimmy Bogard’s blog post, why you would want to dispatch the events before commiting the transaction. Specifically in your case what happens if the email change is not successfully persisted into the DB or if the transaction is rolled back?
Hi Jernej,
In my scenario, I publish the events after the transaction has been completed – for example within the IUserService -> CreateAsync(); dispatching events is the last thing that happens only after the particular operation (e.g. creating and saving a new user in the database) was successfully completed. I hope that it does answers your question (rolling back the transaction would prevent invoking the event dispatcher).
Pingback: "Use your feelings, Obi-Wan, and find Links you will." – Yoda - Magnus Udbjørg
Makes sense. But I forgot to mention I’m working with a long living db context (desktop app). So after rolling back I would need to clear the _events collection in every entity that participated in the failed transaction whereas if you are returning events from commands, you are not mutating states of any entities.
Yes, you’re right.
One cool side effect when returning events from commands is that you can have a EventDispatcherCommandHandler that you can wrap arround a TransactionalCommandHandler and let the command handling pipeline automatically dispatch events so you don’t need to dispatch them manually from application services 🙂
This is something that Jimmy has implemented within the EF’s DbContext :).
Pingback: Zbudujemy nowy DOM – Dev on board
You used 2 constructor for User class. One of them is paremeterless and other gets email parameter. Do you want to take precautions to don’t send email while update data?
I want to make sure that the user instance will always have a valid email address assigned to it (not an anemic class).
I agree that event classes should be small and contain only serializable state, but in case of UserEmailChanged shouldn’t we also include old and new value of user email. Let’s consider situation in which user quickly changes emails from foo1@example.com to foo2@example.com and to foo3@example.com – then if our infrastructure responsible for event processing will not be fast enough we may lost information about first change. Storing additional data in the event also will allow us to more easily debug production issues (if we save event’s somewhere of course).
Of course, this is a good example and I also do it like this. A few properties are good enough, we just have to keep in mind that passing nested objects via service bus is rather not a best choice.
What about “SomethingCreatedEvent” when you are applying CQRS so you have to update the querry database? You’ll need sometimes 15-20 properties to materialize complete view in query database. Any alternatives?
That should not be an issue at all. When you make use of the events in memory (not shared across service bus), feel free to include the whole object if needed. You could also send only the entity ID and then fetch the actual object using some repository or so.
Great 🙂 Now I’m sure I’m on the right track. Thank you for this post!
With Autofac extension generic method ResolveOptional we can do instead of this:
var eventType = @event.GetType();
var handlerType = typeof(IEventHandler).MakeGenericType(eventType);
object handler;
_context.TryResolve(handlerType, out handler)
if (handler == null)
return;
var method = handler.GetType()
.GetRuntimeMethods()
.First(x => x.Name.Equals(“HandleAsync”));
await (Task) ((dynamic) handler).HandleAsync(@event);
just this:
var handler =_context.ResolveOptional<IEventHandler<T>>();
if (handler == null) return;
await handler.HandleAsync(@event);
Thanks, wasn’t even aware of this.
A little mistake in my comment. ResoveOptional should be called with type argument IEventHandler:
var handler =_context.ResolveOptional<IEventHandler>();
oh, now I see, that’s is not my mistake. Type argument was automatically removed . Type argument should be “IEventHandler” (remove underscore)
Not a problem, thanks :).
Great post, just interest where would you like to put your event handler? and why? I think the application service may be a good layer to put the event handler.
Thanks, I’d put the event handlers in the layer above Core e.g. Infrastructure, where the application services are implemented.
Hi
Can you share a sample code using the above code to see the full picture when it comes to using Ioc, UserService, etc.
Thanks
Bilal
You can find such samples in this repository: https://github.com/passenger-stack/Passenger
First of all thank you so much for sharing this. I’ve been searching for this for a long time. You have no idea how happy it made me upon reading this article. Keep up the good work!
With that said – I’m getting this error. Can you please throw some light on this?
The best overloaded method match for ‘Domain.Services.Processing.Order.SalesOrderLineCreateHandler.HandleAsync(Models.Entities.Processing.Order.SalesOrderLineCreated)’ has some invalid arguments
happens on the last step of the EventDispatcher
await (Task) ((dynamic) handler).HandleAsync(@event);
fixed:
await (Task) ((dynamic) handler).HandleAsync((dynamic) @event);
Try to remove the dynamic and provide IEventHandler while casting, let me know if that helps.
Are you using handler to get state of DM?
public async Task HandleAsync(UserEmailChanged @event)
{
//Fetch the user from database by id, log this event, store some data etc.
You can’t do like that.
Register handler by Container has one problem. You can use only one scenario per event. What you gone do if two application services will use the same event which has difference user case. ?
I could easily register as many different handlers per single event as needed, and then instead of e.g. Resolve call ResolveMany or so.
AddEvent(new UserEmailChanged(Id));
It should belong to application services or less common scenario to domain services.
DM should only publish the event. Responsibility of subscribe events not belong to DM.
Domain models do not publish events at all. They only store the events in their internal collection (as shown above) and only then, events are being dispatched by EventDispatcher.
“only publish”
Isn’t there a bug in dispatcher code, i think lines:
if (handler == null)
return;
should be replaced with:
if (handler == null)
continue;
if we don’t have a single handler we still want to handle other types of events (we are in foreach loop).
HI PIOTR
I created a sample application based on your post and In EventDispatcher.cs class, I got handler=null in DispatchAsync() method. due to this my handler didn’t invoke.
Please do the needful.
Thanks in advance.