Umbraco Mapper redux and reviewed
It’s been over two years now since at Zone we open-sourced our Umbraco add-on package Umbraco Mapper, intended to simplify the production of Umbraco websites using MVC best practices. Influenced by AutoMapper, which we regularly use for custom ASP.Net applications to map from domain to view models, Umbraco Mapper does the same job for mapping from Umbraco content. The result of which is a strongly typed model that can be used in the views, providing benefits such as Intellisense, compile time checking and a simpler API for front-end developers to work with.
We certainly weren’t the only ones thinking along these lines. Shortly afterwards, Ditto was released, tackling the same problem in a similar way, and, being backed by some of the prolific and respected package and open source developers in the Umbraco community, gained quite a bit of traction. Following various other code-first and “code-last” initiatives, not long after Models Builder was produced, initially as an independent effort and later incorporated into the core. This takes a different approach, avoiding the reflection techniques that Ditto and Umbraco Mapper use, and instead using code generation to wrap the calls to Umbraco published content APIs with strongly typed equivalents.
Despite these alternative approaches, we’ve continued to use Umbraco Mapper as part of our toolkit for developing Umbraco based web applications and have incorporated it’s use into our “base build” which forms the core of new projects on the platform. I’d like to think that along the way we’ve had quite a bit of success with it and, judging by the continuing download figures, hopefully others have too.
That said, we recognise that it’s important to take stock of the different ways we might go about working with Umbraco and some colleagues (the less biased ones!) are doing this to make sure we are making best use of the core and other community offerings in this space.
We had plans for this anyway, but following Lars-Erik’s talk at the UK Umbraco Festival, we thought it would be good to do this piggy-backing on some of the work he and others had done. In preparation for his talk, he’s worked on a version of the Umbraco Txt starter kit, which is available in the original and then, in a copy, adapted — or “Dittoified” — by Matt Brailsford, one of the developers of Ditto. Lars-Erik then created a third instance, that he “ModelsBuilderified”, as a point of comparison.
And so to throw another hat in the ring, we’ve taken a further copy, and “UmbracoMapperified” it, which is available for review here if anyone is interested to see how we are going about things. It’s not solely presenting use of Umbraco Mapper, though that’s part of it; we are also include some other techniques that we utilise.
What we’ve tried to do with this implementation is achieve the following benefits:
- Simple, logic-less views. Each view is based on a view model that provides only the data required for that view. The view models are simple POCOs, so it’s actually not possible access the Umbraco APIs from the view at all. Some may find that restrictive but for us it’s a good way to ensure that any logic further than simple loops and conditions for presentation are kept out of the views.
- Separation of “content” models and view models
- Given this separation, make the mapping to the view models as painless as possible — using conventions where we can, overriding where needed and being able to handle the mapping of more complex types in a consistent and reusable way
- Thin controllers — utilising a “mediator” pattern to delegate the construction of view models to dedicated “handler” classes
- Following SRP (single responsibility principle) — meaning we likely have quite a few classes, but each one is small, and focused on one thing
- Try to treat Umbraco MVC rendering as closely as possible to how we would work with a custom ASP.Net MVC application or other CMS products
- Support unit testing the parts of code where the logic lives, which is primarily in the “handlers” that are responsible for mapping from the Umbraco content to the view models
Before moving on to explain some of the details of the implementation, whilst comments and feedback are welcome, I want to emphasise that this post isn’t intended to get into a pillow fight on the best way to build Umbraco sites — it’s for everyone to judge what works best for them and people will have different opinions and priorities. As I say, could be we’ll be looking at other options for future sites.
But we do currently find it works well for us, and as part of our own review of practices, presenting the same application put together in four different ways provides a really nice way to see the pros and cons of the different approaches. We’ll be using that comparison for our own review, and hopefully by presenting an alternative approach others might find it useful too.
Hopefully the code is relatively easy to follow through, when read in conjunction with the Umbraco Mapper documentation itself. Each class is commented quite thoroughly. That said, it may help for people new to the approach used here to take a path through the code and describe in more detail, which I’ll do taking the “News overview” page as an example.
The best place to start is likely at the controller — UmbNewsOverviewController is used to hijack the route for requests for pages based on the news overview document type. This calls into a class called ViewModelBuilder, which has a number of overloads available. All of them are involved with mapping from some Umbraco content to a view model, and the overload we use here allows us to pass in an instance of the current page as an IPublishedContent, along with some additional data provided in the query-string for paging of the news list presented on the overview page.
Tracing into the ViewModelBuilder’s Build() method, this leans on Ninject, an IoC container, to resolve the particular handler configured for this view model. This is an example of a Mediator pattern (implemented for example in .Net by the open source package MediatR that we use in other ASP.Net applications to keep controllers thin and not mix too many concerns.
In this case the resolved handler is NewsOverviewPageHandler, which is responsible for mapping properties from the news overview page itself, along with a paged list of ordered news articles.
Tracing back though to the ViewModelBuilder we can now return a populated view model to our controller, where it is passed to the view. The view has the view model defined as it’s model, thus allowing the content to be displayed with a simple, strongly typed, Model.Property syntax.
Finally there’s the NewsOverviewPageHandlerTests class in the tests project, which illustrates how we can achieve a good level of code coverage for the key parts of the application that contain logic, primarily in the mapping from Umbraco content to view models.
Considerations and concerns
Some may baulk a little at some of the above, seeing a bit too much abstraction and indirection, which is something we are conscious of. That said, doing things this way allows us to achieve the benefits outlined above, and much of the infrastructure for this is something we only need to write once and then can forget about as it’s tucked away nicely in our base build. When it comes to a new feature, we just need to implement an appropriate set of controller, view model, view and handler (and of course tests), which is a nice pattern for anyone new to the project to pick up.
One way to make this slightly simpler would be to have a means of creating each of these files for a given document type in one go — perhaps via a VS.Net code snippet or command line tool, to allow the scaffolding of a controller, handler and stub view model for a given document type. That’s something we have under consideration.
Another, related objection we are aware of is that there are quite a few classes required. Some of this is inevitable in the way we are doing things — if we aim to adhere to SRP we’d expect to utilise more, smaller classes. There are some classes though that don’t do a great deal — in the simple cases where we do just render the content of a single content item as a page, the controller and handler don’t have much work to do, but need to exist. We’d like to investigate if there’s a way of doing this via convention, perhaps using a default controller and/or generic handler, which would mean we’d only need to create these classes where they need to do something out of the ordinary.
Credits and thanks
In closing just wanted to say thanks to Matt and Lars-Erik for the work they did in their transformations of the starter kit, discussing the different approaches, and giving us the idea to put together this sample ourselves. Ali Taheri, Tom Morris, Rob Purcell, Robin Herd and others at Zone also should take most of the credit for the production of our Umbraco base build from which much of the techniques in the sample are drawn.