Skip to content

Learning how to implement domain events without any external references or DI in a domain entity

License

Notifications You must be signed in to change notification settings

GLARDEN/DDDPracticeDomainEvents

Repository files navigation

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.

  1. 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

  1. 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>();
      }
  1. 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

About

Learning how to implement domain events without any external references or DI in a domain entity

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published