I'm working on an AspNetCore application, with a requirement to raise and handle certain events asynchronously using an external event queue. I'll use AWS services as examples here because that's what I'm using, but this would apply to any queue infrastructure.
Here is my design for connecting my external event queue. I think this is workable, but I would like some corrections / feedback.
I have the following considerations in mind:
- As mentioned earlier, I'm trying to maintain inversion of control principles.
- Each handler should have it's own SQS queue. This means that if two handlers both handle the same event, that event is put into two queues - one for each handler.
- Receiving the events from a queue and calling the handler should be done on the application layer (I think). I don't think it makes sense for the infrastructure layer to know about the application event handlers.
- I'm not sure this is achievable (see Problem #1 below).
- Dispatching events should be done using a single interface,
IDispatcher
.- Because of AWS's SNS subscription filters, matching an event with the appropriate queue can be done by SNS. A more generic solution might involve moving this logic into the application layer.
Here are the problems I see:
- The infrastructure layer has to know some information about each of the handlers so that
IReceiver<...>
knows which queue to read from. This violates consideration #3, though I think that's unavoidable in this case? - I have a code smell with the way I've set up my
IReceiver<...>
interface, I just don't know how to resolve it:- Currently I have a
THandler
type parameter onIReceiver<TEvent, THandler>
, where the host would do something like...
... this way when I request anservices.AddSingleton(typeof(IReceiver<,>), typeof(SQSReceiver<,>));
IReceiver<...>
in my worker, the instantiatedSQSReceiver
knows which queue to read based on theTHandler
parameter. - The thing is,
THandler
is not actually used by the interface. I'm just expecting thatSQSReceiver
knows to use that type parameter to identify the queue. - In other words, I'm relying on the implementation to implement my interface in a certain way, which feels like a code smell. I just don't know how to resolve it.
- Currently I have a
In summary, my question is:
How do I architect a solution to use an external event queue where each handler has it's own queue, whilst adhering to inversion of control?
I feel like my solution gets me most of the way there, but I'm looking for improvements to address the code smell/s, and any other issues that I haven't considered.
Receiver
? You might need different queue client settings when consuming different messages. And relying on your generic type to determine something (in this case the queue to subscribe to) can be a sign of questionable design.EventA
being raised would need to call both handlers 1 & 2. If all events were on a single queue, whichever handler polls first would get the event, and the other wouldn't see it at all.