6
votes

Most if not all of the NSB examples for ASP.NET (or MVC) have the web application sending a message using Bus.Send and possibly registering for a simple callback, which is essentially how I'm using it in my application.

What I'm wondering is if it's possible and/or makes any sense to handle messages in the same ASP.NET application.

The main reason I'm asking is caching. The process might go something like this:

  1. User initiates a request from the web app.
  2. Web app sends a message to a standalone app server, and logs the change in a local database.
  3. On future page requests from the same user, the web app is aware of the change and lists it in a "pending" status.
  4. A bunch of stuff happens on the back-end and eventually the requests gets approved or rejected. An event is published referencing the original request.
  5. At this point, the web app should start displaying the most recent information.

Now, in a real web app, it's almost a sure thing that this pending request is going to be cached, quite possibly for a long period of time, because otherwise the app has to query the database for pending changes every time the user asks for the current info.

So when the request finally completes on the back-end - which might take a minute or a day - the web app needs, at a minimum, to invalidate this cache entry and do another DB lookup.

Now I realize that this can be managed with SqlDependency objects and so on, but let's assume that they aren't available - perhaps it's not a SQL Server back-end or perhaps the current-info query goes to a web service, whatever. The question is, how does the web app become aware of the change in status?

If it is possible to handle NServiceBus messages in an ASP.NET application, what is the context of the handler? In other words, the IoC container is going to have to inject a bunch of dependencies, but what is their scope? Does this all execute in the context of an HTTP request? Or does everything need to be static/singleton for the message handler?

Is there a better/recommended approach to this type of problem?

2
I don't know about handling events in IIS, but anytime you introduce async processes you'll have to deal with eventual consistency. Does the web app need to update immediately or can you get away with refreshing the cache every minute, 15 minutes, hourly?Ryan
@Ryan: Can we get away with it? Probably. Am I comfortable with it from a customer POV? Not really. I'm fine with EC in general - for example, I realize that nothing will be handled if the site or app pool isn't running, until it starts up again, and that is completely fine - but the information should be up-to-date within a few minutes of some user actually requesting it, and the data really should be cached for much longer. This discrepancy is ordinarily resolved by manual or dependency-based cache invalidation.Aaronaught
Right, and that's what I do in my synchronous apps. For NSB I just hit the DB every time but I just have a back office app with a few users.Ryan
@Ryan: Certainly, for a small-scale app you don't need the caching at all, and that would solve the problem (though I'm sure there are other scenarios in which you might want to handle a message from within ASP.NET). This is a customer-facing app and while we are not Stack Overflow scale, assume at least a few hundred users online at a time - the caching is definitely important.Aaronaught

2 Answers

3
votes

I've wondered the same thing myself - what's an appropriate level of coupling for a web app with the NServiceBus infrastructure? In my domain, I have a similar problem to solve involving the use of SignalR in place of a cache. Like you, I've not found a lot of documentation about this particular pattern. However, I think it's possible to reason through some of the implications of following it, then decide if it makes sense in your environment.

In short, I would say that I believe it is entirely possible to have a web application subscribe to NServiceBus events. I don't think there would be any technical roadblocks, though I have to confess I have not actually tried it - if you have the time, by all means give it a shot. I just get the strong feeling that if one starts needing to do this, then there is probably a better overall design waiting to be discovered. Here's why I think this is so:

  1. A relevant question to ask relates to your cache implementation. If it's a distributed or centralized model (think SQL, MongoDB, Memcached, etc), then the approach that @Adam Fyles suggests sounds like a good idea. You wouldn't need to notify every web application - updating your cache can be done by a single NServiceBus endpoint that's not part of your web application. In other words, every instance of your web application and the "cache-update" endpoint would access the same shared cache. If your cache is in-process however, like Microsoft's Web Cache, then of course you are left with a much trickier problem to solve unless you can lean on Eventual Consistency as was suggested.
  2. If your web app subscribes to a particular NServiceBus event, then it becomes necessary for you to have a unique input queue for each instance of your web app. Since it's best practice to consider scale-out of your web app using a load balancer, that means that you could end up with N queues and at least N subscriptions, which is more to worry about than a constant number of subscriptions. Again, not a technical roadblock, just something that would make me raise an eyebrow.
  3. The David Boike article that was linked raises an interesting point about app pools and how their lifetimes might be uncertain. Also, if you have multiple app pools running simultaneously for the same application on a server (a common scenario), they will all be trying to read from the same message queue, and there's no good way to determine which one will actually handle the message. More of then than not, that will matter. Sending commands, in contrast, does not require an input queue according to this post by Udi Dahan. This is why I think one-way commands sent by web apps are much more commonly seen in practice.
  4. There's a lot to be said for the Single Responsibility Principle here. In general, I would say that if you can delegate the "expertise" of sending and receiving messages to an NServiceBus Host as much as possible, your overall architecture will be cleaner and more manageable. Through experience, I've found that if I treat my web farm as a single entity, i.e. strip away all acknowledgement of individual web server identity, that I tend to have less to worry about. Having each web server be an endpoint on the bus kind of breaks that notion, because now "which server" comes up again in the form of message queues.

Does this help clarify things?

1
votes

An endpoint(NSB) can be created to subscribe to the published event and update the cache. The event shouldn't be published until the actual update is made so you don't get out of sync. The web app would continue to pull data from the cache on the next request, or you can build in some kind of delay.