Event Aggregator: An implementation in C#

Saeed Farahi Mohassel
4 min readFeb 7, 2021

--

Event Aggregator. Design by Saeed Farahi Mohassel

This is a comprehensive document describing this NuGet package. This library is an implementation of Event Aggregator in C# (.NET Standard 2.0).

The relationship between objects is the thing that makes a code-base hard to understand and hard to maintain.

To better understand it, we turn to Martin Fowler:

This image belongs to: https://martinfowler.com

An Event Aggregator is a simple element of indirection. In its simplest form you have it register with all the source objects you are interested in, and have all target objects register with the Event Aggregator. The Event Aggregator responds to any event from a source object by propagating that event to the target objects.

Event aggregator has many benefits. In this article I’m going to show how an event aggregator can make it easier for us to extend our application.

Give me a reason!

To show how it can make our code easier to understand checkout the following models:

public class User {
public string Id { get; set; }
public bool IsMarried { get; set; }
}
public class Resume {
public string Description { get; set; }
public string UserId { get; set; }
public bool IsUserMarried { get; set; }
}
public class Store {
public string Title { get; set; }
public string OwnerId { get; set; }
public bool IsOwnerMarried { get; set; }
}

Here, Resume and Store store the current marital status of the user in a field called IsUserMarried . Suppose each entity is an Aggregate Root so each has its own service:

public class ResumeService {
private readonly ICollection<Resume> db = new List<Resume> {
new Resume {
Description = "My current resume",
UserId = "1",
IsUserMarried = false
}
};
public void SetMaritalStatus(string userId, bool isMarried) {
foreach (var resume in db.Where(a => a.UserId.Equals(userId))) {
resume.IsUserMarried = isMarried;
}
}
}
public class StoreService {
private readonly ICollection<Store> db = new List<Store> {
new Store {
Title = "Restaurant",
OwnerId = "1",
IsOwnerMarried = false
}
};
public void SetMaritalStatus(string ownerId, bool isMarried) {
foreach (var store in db.Where(a => a.OwnerId.Equals(ownerId))) {
store.IsOwnerMarried = isMarried;
}
}
}
public class UserService {
private readonly ICollection<User> db = new List<User> {
new User {
Id = "1",
IsMarried = false
}
};
private readonly ResumeService resumeService;
private readonly StoreService storeService;

public UserService(ResumeService resumeService,
StoreService storeService) {
this.resumeService = resumeService;
this.storeService = storeService;
}
public void GotMarried(string userId) {
var user = db.First(a => a.Id.Equals(userId));
user.IsMarried = true;

// propagate changes to other parts of the code
resumeService.SetMaritalStatus(userId, true);
storeService.SetMaritalStatus(userId, true);
}
}

ResumeService and StoreService both have a method ( SetMaritalStatus ) that updates the marital status of user. As you can see UserService has a dependency to both services, because when a user gets married, UserService wants to inform other services. This code works, but it has two drawbacks:

1- UserService actually does not depend on ResumeService or StoreService to perform its operations! => (False dependency)

2- Whenever we add a new entity that stores the marital status of a user, we have to remember to update the GotMarried method of UserService ! => (Hard to extend)

The solution: Event Aggregator

Instead of introducing dependencies (other services) to UserService we can adapt define an event:

public class MaritalStatusChanged : IEvent {
public MaritalStatusChanged(string userId, bool isMarried) {
UserId = userId;
IsMarried = isMarried;
}
public string UserId { get; }
public bool IsMarried { get; }
}

Then we need to update UserService . First remove dependencies, then update the GotMarried method:

public class UserService {
private readonly ICollection<User> db = new List<User> {
new User {
Id = "1",
IsMarried = false
}
};
private readonly IEventEmitter eventEmitter;
public UserService(IEventEmitter eventEmitter) {
this.eventEmitter = eventEmitter;
}
public void GotMarried(string userId) {
var user = db.First(a => a.Id.Equals(userId));
user.IsMarried = true;

// propagate changes to other parts of the code
eventEmitter.Publish(new MaritalStatusChanged(userId, true));
}
}

So now, it only depends upon and event emitter. Event emitter is our event bus! It publishes event throughout our domain. Now, if want to be informed of this event, we simple create a handler. For example this is a handler added to the body of ResumeService :

public class MaritalStatusChangedHandler : 
IEventHandler<MaritalStatusChanged>
{
private readonly ResumeService service;
public MaritalStatusChangedHandler(ResumeService service) {
this.service = service;
}
public Task Handle(MaritalStatusChanged ev) {
service.SetMaritalStatus(ev.UserId, ev.IsMarried);
return Task.CompletedTask;
}
}

Glue them together:

// 1- create an event bus
var bus = new DefaultEventBus();
// 2- create services
var userService = new UserService(bus);
var resumeService = new ResumeService();
var storeService = new StoreService();
// 3- subscribe
bus.Subscribe<MaritalStatusChanged, ResumeService.MaritalStatusChangedHandler>(
new ResumeService.MaritalStatusChangedHandler(resumeService));
bus.Subscribe<MaritalStatusChanged, StoreService.MaritalStatusChangedHandler>(
new StoreService.MaritalStatusChangedHandler(storeService));
// 4- someone got married
userService.GotMarried("1");

1- This creates the event bus. Event bus implements both IEventEmitter and IEventSink. IEventEmitter publishes events and IEventSink let’s you to subscribe to events.

The full source for these code is here.

--

--

Saeed Farahi Mohassel

An experience Software Developer with a focus in backend, micro-services and cloud native applications.