Subscriptions based on filters at transport infrastructure level

Hi There,

we have a use case where we are debating on how to deal with and still keep using NServiceBus.

We have a publishing azure function that receives data from an external data source. Unfortunately that data source has not been well designed, resulting in using one object type called Asset. The asset has a property called type. We have about 5 different types, in the future this will be extended to more types. Kuch kuch sap

We want to have 5 different eindpoints for receiving 1 asset type.

If not using Nservicebus we could implement this :Azure Service Bus topic filters - Azure Service Bus | Microsoft Learn. We are fine with using headers for example, but its a must that filtering happens at transport infrastructure and not in application code. We want to prevent invocation of functions that will not handle the message. As far as we can see MutateIncomingTransportMessageContext validation is in application code and thus will invoke the azure function to determine if a message could be valid.

Another option is to create different messages with different objects based on the value of the type. But it makes it a bit less flexible in case a new type is introduced at the data source. As we have to update the publisher again with new object type.

So my question is, is it possible to have a solution with NServicebus where we can create subscriptions that filter on a value of a payload property without invoking an azure function, so the subscription must be set on transport level azure service bus?

Hi Menno,

With NServiceBus, you can have a single Azure Function that processes different message types. So you can have message handlers for

  • MessageA
  • MessageB
  • MessageC

But you can also have different message handlers for a single message. So for your Asset message, you can have something like this:

public class AssetTypeA : IHandleMessages<Asset>
{
}

public class AssetTypeB : IHandleMessages<Asset>
{
}

public class AssetTypeC : IHandleMessages<Asset>
{
}

And then inside each message handler, figure out what kind of asset type it is.

If you want to, you can also host 5 different NServiceBus endpoints and define the filtering inside Azure Service Bus yourself. But normally we recommend against these kind of filters, aka content based routing. But I can imagine that if the sending source (kuch kuch SAP) you have to find a workaround.

Does the answer provided above help you at all? You can also contact us via support@particular.net and perhaps set up a conference call to discuss this in more detail, if you’d like.

Hi Dennis,

thank you for your extensive reply and great link to why content based routing is considering a bad practice.

If we use your example the function still gets spun up and than check if its relevant, out goal is to limit spinning up azure functions if they won’t be dealing with the message.

Could you explain the filtering a bit more, even though its considered bad practice? Do you mean its still possible to filter on payload or do you mean we add 5 different subscriptions referring to your example of the IHandleMessages message?

Cheers,

Menno

In the example I gave with multiple message handlers for a single message, you’d host all of those in a single NServiceBus endpoint, which is hosted inside a single Azure Function. It will only be spun up once for each message.

So you’d have something like this

public class AssetTypeA : IHandleMessages<Asset>
{
  public async Task Handle(Asset message, IMessageHandlerContext context)
  {
    if (message.SomeProperty != 1)
      return;
  }
}

public class AssetTypeB : IHandleMessages<Asset>
{
  public async Task Handle(Asset message, IMessageHandlerContext context)
  {
    if (message.SomeProperty != 2)
      return;
  }
}

When a single Asset message is in the queue, the Azure Function will be “spun up” and each Handle method will be called. Each can check if the asset/message is for them and execute one or more steps.

About the filters:

If NServiceBus spins up, it’ll verify if the subscriptions exist. If they don’t, it’ll create them. But if you create them beforehand (or modify them afterwards) you can still create filters on the topics. That way you can still create multiple subscriptions but only forward those messages based on the filter. I’d prefer the other way though with multiple message handles for the same message. And I’m assuming you want to keep costs down or something with not spinning up those Azure Functions? Otherwise I’d have a single message handler for Asset, figure out what you really need and send an appropriate, more detailed message out to the next message handler. So something like this:

public class AssetTypeA : IHandleMessages<Asset>
{
  public async Task Handle(Asset message, IMessageHandlerContext context)
  {
    if (message.SomeProperty == 1)
      await context.SendMessage(new WhateverAsset());
    else if (message.SomeProperty == 2)
      await context.SendMessage(new SomeOtherAsset());
    else if (message.SomeProperty == 3)
      await context.SendMessage(new MostImportantAsset());
  }
}

@MennoLaan, If I understand the scenario correctly, you are using (or willing to do so) NServiceBus in a serverless environment. If that’s the case, then the topology is not automatically deployed, and you can turn off auto-subscribe (if it’s not already off by default in serverless).

With auto-subscribe off, you can create your preferred topology, which could be exactly the one NServiceBus would create, except that for certain subscriptions (the one on Assets), you’ll have additional filters other than the message type.

.m