Paul, whom some of you may know as the maintainer of Horn project, left a comment on my blog, that was (or to be more precise – I think it was) a continuation of series of his tweets about his dissatisfaction with the state of affairs when it comes to dependencies between various OSS projects in .NET space, and within Castle Project in particular.
I must say I understand Paul, and he’s got some valid points there, so let’s see what can be done about it.
One of the goals of Castle Project from the very beginning has been modularity of its elements. As castle main page says:
Offering a set of tools (working together or independently) and integration with others open source projects, Castle helps you get more done with less code and in less time.
How do you achieve modularity. Say you have two projects, Foo and Bar that you want to integrate. You could just reference one from the other.
This however means that whenever you use Foo, you have to drag Bar with you. For example, whenever you want to use MonoRail, you’d need to drag ActiveRecord with it, along with entire set of its dependencies, and their dependencies, etc.
Instead you employ Dependency Inversion (do not confuse with Dependency Injection). You make your components depend on abstractions, not the implementation. This however means, that in .NET assembly model, you need to introduce third assembly to keep the abstractions in.
Now we have 3 assemblies instead of 2 to integrate two projects. Within Castle itself common abstractions are being kept in Castle.Core.dll. But what if we want to take more direct advantage of one project in another project still maintaining the decoupled structure? We need to extract the functionality bridging the two projects to yet another assembly. Tick – now we have 4 of them.
In this case the FooBar project would be something like ActiveRecord integration facility, which integrates ActiveRecord with Windsor.
When you mix multiple projects together you enter another problem – versioning.
Say you want to integrate few projects together, some of which are interdependent (via bridging assemblies, not shown here for brevity)
Now, once a new version of one of the projects is released, you either have to wait for all the other projects to update their dependency to the latest version, do it yourself (possibly with some help from Horn), or stick to the old version. The situation gets even more complicated when there were some breaking changes introduced, in which case plain recompilation will not do – some actual code would need to be written to compensate for that.
These are the main issues with this model, let’s now look at possible solutions.
First thing that comes to mind – if having some assemblies means you’ll need even more assemblies, perhaps you should try to minimize that number? This has already come to our minds. With last wave of releases we performed some integration of projects. EmailSender got integrated into Core, one less assembly. Logging adapters for log4net and nlog were merged into core project, which means they still are separate assemblies (as they bridge Castle with 3rd party projects) but they’re now synced with Core and are released with it, which means this is one less element in your versioning matrix for you to worry about. Similar thing happened with Logging Facility, which now is versioned and released with Windsor itself.
For the next major version, there are suggestions to take this one step further. Merge DynamicProxy with DictionaryAdapter and (parts of) Core into a single assembly; Merge Windsor and MicroKernel (and other parts of Core) into an other assembly. With that you get from 5 assemblies to 2.
That reduces Castle’s internal dependencies, but what about other projects that depend on it? After the recent release, we started a log of breaking changes, along with brief explanation and suggested upgrade paths, to make it easier for applications and frameworks to upgrade. We have yet to see how this plays out.
What else can be done?
This is the actual question to you? What do you think can be done, for Castle specifically, but more broadly – for entire .NET OSS ecosystems to make problems Paul mentioned go away, or at least make them easier to sort out?