DDD Practice project - Implement Domain Entity Events
Created new project using Clean Architecture Visual Studio template as a test project to practice implementing Example 11 from DDD NoDuplicates in a .net core 6 web project.
Implementation Highlights
Code below is provided by sample projects below.
- Method from Project class that calls the domain event to validate the requested project name is unique.
public void UpdateName(string newName)
{
Guard.Against.NullOrEmpty(newName, nameof(newName));
DomainEvents.Raise(new ProjectNameChangeRequested(Id, newName)).GetAwaiter().GetResult();
Name = newName;
}
QUESTION
- Setting up Mediator in Program.cs (.net core 6 single file set up). SetUpMediatR method was changed to use IApplicationBuilder to get the application services.
NOTE: I'm not sure if this is correct! It seems to work great but I'm not sure I fully understand how the GetRequiredService call from IApplicationBuilder works. I believe serviceProvider.GetRequiredService<IMediator>(); will return a unique instance of the service per request.
In the ProjectNameChangeHandler I added a delay to the validation for a specifc project id to make sure this was not a blocking call. I opened 2 instances of swagger and triggered Product.UpdateName for both id 1 & 2. I was able to call Product.UpdateName for ID 2 while the validation for the ID 1 was sleeping. SUCCESS!
public async Task Handle(ProjectNameChangeRequested notification, CancellationToken cancellationToken)
{
if(notification.Id == 1)
{
Thread.Sleep(10000);
}
var findProjectsWithSameNameSpec = new FindProjectsWithSameNameSpec(notification.Id, notification.NewName);
var foundDuplicateRoleName = await _repository.AnyAsync(findProjectsWithSameNameSpec);
if (foundDuplicateRoleName)
{
throw new DuplicateProjectNameException($"{notification.NewName} already exists.");
}
}
}
//Set up a static instance of mediator to handle Immediate Domain Events.
//Program.cs truncated for readability
SetUpMediatR(app);
app.Run();
void SetUpMediatR(IApplicationBuilder app)
{
var serviceProvider = app.ApplicationServices;
DomainEvents.Mediator = () => BuildMediator(serviceProvider);
}
IMediator BuildMediator(IServiceProvider serviceProvider)
{
return serviceProvider.GetRequiredService<IMediator>();
}
- We need a static instance of Mediator which is implemented by creating a static DomainEvents Class, so that we can avoid the need to DI Mediator into our domain entity.
public static class DomainEvents
{
public static Func<IMediator> Mediator;
public static async Task Raise<T>(T args) where T : INotification
{
var mediator = Mediator.Invoke();
await mediator.Publish(args);
}
}
Reference Projects