This project has moved and is read-only. For the latest updates, please go here.

What happend with MEF?

Aug 7, 2013 at 4:39 PM
Hello
i've been following this project long time ago, and after checking your new version 3 code, i found some lost of Modularity on the project....
What about MEF have you abandoned for a new friend called "Javascript classes and loaders"?

Thanks
Aug 9, 2013 at 9:51 AM
I realize this is a longer answer than you'd probably expect, but it falls in nicely with an entry I was working on for the TD3 dev diary... so here you go :)


I'll be honest, I haven't decided on a dependency architecture for the back-end yet.

The front-end is using require.js for module management, which follows the Asynchronous Module Definition (AMD) specification. This gives the front-end good composability and modularity, though it doesn’t make for a 1-to-1 architectural comparison to MEF or server-side IoC frameworks. The Front-end modularity is intended to support swapping blocks of code in and out of scope as needed, and downloading modules asynchronously only when the client needs them.

The back-end will need simple dependency resolution and object composition --an IoC container. It will likely end up using Ninject instead of MEF this time around. I'm waiting until I get to a solid case where dependency injection is absolutely required before I decide for sure. I expect to run into that with security very soon.

So no, I'm not backing away from modularity in general, but I am backing away from some of the unnecessary modularity that was prevalent in TicketDesk 2's design.

TicketDesk 2 - Decoupled design:

The use of MEF in TicketDesk 2 was not about modularity, at least not in a way that is relevant to business requirements. TicketDesk 2 was never intended to support plug-ins or dynamic external module loading. I used MEF for two reasons; I was giving test driven development (TDD) another shot, and I had planned to write a Silverlight client for TicketDesk 2.

MEF was originally built by the Silverlight team. It had a lot of potential for other environments, but didn't play well with MVC back then. It took some dark magic hacking to just make it work there. MEF is an extensibility framework first, but an IoC container only by accident. While MEF can do the job of an IoC container, it wasn't particularly good in that role.

As an extensibility framework, MEF actually has more in common with require.js than traditional server-side IoC frameworks. It was a Silverlight technology, and the primary purpose was to enable the client to download executable modules from the server on demand as needed. This is exactly what require.js does for JavaScript in HTML applications. The truly interesting thing is that TicketDesk 2 did not use MEF in this way. Asp.Net MVC is a server-side environment with a request-response-done execution flow. Deferred module loading isn't very relevant in that environment, so TicketDesk used MEF only for its secondary IoC features -- runtime composition and dependency injection.

Considering the difficulty in getting MEF working, and the fact that there are better IoC frameworks for MVC environments, I should have scrapped MEF in favor of Ninject --which has made me happy in dozens of projects. I stuck with MEF partly because it would pay off when I got to the Silverlight client, and partly because I liked the challenge.

Sadly, I was only three weeks into development of the Silverlight client when Microsoft released Silverlight's obituary. I had two other projects under development with Silverlight too, so that was a very bad summer.

The modular design of TicketDesk's business layer is mostly about testability. EF 4 was quite hostile to unit testing, so I did what everyone else was doing... I wrapped the business logic in unit-of-work and repository patterns, and made sure the dependencies targeted abstract classes and interfaces. If you want to get all gang-of-four about it, the service classes in TD2 are more transaction script than unit-of-work, but it gets the same job done either way. This gave me the level of testability I needed to follow a (mostly) TDD workflow.

One thing I have never liked about heavy unit testing, and TDD in particular, is implementing complex architectures purely for the sake of making the code testable. I'll make some design concessions for testability, but I have a very low tolerance for design acrobatics that have nothing to do with the application's real business requirements.

TicketDesk 2 walks all over that line. I dislike that there are a dozen or more interfaces that would only ever have one (real) concrete implementation. I also dislike having attributes scattered all over the code just to describe things to the IoC container. Neither of those things make TicketDesk track issues better. It just makes the code more complex, harder to understand, and harder to maintain.

On the flip-side, I was able to get decent testability without going too far towards an extreme architecture. The unit tests did add value, especially early in the development process --They caught a few bugs, helped validate the design, and gave me some extra confidence.

If you noticed that the current source lacks unit tests, bonus points to you! My TDD experiment was never added to the public repository. I was pretty new to TDD, and my tests were amateurish (to be polite). They worked pretty well, and let me experience real TDD, but I didn't feel that the tests themselves made a good public example of TDD in action.

TicketDesk 3 - Modularity where it matters:

A lot has changed for the better since I worked on TicketDesk 2.

Some developers still write their biz code in a custom unit-of-work and repository layer that abstracts away all the entity framework stuff; which is fine. But when EF code-first introduced the DbContext, it became much friendlier towards unit testing. The DbContext itself follows a unit-of work pattern, while its DbSets are a generic repository pattern. You don't necessarily need to wrap an additional layer of custom repository and unit-of-work on top of EF just to do unit testing anymore.

I plan to move most of the business logic directly into the code-first (POCO) model classes. Extension methods allow me to add functionality to any DbSet<T> without having to write a custom implementation of the IDbSet interface for each one. And the unit-of-work nature of the DbContext itself allows me to put cross cutting business logic in the context itself. Basically, it will be something closer to a true domain model pattern.

As for dependency injection, the need to target only interfaces and abstract types has been reduced. An instance of the real DbContext type can be stubbed, shimmed, or otherwise mocked. In theory, I should be able to target stubbed/shimmed instances of the concrete types. If I do find the need to target abstracts though, I can refactor the DbSets and/or the DbContext itself to inherit custom interfaces anyway. There should still be no compelling need to wrap business logic in higher layers of abstraction.

In TicketDesk 3, I will not be using a TDD workflow. I love unit testing, but am traditionally very selective about what code I choose to test. I write tests for code that will significantly benefit from the tests --complex and tricky code. I don't try to test everything. Using TDD as a design tool is a neat process, but I find that design-by-test run counter to my personal style of design. I can easily see how TDD really helps people with design, but I personally achieve better designs by coding first and testing later.

When I do get to dependency injection, I plan to run an experimental branch in TicketDesk 3 to explore MEF 2 a bit further. I think they have fixed the major issues that made MEF 1 hard to use in web environments, but it is almost impossible to find good information online about MEF 2. The documentation, when you can find it, is outdated, contradictory, and just plan confusing. What little I find suggests that MEF 2 works with MVC 4, but still requires some custom infrastructure.

With the need for dependency injection reduced, no compelling extensibility requirements on the back-end, and no plans to do heavy testing, I am more inclined to go with Ninject. They care enough to write top-notch documentation for it, and it was designed explicitly for the purpose of being an IoC container... which is the feature set that TicketDesk actually needs.