MVVM – a story of pain, misery and finally relief
To be brutally honest, when learning about coding patterns – there were a few patterns that I just found to be … cumbersome or just plain without relevance (thinking they were the remnants of C++ times long gone…). The Model-View ViewModel (MVVM) and Monostate, to name a few seemed like things you’d never use. Monostate… why in the name of all that is good do we need monostate when we have Singleton? Well, I got wiser…now, I tend to never use singletons (aside from IoC-enforced) and always use monostates for my own classes when shared state is needed. Not having to deal with locking-and threading issues, lazy versus eager loading just to name a few of the problems with singletons is well worth the less time spent implementing them (wait, isn’t that actually a good thing when you aren’t paid by the hour…).
But MVVM – for the life of me, I just couldn’t see the benefits – handling the mismatch between the string-typed world of UI and the many-typed world of the domain classes were beautifully handled with shared ValueConverters (thanks, WPF) and nice services. So, I was left thinking – I’ll end up duplicating logic, duplicating all interfaces and all I’ve got to show for it was no implementation of INotifyPropertyChanged in my domain-classes? No way, Jose!
Let’s just say – things went well for about a 8-9 months period – and then pain came creeping. Performance was dwindling faster than you can say *cough*poorly-matched domain-model*cough* and I simply didn’t understand why. Everything was coded both to satisfy a rich end-user experience, caches were used to share common DB-queries and patterns were used to relieve coding smells when they emerged, but still performance kept dropping… And some weird persistence bugs kept re-emerging (“Why is this entity suddenly detached and this one screaming it has a transient one… when they should be one and the same?”).
Then it dawned on me (reading “Nhibernate in action”) – everything keeps ending up dirty whether I’ve touched it or not after retrieving it from the database making for endless useless round-trips when there should have been none. Investigating the matter further, I came to a clue: Every-time an entity had a collection of custom type, it wasn’t dirty when retrieved, but as soon as it was shown in the UI, it became dirty. WTH!? I started investigating in all the wrong places, WPF Binding bugs might be it, doing an extra setter somewhere – maybe the NHibernate Interceptor taking care of audit – and I just kept drawing blanks.
Then (going back to reading NHibernate in Action) it finally dawned on me… collections => dirty when databinding to it… I found code-snippets like this all-over our domain-model (truth be told, I made them myself… They did not appear by magic):
private IList<ISomeClass> _list;
public ISomeClassCollection List
{if(!(_list is ISomeClassCollection))
{_list = new SomeClassCollection(_list);
OnPropetyChanged(“List”);}
return _list as ISomeClassCollection;}
The reason for the ISomeClassCollection was a work-around to better support INotifyCollectionChanged as ISomeClassCollection would act as a wrapper around the original list and notify UI of changes to the collection. Can anyone spot the problem? NHibernate identifies dirty-state in collections by ReferenceEquality on the collection and equals on the content of the collection… So, the first time, UI would ask for the current collection to show it in a ListBox or a DataGrid, it would mark the entity dirty because the retrieved collection was no longer… Collected as common garbage, you might say…
So, back to the drawing-board for that one. Before turning to NHibernate in Action I had just plowed through HeadFirst Patterns and Design Patterns (Amazon them, I couldn’t be bothered to find a link) and I came to the conclusion why MVVM would be a good idea here… Not only would entities stop being marked dirty all the time without cause, but I could support 1 level undo, simply by copying the state of the entity when read, and only setting it when the conversation was over and the user had agreed to the changes reducing the number of round-trips even further.
Later, when implementing it, I also discovered a nice side-effect, I hadn’t considered. The domain-classes are now very compact and it is much easier to enforce how to use the domain. Since collections used for full-fledged databinding needs to both implement IList and INotifyCollectionChanged, the collections in the domain tended to be full of features, that weren’t used (and shouldn’t be used, since they lacked the logic of the proper methods).
So, now collections are usually IEnumerable and the ratio of value-type compared to entity-types (in the sense of DDD’s notion of value-type and entity) has reached an all-time record of 65%! Having a value-type with only getters and a few methods and then letting the ViewModel have the setters and all the handling of the user… it just makes for real simplicity in the domain. Yes, the ViewModel can end up cluttered with all kinds of weird tricks to satisfy the demanding master of UI-updates, but that’s what it’s there for!
I still have a few things to iron out (mainly with duplicate logic to support notifications to the UI without touching the domain entity when services and dependant objects are in play), but just the first round of refactorings has increased my satisfaction with the domain-model ten-fold. It turned from being just wrapping the domain objects to a complete refactoring of the interfaces of the domain and complete re-working of the database-schema, but it is well worth it in my oppinion. For certain it would have been much more critical to do it after it had gone to production (the mind boggles at the amount of data-conversions to be done…).
The lesson? I, Magnus, do hereby solemnly swear, never to let UI-concerns enter my domain model again! I now understand it will be my doom and much pain will be caused… mainly to me. (And I’ll swear again, the next time it happens!)