Endpoint Best Practices and Community Recommendations

Howdy everyone. We’re currently evaluating NServiceBus using several PoCs in our organisation to see how it fits in for our use on evolving an existing legacy system. As a bunch we’re pretty familiar with messaging systems and have done extensive work on large applications designed with that in mind. We’re pretty happy with our results so far and have to say that from just a year maybe two ago, your documentation and samples have really come a long way. However we do have a few questions about very specific recommendations, generally about IEndpointInstance use and management that we figured we’d reach out and see if we can get some guidance about. These generally cover an area I don’t think is well spelled out in the docs and samples and we’ve not been so far able to find anything current on to say one way or another therefore we come before the synod :wink:

For completeness we’re using

NServiceBus 6.0.0
Sql Transport 3.0.0
Castle integration 6.0.0
Callbacks 2.0.1
NHibernate integration 7.1.1

In additional everything is in the same database and it’s pretty plain deployment. In addition we’re not running installers (we precreate queue tables) nor do we handle (for now) subscriptions dynamically but have a management process to enter the configuration; It’s rather straight forward after all.

  1. Say we have a client application that is used to send a particular set of commands (specifics don’t really matter here). These commands are handled on another application and what they do isn’t, again, particularly important for this conversation. Both commands would be using an identical configuration when it comes to conventions, transport, persistence, etc.

So for simplicity here we have a Comand Foo and Command Bar that do really interesting stuff :wink: They are both, logically SendOnly endpoints. We drop a message on the bus and they go off and life is good. So the question here is about the endpoint use on the client app:

Is it a best practice to have a two separate IEndpointInstance basically differing only by name and both otherwise identically configured or is it a better practice to have have one and simply use the differing destination values to send ultimately to the correct location?

Yes we lose some fidelity on the headers but honestly the originating endpoint isn’t really important for us (It may be of importance to the infrastructure or other places where things go sideways when it’s not clear and we’ve just not proverbially ‘stepped in it yet’). I mean, we care it’s from Server X and Application Y, but don’t care to know if the endpoint was sending Foo or Bar commands (i mean the destination tells us that)

In short, should we have a single IEndpointInstance per destination or can we share a single running instance, provided both logical channels are configured the same?

  1. Let’s do the same scenario now but switch this to a duplex exchange here and best practices for response queues. In our case we’re using at a minimum host and application as an identifying value in this POC. It’s pretty obvious the response instance needs a discriminator so we can get it to the proper place and no big surprise here. The question is then if Foo Command has a FooResult response and Bar Command has a BarResult, can we actually share the same response queue and have Callbacks actually do the correct thing here and even if so, is that even a good idea?

Looking at the exchanges it would appear that everything’s tied together via correlation id and the response queue would just be peeked for the matching value to the request, ignoring the rest. Do they bump heads together or is that a case where they’re fine on the same queue?

note I am not saying use one response queue but two running endpoint instances, we know that ends up with them potentially ditching the response because they don’t think it’s current. One instance handing a dialog for two commands over the same response queue.

TL;DR; Yes, have one IEndpointInstance. Also have one instance and one incoming queue for callbacks. Both questions answered. :slight_smile:

I would do something like this

var endpointConfiguration = new EndpointConfiguration("MyClient");
var transport = endpointConfiguration.UseTransport<SqlServerTransport>();
transport.ConnectionString("connectionString");

var routing = transport.Routing();
routing.RouteToEndpoint(typeof(Foo), "Sales");
routing.RouteToEndpoint(typeof(Bar), "Finance");

endpointConfiguration.SendOnly();

var endpointInstance = Endpoint.Start(endpointConfiguration).GetAwaiter().GetResult();
var messageSession = endpointInstance as IMessageSession;

var fooMessage = new Foo();
var barMessage = new Bar();

messageSession.Send(fooMessage);
messageSession.Send(barMessage);

I added the conversion to IMessageSession because on SendOnly endpoints that interface includes less methods and is cleaner, less likely to make mistakes. Read more about it here.

Does the above make sense? The routing configuration makes sure the message go to the correct endpoint and the Send(message) method just sends them. Only during configuration you specify what goes where, but later during sending of messages you don’t care. Just send them and NServiceBus takes care of the rest.

Question 2

TL;DR; I wouldn’t use callbacks/duplex messaging at all.

You can use a single instance, they won’t clash.

Why are you using callbacks on a new project? Isn’t there any other way?
For example, check out our showcase sample, where we use SignalR to inform the browser about updates to data. It’s 100% transparent to the end user, but you’re not waiting for a response from an endpoint that might never arrive. Or arrive with the thread already being cancelled, recycled, etc, etc.

Please note the read alert bar on the callbacks page : Client-side callbacks • Callbacks • Particular Docs

Let me know if you have additional questions based on my answers…

Yup absolutely makes sense. Everything’s working as mentioned above but we wanted to make sure if we were inadvertently going to run into issues down the line. One of those, “yeah guy you shouldn’t do that-no wonder you’re having [insert issue] you putz!” situations.

Nice tip on leveraging the routing instead of using the Send overload with the destination supplied. Far nicer even though it is hidden away in some config of ours, that’s even more elegant. Cheers

In answer to your question about calls backs: Deal is that our scope of the project isn’t to spend too many cycles on rebuilding the end user apps to operate in a different methodology, even if in a greenfield application we’d go with a general approach as you outline. It’s not a new app; just a poc on one scenario of the greater problem set that we can use to make the value proposition moving forward.

Therefore we’re stuck relatively in a classic MVC => Service Boundary => Response story for this due to the way the application controllers have been designed, thus the duplex. Yes they don’t survive restart but neither would the existing implementation either so not really an issue in this particular case. The goal of the exercise is to extract a problem feature set which has bad coupling due to the lack of a bounded context that should have in place in the start so … leezrom =/

Several reasons

  1. It’s less cumbersome and you can specify an entire assembly to route message to somewhere else. What we usually do is create at least 1 assembly per endpoint with messages that can be shared. So if your endpoint is Finance we also have Finance.Messages full of commands and events. Those can then be used by Shipping for example, where we configure once that all those commands need to go to Finance.
  2. Once something changes, you only need to check configuration in 1 location, instead of changing code all over the palce.
  3. Developers are less likely to make mistakes. It’s easy to think one message should go to A, whereas it should go to B. Or perhaps a copy-n-paste mistake.

But glad you liked it :wink:

That’s EXACTLY what we build the Callbacks feature for. I made the assumption somewhere that this was greenfield. Than you’re perfectly fine!

Well done!