Some thoughts on WPF and the Model-View-ViewModel pattern

Skrevet - Saturday, May 8th, 2010 kl. 20:33 | Kategori - * Coding, English posts

Having started on the endeavour it is to create an ERP system (this time for real), I’ve put a lot of thought into the structure of the project. Having worked on our solution for transport for nearly 3 years has of course given me a lot of insights into what does not work, but also a lot of things that really works for me. Just to outline what I’ve tried during these three years:

CSLA – it sounded good when we started out – having all logic for persistence, validation etc. defined in base classes sounded too good to be true. It was. As the domain grew, it became impossible to follow what was happening. On top of this, there was a lot of backwards compatibility that was polluting the API. In the end we ended up discarding the idea – and going more light-weight.

Enter our first from-the-ground-up domain model with SubSonic as our ORM. All domain classes were encapsulated, handling all internal logic including UI-logic. Having only two developers, we constantly had problems doing things the right way – having odd work-arounds to get objects behaving correctly (no UI-updating when getting the object from the database was one of the major problems). We hired a guy to show us how it was done…

Enter our current solution: We use NHibernate for persistence, have repositories/factories create objects for us – separated all UI-logic out into ViewModels, started using Presentation models for semi-lazy loading. I’m quite happy with our current structure – although there are some mistakes that need to be refactored out. Some of the domain objects have too much responsibility and are way too heavy when loading.

So, why not just use the current structure for the ERP solution? Well, much of it is going in as-is. There is one major difference, though. The ERP-solution is going to run on WCF-services instead of a direct link to a database. This means that as much as I would like it, domain objects are not going to be loaded directly from the database, but rather from Data Transfer Objects (DTO). What we’re going to do is this:

For a common scenario, we need to list Orders and then be able to pick one and edit it:
1. We send a paged query for Orders satisfying our conditions to the WCF-service.
2. We show the DTO’s directly – all DTO’s are light-weight and only represent the data of the current entity (no journal, no vat-account etc. – we only have the Ids to find it later)
3. The user selects one order for editing – we use a factory (we call it entity-creator as we have a factory already) to convert the DTO into a full-fledged IOrder (the domain object with all the logic). During this, everything needed will be loaded – the Partner that should recieve the order, VAT-codes, Currency and so on.
4. We wrap the IOrder into a ViewModel that will give us a rich editing experience with nice combo-boxes, nice graphics and on-the-fly updates with totals and so on.
5. The user now makes his edits – the order is continually validated (it is responsible for this).
6. The valid IOrder is sent to the repository for saving – here it is converted into a DTO again and sent to the WCF service which again converts it into an IOrder and checks everything is still valid and is saved to the DB.
7. The list of Orders is updated to reflect the changes.

This may seem like a lot of steps for something this simple. We reached this model after a number of discussions and try-outs. This solution will offer some benefits:
* It is extremely performant – we only load enough data to enable the user to make decisions.
* It is extremely flexible – we can use scalar properties on the DTO that does not exist in the domain – we can even use views to populate it handing the calculation to the database server.
* We enable a rich UI when editing as the ViewModels will be very responsive and can show consequences before actually committing it.
* All functionality when it comes to business logic is portable – everyone can make use of the API as it exists far below the UI-components.
* Designed for testing. All components are very small and encapsulated. SRP was one of the major priorities when coming up with this model.

There is a few drawbacks, though:
* Complexity. It goes without saying that developers will need to keep a clear head as there will be up to 25 classes into play for an average Order view to work.
* The ViewModels will have a tendency to grow a lot.

ViewModels… well, it is my pain-point. All the ‘junk’-code seems to end up here… It is far nicer than bloaty code-behinds for sure, but it seems to me that some of the UI logic tends to be a lot of boxing and unboxing – scores of If’s and maybe’s. And of course, the master of spaghetti-code: drag and drop. I will try something ‘new’ here (as in: new for me). I’ll try to split the responsibilities into a few groups and dedicate a class to each.

First candidate is Drag and Drop – here the problem is: What to put in code-behind and what to put into DragAndDropHelperClassYetToBeNamed (DAD). I’ve learned that attaching event-listeners directly to the view is a bad idea. Instead the DAD will throw Actions, Functions and Delegates at the view, and let the view decide when to use which. This will allow me to re-use functionality for different events (like when double-clicking and left-clicking-while-doing-the-hokey-pokey should do the same) – one drawback is that the view will need to know enough of the domain, to do simple deciding.

Second candidate is ICommands – many commands need to be re-used anyway, so they might as well reside in reusable classes. Almost all commands will be relayed commands with Predicates and Actions. Almost all ViewMdodels will have a Commands-property where functionality available for key-presses, menus and buttons will reside.

ValueConverters are already not in the ViewModel as they need to be reused – and thus reside in ResourceDictionaries. This leaves showing data, doing UI-updates and delegating requests to the domain/repositories. This will have to do for now… Now to actually implement it… Don’t get me wrong, I think ViewModels are a fantastic idea – but they can become unwieldy if attention is not kept.

Feed | Trackback |

Post a Comment