Domain Events, CQRS, and dependency resolution in handlers

aggregateasp.net-corecqrsdomain-driven-designevent handling

Currently: ASP.NET Core 2.2.

I've been doing quite an extensive research in this topic (Domain Driven Design used together with Clean Architecture):

DDD: Where to place domain event handlers?

And I've seen a couple of DDD and Clean Architecture Repos to better understand:

Some other topics:

(plus many other resources)

And I've even been skimming through Vaughn Vernon's book..

I'm trying to keep things as simple as possible. But there is something I'm not understanding… If the Aggregates themselves are the ones supposed to raise the domain events, how are we supposed to resolve the dependencies that the event handler need in order to execute successfully?

Let me explain (This is my complete understanding are you are more than welcome to correct me):

Aggregates are the ones in charge of raising the event. You would have an Event Bus that will subscribe and publish the Events and Handlers accordingly. So far so good. The problem starts when you're going to resolve the handler's dependencies.

From what I'm understanding, Aggregates should have zero external dependencies (That should be the Application's Layer responsibility), therefore you're forced to use a static method that raises the event inside the Aggregate. Once raised, you dispatch them to the respective handler. But what happens if the handler itself has dependencies such as a Repository/Service/Processor that needs to be instantiated?

Wouldn't I have to create some sort of factory that creates these dependencies? That means that I wouldn't be able to use the IoC library to make the dependencies for me, right?

I thought on using MediatR to handle same-tier, same-domain dispatches because it wires into my Dependency Resolver and automatically injects all the dependencies. For this to be achieved, MediatR would need to be injected in the Aggregate, breaking the principle of having no external dependencies.

From one of the GitHub links above, the Ametista project has the events raised in the Application Layer (Command Stack), and not inside the Aggregates.

While that would violate the Aggregate principle, it would've solved the dependency issue. Yet, it would introduce another problem, which is domain events duplication. You would've to reimplement the same event each time the criteria is met for that event to be raised.

That just put me to think… deep, and made me come up with the following (untested) solution (Which I don't have high hopes that it's going to work):

We implement the Service Locator pattern to wrap up MediatR in a Static Class. We would inject via a static method and property the Startup.cs Service Provider. I don't know if the shared static property across the entire application is a good idea. I also don't know if the Service Provider would get disposed or inaccessible in a certain part of the app. I also don't know if I'm creating a consistent MediatR instance that will be able to publish all the events transaction-wise from the bounded context (That is, all the events that were raised in the domain would work as a same transaction within the same bounded context).

In Startup.cs:

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{

     StaticMediatR.LoadServiceProvider(app.ApplicationServices);
}


    public class StaticMediatR
    {
        private static IServiceProvider _serviceProvider;

        public static void LoadServiceProvider(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        public static IMediator Mediator()
        {
            var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();

                return serviceScope.ServiceProvider.GetRequiredService<IMediator>();

        }

    }

I haven't tried the code above yet… That's what I have in mind.

Edit: I just tried the code above… and effectively, it's been disposed.

Edit x2: Wait, removing the Using does not dispose the object. I've made the changes above.

Best Answer

Using a static class vs injecting some sort of "event bus" into either an Aggregate's constructor or into one of it's methods is a matter of preference, and each has its trade-offs.

While injecting the eventing infrastructure does yield slightly more flexibility, it also means splattering this concern all over your domain. Is EventBus really something a domain expert talks about? Not usually. Furthermore, in my experience, I have never once found myself wanting for the flexibility pure injection gives. The event paradigm is rather simple and tends to remain constant (for good reason).

On the other hand while a static class may "hide" a dependency (although it should be common knowledge your domain objects will need to raise events), it offers a one-line solution to raising events without mucking up method/constructor signatures. In practice, I find it's worth this trade-off.

All that said, your question seems to be more about dependency injection than anything else. How an event is raised is a different concern than how it is handled. You are correct that your event handlers will have dependencies, and that wiring them up is a pain. Using a DI container is a pretty standard solution here, and can save you quite a bit of headache.

Related Topic