Wrapping NServiceBus.IHandleMessages
Asked Answered
U

1

7

I am being asked to develop a layer that will act as a generic bus without any direct references to NServiceBus. Which so far thanks to support for unobtrusive messages isn't too hard. Except now I've been asked to provide our own definition for IHandleMessages and find a way to map it during wireup. So I'm thinking something like this:

    public class MessageHandlerAdapter<T> : IHandleMessages<T>
{
    IUnityContainer container;

    public MessageHandlerAdapter(IUnityContainer container)
    {
        this.container = container;
    }

    #region IMessageHandler<T> Members

    public void Handle(T message)
    {
        var handler = container.Resolve<IHandle<T>>();
        handler.Handle(message);
    }

    #endregion
}

Where IHandle would be our own definition (which by the way is exactly the same as IHandleMessages). I would expect to reflect over the AppDomain and find all classes that implemented IHandle and Register them with the container, then register a MessageHandlerAdapter with the same type T.

My problem is I haven't used NServiceBus for almost 2 years and I don't remember where to hook into this kind of functionality in the NSB pipeline.

Untangle answered 17/1, 2013 at 21:27 Comment(1)
Mark if you want to discuss this more offline my contact details are in my profile.Bribe
B
42

You probably wont like this answer but... Don't write abstraction layers for tools you use.

I have seen many instances where people attempt to write an abstraction layer around certain tools. Mostly the cases are logging and ORM frameworks. Now people have good intentions when they do this. They want to "be able to switch library X easily". Unfortunately this is a bad idea for several reasons

  • Incompatible concepts. In your example you might extract out the concept of a NSB Saga timeout. However there is no guarantee that this concept will be behave the same way in the theoretical "library to switch to in the future", or that the concept will exist at all. Usually these "abstraction layers" end up being a direct map of single library and are not portable at all.
  • Added complexity. You will add a large amount of complexity to your solution.
  • Samples wont work. when you look at samples of the library you will need to "map in your " over to your abstraction layer
  • Barrier to entry. While new developers joining your team may have used the library in question they will have no understanding of your wrapper
  • Fighting against the API. No library is ever designed with the feature "be able to extract a common implementation" in mind. Because of this the API will actively fight you performing this activity.
  • Debugging. An extra layer will make it harder to debug your solution
  • Performance. In general more code is slower code. Also often these abstraction layers need to use reflection...
  • Support. You will make it harder to get support from the people who own the library because it will be difficult to document how you interact with the library.
  • On-going changes. Every time the library in question adds or changes to API you will have to add mapping code before you can leverage that functionality in you solution.
  • Documentation. Often huge amounts of person hours has gone into creating documentation for the libraries. It will be a significant effort, on your part, getting the documentation for your abstraction up to that level.

It all comes down to time. You are attempting to spend time now abstracting the tool. With the hope of saving a larger amount of time in the future. The problem is you will spend much more time creating and maintaining this abstraction than you will ever save IF you decide to switch. This should be your response to your coworkers.

Here is an interesting post from Ayende talking about the evils of abstraction. Much of it is applicable to this scenario http://ayende.com/blog/4784/architecting-in-the-pit-of-doom-the-evils-of-the-repository-abstraction-layer To quote

...try to avoid needless complexity... Adding additional layers of abstractions usually only make it hard.

Bribe answered 17/1, 2013 at 22:52 Comment(9)
I would add that there is a strong chance that your abstraction will end up getting leaky too as a desire to surface the unique parts of the API are added to the wrapper.Forzando
@Bribe - you're right and I've read that post by Oren before. I read it again just as a refresher. My hope was to find an easy way to allow for a simple abstraction to appease my colleagues. While my curious nature would still like to know how this could be done, I will try and have another discussion with my co-workers about this.Untangle
What about changing frameworks or tools. Are you saying that's just not a valid concern? If the abstraction satisfies my requirements and isn't simply a wrapper over the tool why would I want the dependency on the tool in my code?Denis
@Denis well it is all about time. You are attempting to spend time now abstracting the tool. With the hope of saving a larger amount of time if you decide to switch in the future. The problem is you will spend much more time creating and maintaining this abstraction than you will ever save IF you decide to switch.Bribe
Just for completeness: We ended up creating a wrapper around our implementation. The wrapper implements the NServiceBus interface and our class is injected via the container. This was over my objections and so I am marking this as my answer. @Denis - here's the thing about abstracting the dependency - are you testing your abstraction against other possible dependencies? If not, then you don't know your abstraction works and most likely it doesn't. Unless you're doing that, your abstraction is no more effective than russian roulette.Untangle
Hmm... as always it depends why you need/use abstractions. If you look at NServiceBus, it uses abstractions (for their transport layer for example). And guess what, it works just fine. Thanks to this abstraction layer it was possible for the team to add support for Windows Azure Queues and Windows Azure Service Bus Queues instead of MSMQ.Stavro
Don't write abstraction layers - What nonsense. What do you think the makers of Unity3D did? WCF by itself does a brilliant job of abstracting arguably incompatible concepts such as queued and non-queued messages; streamed and non-streamed data all with a single API. If it were not for WCF we would have one API for remoting and another for TCP/IP. So abstractions work in my book regardless of the level of detail.Competency
MickyD, I don't think he is saying "Never abstract things" I think the point is that if a framework or library already supports the abstractions you need and is testable it should be good to go. It's YAGNI to just start adding wrappers around every piece you want to use.Roselleroselyn
@MickyD yeah perhaps the advice could be softer. but i think it accurately reflects the safest stance to start fromBribe

© 2022 - 2024 — McMap. All rights reserved.