31 May 2016

Re Simplifying Message Handlers

In a previous post, Simplifying Message Handlers, I put forth a way to remove unnecessary interface declarations for message handlers (commonly seen in CQRS examples). I provided the research I used and left the implementation as an exercise to the reader. Today I'm going to revisit that topic. I'll explain my experiences with this process which led me to believe it was a bad idea. I'll also explain my current method of tying messages to code.

So wiring up with reflection and marker interfaces is very clever. (note: clever is a developer curse word.) So clever in fact, that it remained a mystery to my co-worker even after I explained it multiple times. She would always ask a perfectly reasonable question like: "Ok, but what causes this code to run?" Then I would show her the API call, which consults the handler collection to figure out who needs to get the message. That would lead to showing the bootstrap code, which called the reflection code to find all the handlers. By then her eyes were glaze over, and she would just label it "magic" and move on. The reflection code was just this side of inscrutable... relying on a small amount of arcane knowledge of .NET CLR internals. So it was hard to understand without actually debugging it or already knowing how reflection works.

The end result was that even after multiple explanations, she was afraid to touch anything in the project for fear of using the wrong incantation (marker interface and method declaration in this case). Needless to say, this presented some challenges. It became so that all items dealing with X were mine and Y were hers... exactly what you are supposed to combat on an Agile team. This is not even to mention the tooling issues, and production issues. In particular, we discovered the hard way that when an IIS application pool wakes from idle sleep it only loads assemblies which are directly referenced. This is different from its startup behavior where it loads all deployed assemblies into the app domain. So we would deploy and everything would work fine, then when we all go home and India starts work, the app would wake from sleep and crash with a TypeLoadException. As for tooling issues, I had to become blind to "this code is not referenced anywhere" type of warnings and get used to not being able to Go To Definition in certain cases.

So what do I do now? The most boring thing possible... I manually wire things up. It's crystal clear and leaves a reference trail that is easily discoverable by other developers and tooling. Once you settle on that, the remaining issue is optimizing for manual wiring. It is ideal to have only one place where you wire your implementations to the handling infrastructure.

No comments: