This is my second project in which I used an Interop layer for calling custom code in BizTalk and it still feels (and smells) good.
The Interop layer is an assembly that isolates the BizTalk specifics from the rest of your code base (I’ve not needed more than a single assembly but I can imagine you might want to split things up in multiple assemblies with bigger and more diverse projects). The BizTalk specific types are only used in this assembly and requirements like [Serializable] are also implemented here (if not supported by underlying code base). Note that I do not use the Interop layer for Pipeline Components: each pipeline component is an ‘interop layer’ on its own – so I do take care not to leak any BizTalk specifics into the general code base.
Some examples of the types of classes I found myself writing in the Interop assembly are:
Message Factory classes
Easily construct new message instances using BizTalk (XLANG) specific types.
Content Enrichment classes
Classes that fetch additional information and return it as Xml (string) for use in maps. See also my post on How to Enrich a Message.
A serializable class that contains configuration settings in an orchestration; when you want to keep the settings an orchestration works with during its lifetime constant.
Collection / Iterator classes
Classes that represent a collection of (domain) information and can be used inside a Loop shape (MoveNext/Current).
Xml Aggregator class
A serializable class that knows how to generically aggregate multiple Xml documents into a single Xml document (of the same schema).
The fact that the Interop layer shields the rest of you custom code base from BizTalk specifics makes your (non interop/other) code Unit Testable without having to resort to ‘creatative’ injection techniques ;-).
I hope it works just as well for you as it has for me.