Recently, Chris and I have been working on the next release of MassTransit (MT) and in the process of getting everything all nice and shiny we ran smack into an issue that neither of us have really had the chance to think about before. At what point does an application need more than one IoC container?
The situation is this. In order to make the samples easy in MT, we have built the MT Gui which hosts the subscription service, health service, and timeout service in one .Net process and is displayed via a Win Form which communicates with these via its own bus. Until recently this hadn’t been an issue as they all communicated over the same address. However, that isn’t what we would expect to see in production so we started move each service to its own queue address. One queue per service also means one ServiceBus instance per service, At this point we had four service buses for four services in one .net process space (yikes!). In production these would all run in their own win services, with their own containers. Whereas, this one WinForm app has 4 instances of IServiceBus running around in its container. Well this makes it pretty hard to say give me IServiceBus in the constructor of the various sub-components and get the right instance.
Well crap, what are we going to do? We kinda had it figured out with StructureMap by using its lambda support, but that doesn’t work with windsor at all (its also not a good answer). After discussing it some more we started ask ourselves questions like “Do you really want one container per bus?”, “Should we embed the container in the bus and then just support the one internal one?”, ultimately, we decided that having one container per service bus actually made a lot more sense. The fact that we made it easy on ourselves to cram a bunch of stuff in one service process, didn’t excuse that we really needed to be configuring one container per service.
We came up with the following next actions:
1. Topshelf services need to be able to run in their own appdomain (and thread for that matter). This should help me to not be lazy as I will have to have one container per appdomain. After all, why should Service A be able to grab a dependency from Service B? By drawing a harder line it should make it easier to not screw up unintentionally, of course we will keep the old mode available for you scissor runners. 😉 -done (rev 29) and ready for review
2. Topshelf services need to use the IServiceLocator instead of the ServiceLocator.Current. The use of static ServiceLocator.Current or ObjectFactory type doohickeys are probably not the best idea when you are trying to cram 8,000 completely different services into one hosted process (which is probably not the best idea either).
3. We hope that most people don’t run into this issue. I would think that for most applications you would really only need one (maybe two) instances of IServiceBus, one for the control channel and one for the data channel. To make this a bit easier we now have an interface called IControlBus which you can use if you want your component to talk over the control channel and you would use IServiceBus as you data channel.
Although its been frustrating trying to come to this conclusion, I think that this makes the most sense for this case.