Thursday 7 April 2011

MVVM Revisited

I have been working with MVVM and Prism for a couple of years now and have had some recent revelations which you may find useful.  Working with MVVM by itself, is relatively straightforward.   Combine it with other patterns, such as MVC and IoC, and things can get complicated.

Here is a common implementation of MVVM:

This example also utilises a CustomerController along with some services, all of which are supplied by the “Model”.  If you’ve worked with Prism you’ll probably recognise this rather unorthodox implementation of MVC, something I’ve never been comfortable with.  Injecting a controller into a ViewModel seems backasswards to me.  I would prefer to have the Controller be aware of the ViewModel and indirectly control/manage the Views.

Aside from being awkward, this approach does work.  But don’t expect your modules to be “modular”.  Sure, within your solution it will have the appearance of being a module, but it won’t very portable.  If you’re persistent and are dedicated, you can try to “reuse” the Customer module above, but be prepared to drag a half dozen dependent assemblies with it.

So, what is modularity without portability?  Without portability, modularity is reduced to a code organisation concept.  

Looking over this situation it occurred to me that the injection of dependencies in the ViewModel was pretty much unrestrained.  If the ViewModel needed it, just add another parameter to the constructor.  The entire Model was wide-open for consumption, provided it was registered with Unity.  Needless to say, this leads to greater and greater consumption, until finally the ViewModel is entrenched in implementation.

When viewed as a microcosm, MVVM can act like a tiered architecture.  And tiered architectures are great for scalable server applications, but pretty much ruin any hopes of portability.  

The other undesirable aspect of this is, while the ViewModel may require the ability to display a MessageBox (provided by the IMessageService), it has no use for the other 10+ methods provided by the service.  So, why not give it exactly what it needs – and no more.

One way to accomplish this through the introduction of a custom Model class:

Here the CustomerModel class defines exactly what the ViewModel demands of the Model.  In effect, it is a subset of the Model and is specific to that ViewModel.   It’s the ViewModel’s shopping list – “if you’re going to use me, this is my list of demands”.  It is now up to the implementer to provide this functionality – which is where the responsibility belongs.

With this approach the ViewModel has now been liberated and all of the “dependencies” removed.  Service methods have been replaced with delegates.  The ICustomerService can reside in the module itself, effectively making the CustomerModule a self-contained, standalone assembly.

A side benefit is that the View now contains no code-behind and is no longer dependent on the ViewModel interface.

While working through this I’ve created a new Prism sample solution, which includes a CustomerModule and an OrdersModule, Implementation project and a Shell.  I have reduced the module dependency down to a single Core assembly.  The Implementation project contains controllers, core services, a persistence manager, etc.  It knows about the modules and can orchestrate the workflow and manage the views.  The controllers “wire up” the Model and inject them into the ViewModels.   

At the moment, CustomerModule + Core is about as portable as it gets.

6 comments:

Rimu said...

Hi Ron,

I'm sitting here remembering that day in the office, watching the twin towers burn. I'll never forget that feeling.

I hope things are going well for you, wherever you are :)

Rimu

rgramann said...

Hi Rimu, good to hear from you. Yes, difficult to get rid of that picture. You should send me an email and bring me up-to-date.

Cheers

Ron

Anonymous said...

Hey Ron,

This is Yaz, Good idea.Passing an IView to the ViewModel. Now your ViewMdel is aware of the View. This is more like the MVP pattern, where you pass an IView to the Presenter. A lot people would say that the ViewModel should not contain UIElement. Once these are introduced in the ViewModel, the ViewModel becomes difficult to test.

I never forget the bacon sandwiches we used to have.

Cheers
Yaz

rgramann said...

Hi Yaz, it's been a while. Hope you are doing well.

I agree about the ViewModel being unaware of UIElement. I've been working with a minimalistic IView with a single object DataContext property. No need for anything else really.
One thing I like about this approach is that the view no longer has a dependency on the ViewModel, and is treated more like a resource that is consumed by the ViewModel.
Also, I've noticed a "standard" pattern and structure that naturally comes about in the ViewModel. No business logic, just UI logic. The heavy-lifting is done on the implementation side (in my case, a Controller).
Of course, reducing the module dependencies to a single common DLL is the real bonus.

Send me an email and let me know what you've been up to.

Cheers

Ron

Miles Austen said...

Ron

This supports BDD nicely. Using tell-don't-ask as espoused in ‘Growing Object-Oriented Software, Guided by Tests’, as the behaviour is broken down to sub-behaviours in the VeiwModel, the VeiwModel then 'tells' what it wants precisely. It is up to the injected CustomerModel to provide what it’s told to.

This means that you can follow BDD - breaking down behaviours into sub-behaviours and staying in the genre of the problem domain (Behaviour) as you are TDDing (agh! did I just say TDDing).

Miles

rgramann said...

Hey Miles,

I think there are advantages to approaching MVVM in this manner...but also drawbacks. On a recent project I applied this and found that there was a considerable amount of additional code.
I am convinced the tell-don't-ask principle is sound, it's working out the implementation that needs a bit more thought.
If what you're after is low-dependency, portable modules I think MEF may prove to be a better technology. I'm currently working on a prototype using MEF and initial impressions look good. Once I get the MEF-MVVM patterns sorted out I'll post another article.