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. Diagram of external event queue with IoC

I have the following considerations in mind:

  1. As mentioned earlier, I'm trying to maintain inversion of control principles.
  2. 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.
  3. 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).
  4. 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:

  1. 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?
  2. 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 on IReceiver<TEvent, THandler>, where the host would do something like...
      services.AddSingleton(typeof(IReceiver<,>), typeof(SQSReceiver<,>));
      ... this way when I request an IReceiver<...> in my worker, the instantiated SQSReceiver knows which queue to read based on the THandler parameter.
    • The thing is, THandler is not actually used by the interface. I'm just expecting that SQSReceiver 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.

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.

  • Not sure about question 1. Knowing the name of a queue (or exchange) is a different thing than knowing "information about the handlers", right? Upstream vs downstream. The former is unavoidable for the infrastructure layer. The latter can be abstracted out if needed: the infrastructure knows the shape of what to call upon message reception but not its actual implementation. Commented Jun 26, 2023 at 8:05
  • Also, isn't it overengineering to want a generic 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. Commented Jun 26, 2023 at 8:35
  • Why not put all the different types of messages into a single queue instead? This way any application only ever needs need a single handler (each payload as a raw sequence of bytes that the application can handle however it prefers). SQS is agnostic to the payload of a message, and allows you to include your own custom metadata so that the application's handler can just receive each payload as a raw sequence of bytes and always have enough information from the metadata to decide what to do with it. Commented Jun 27, 2023 at 7:49
  • @guillaume31 I think you're right about the generic receiver. If I made a receiver for each queue then I wouldn't need to use a generic type for that purpose - which is one of the key parts of my question. Thanks!
    – brads3290
    Commented Jun 27, 2023 at 21:09
  • @BenCottrell I thought about that. I would prefer a separate queue because as far as I know, SQS has no built-in way to make sure an event is processed by each handler that wants it. For example in my question, An 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.
    – brads3290
    Commented Jun 27, 2023 at 21:14


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.