1

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.

1

Note: the literature sucks. You aren't missing anything easy.

Aggregates should have zero external dependencies

That's right.

therefore you're forced to use a static method that raises the event inside the Aggregate

That's not right. You are allowed to pass in the event raising capability as an argument. Think "domain service" -- we're expressing the ability to share information in the language of the domain.

(Note: "event bus" is probably not in the language of your domain).

As far as the domain logic of the aggregate is concerned, we're just taking a bunch of information and throwing it into an "out box". What that outbox does with the information is not the aggregate's concern.

Your complicated graph of dependencies doesn't get injected into the aggregate, but instead into the handler for the aggregate, which will pass the capability into the aggregate when needed.

  • Thank you so much for the explanation 😊. That means that I will pass the Event Bus as part of the constructor. I guess it's going to come through the Application Layer... Or should I wire the aggregates to an interface and Inject them into the Application Layer as well? – Jose A Jan 19 at 2:08
1

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.

Oh no I'm not.

Zero external dependencies doesn't mean it takes no parameters. It means it doesn't know or care what it's talking to.

By using static methods you are NOT avoiding a dependency. You are hiding one. And you're doing it in a way that hard codes it. Even using a service locator creates a dependency, on the locator.

What you should study to fix this problem is called pure dependency injection. That's a fancy term new term for old school reference passing.

Using that your handlers wont know what they talk to until they are told what to talk to.

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?

Anything that lives for the life to the program can be created in main and passed to whatever needs to talk to it. However, some things, such as timestamps, need to not exist until their time. For these abstract factory is a wonderful pattern.

You don't have to use a hard coded static method to create things on the fly. You can use polymorphism to make whatever whenever. This lets you decouple what from when. It's very powerful. It just a bit of work.

The idea is, while you might not be able to create the time stamp at the start of construction in main, you can configure what kind of time stamps will be created at the start by passing in an abstract factory that creates that type.

This ensures your handlers have no source code dependencies. Because all they know is the interface they talk to this abstract factory through. They have no idea what they're making. That frees them from caring.

I strongly suspect pure DI is the missing ingredient of your studies. It's not a silver bullet. It's not the only way. It's not the easiest. But it gives you the power you're asking for.

  • 👌👌👌 Awesome! That does simplify things a lot! I would just pass the Event Bus as part of the constructor! This makes everything easier. I'll b researching how to. I appreciate all the help, time and effort put into the explanation! – Jose A Jan 19 at 2:07
  • BTW, from the post above, I'm wondering. Would it be bad to have the aggregate contracted to an Interface so I can use DI to inject it to the Application Layer (In this case, the Command?) – Jose A Jan 19 at 2:09
1

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.

  • Yup. You got that right. I guess this is another one of those muddy water and unclear statements in which people who implement DDD start to have diverging opinions – Jose A Jan 20 at 22:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.