I am working on a .NET core MVC web app.
I have a domain object method that perform some business logic and return a result object containing variables passed to view models. The result object has some variables that are from an event handler if a target event happened.
I am wondering how to access variables from the event handler with DI container like Simple Injector in sophisticated way.
This is a simplified version of snippet of what I am doing.
Class DomainA (has domain logic)
private DomainB _domainBObj;
/**************
* constructor
***************/
public DomainA(){
_domainBObj = new DomainB();
}
/********************************
* called in Application Service
********************************/
public MyEventResult doSth(string arg){
/************************************************************************
* It did the job
* but I am thinking about if there is any approach
* that DomainA can get access to the event result if the event happened
* without getting the event handler here using DI container in C#
*************************************************************************/
var eventHandler = DIContainer.INSTANCE.getInstance<MyEventHandler>();
_domainBObj.doSth(arg);
return doSthBasedOnEventResult(eventHandler.Result);
}
public MyEventResult doSthBasedOnEventResult(EventResult eventResult){
var ret = new MyEventResult();
// setting variables of MyEventResult
return ret;
}
Class DomainB (Wrapper of 3rd party library object)
/**************
* constructor
***************/
public DomainB(){
// configuration of the 3rd party library object
some3rdPartyLibraryObj.OnSomeEvent(
var event = new MyEvent(); // wrapper of some variables
var eventBus = DIContainer.INSTANCE.getInstance<MyEventBus>();
eventBus.Dispatch(event);
)
}
/***************************
* called in DomainA doSth()
***************************/
public void doSth(string arg){
some3rdPartyLibraryObj.doSth(arg);
}
Event Bus (Singleton scoped)
public interface IEventBus
{
void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent;
}
public class MyEventBus : IEventBus
{
public void Dispatch<TEvent>(TEvent @event) where TEvent : IEvent {
if (@event == null) throw new ArgumentNullException("event");
var handlers = DIContainer.INSTANCE.GetAllInstances<IEventHandler<TEvent>>();
foreach (var handler in handlers) {
handler.Handle(@event);
}
}
Ref: Implementing Domain Event Handler pattern in C# with Simple Injector
Event Handler (Request scoped)
public interface IEventHandler
{
void Handle<TEvent>(TEvent @event) where TEvent : IEvent;
}
public class MyEventHandler : IEventHandler
{
public EventResult Result { get; private set; }
public void Handle<TEvent>(TEvent @event) where TEvent : IEvent {
var result = new EventResult();
// setting variables of EventResult
Result = result;
}
}
CodePudding user response:
It's hard to answer your question, because there are a lot parts of your chosen design that are unclear. There also seems some inconsistency in your examples. For instance, you show an implementation of a handler that is using a non-generic IEventHandler
, but at the same time resolve a generic IEventHandler<TEvent>
inside the MyEventBus
.
The only thing I can do at this point is to provide some comments and suggestions. Here goes:
- Event handlers would typically be fire-and-forget operations. Letting event handlers return data can extremely complicate your application, especially because you can have zero to many handlers per event type, while they might wish to return different things. This causes a strong coupling between the consuming code (typically a command handler or Domain Method). It, therefore, seems peculiar to me to have such handler return data. You might want to split this into an event handler that updates the system and a query handler that allows you to retrieve that information. If possible, try to design the system such that to can retrieve that data later on (e.g. in a future (web) request).
- You seem to be applying the Service Locator anti-pattern. Because of its many downsides, you might want to reconsider this. For Domain Methods, Method Injection is usually the most suitable way of getting dependencies into them. In your case, supplying the
IEventBus
to your Domain Methods seems reasonable, because you want to decouple down domain objects from the event handlers. - If you're not already using them, you should favor using generic interfaces such as
IEventHandler<T>
over the a non-genericIEventHandler
(with a genericHandle<T>
method), because this simplifies registration and use of those handlers enormously, because you make maximal use of the .NET type system.