However, by adding a new Rating document type to the Event doctype via composition, we:

Extend the generated properties in the class

Do not modify the existing behaviour of the class

Although, it should be considered that if you want to delete or change the data type of an existing property on a document type instead, you could lose data and are perhaps breaching the open/closed principle.

Long-term maintenance and effects need to be considered if you're always extending and never deleting. We don't want the CMS to become cumbersome due to legacy properties. A balance needs to be had, and the last thing anyone needs is an over-architected solution.

Compositions

Now back to the Event Rating example. Instead of adding the new Rating property directly to the Event doctype, we could add a new property to the Umbraco document type via Umbraco's Compositions feature, rather than modifying an existing property. In Models Builder generated classes, any document type used in a composition (previously known as a "mixin") generates an interface for the class it is composed from, and implements the well-known IPublishedContent interface. The generated classes of any doctypes composed of another doctypes implement it's interface, and contain all of the members of that interface. This is programming by contract.

Create the new doctype for Event Rating and add it to the Event via composition. Don't forget to regenerate the models after saving the doctype:

The effect of creating a new docType called Rating, adding this to the Event doctype via composition, and running Models Builder is that:

The Event class now implements a new IEventRating interface that has been generated as a partial interface, which itself implements IPublishedContent

Event still inherits from PublishedContent and has its original properties, but now also implements the new IRatinginterface containing the new Rating property

Regenerating the models has created a concrete instance of the EventRating class that also implements the IRating interface

We can populate the Umbraco Rating field for the Event, save and publish. Next, our custom Event class now can be edited to use the new Umbraco property instead of the dummy hard-coded int:

using System; using System.Globalization; using Umbraco.Core.Models; namespace eg.skrift.data { public class EventDetail : Event, IEventRating { public EventDetail(IPublishedContent content) : base(content) { } /// <summary> /// Get the rating from Umbraco /// </summary> public int EventRating { get { try { int.TryParse(Rating, out int rating); return rating; } catch (Exception e) { Console.WriteLine("Issue when parsing the rating to an int", e); throw; } } } } }

Our controller can simply return the view with the typed Umbraco content (it uses Ditto to map the IPublishedContent to a concrete EventDetail class):

using System.Web.Mvc; using Our.Umbraco.Ditto; using Umbraco.Web.Models; using Umbraco.Web.Mvc; namespace eg.skrift.data.Controllers { public class EventController : RenderMvcController { /// <summary> /// Renders the Event page /// </summary> /// <param name="model"></param> /// <returns></returns> public override ActionResult Index(RenderModel model) { var typedModel = model.As<EventDetail>(); return CurrentTemplate(typedModel); } } }

In future, if requirements change to using a third party service instead of Umbraco to store the ratings, we could do the following:

Undo the composition of the Rating doctype on the Event docType

Regenerate models

Recreate the IRating interface previously generated and make it into a custom interface (property can now be an int)

Revert the EventController to assign the rating value instead of taking it from Umbraco

The implementation can now use a third party service to store event ratings. Our existing EventDetail class will not change as it still inherits from Event, it just has to implement the IRating interface, so we don't need to alter the code inside the Event class. When EventDetail inherited from the Event with an Umbraco doctype Rating composition on it, it already implemented the IRating interface so we didn't need to specify that in code. Any unit tests using IRating interface should still be good to go. Bear in mind that since you will be removing a composition from the doctype, you are modifying data, so best to have a data migration strategy in place.

Liskov substitution principle

The Liskov Substitution Principle (LSP) was defined by American scientist and professor Barbara Liskov in “Data Abstraction and Hierarchy,” 1988, and while it seems complex initially, it can be summarised as:

"objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program"

...right, so that actually still sounds quite complex. I want to break it down more:

Similar objects (like those that derive from the same definition, or implement the same contract), can be replaced with ease and no YSOD, DEFCON 1, page-exploding support tickets coming through.

...even more:

You can swap things and they don't break.

Lets carry on in terms of our Event example above. Now that we have successfully got Ratings implemented and have some breathing space, we've looked at the backlog and one of the tasks is to sort Events into different Event types. There are actually several - Live Events, Interview Events and Prerecorded Events.

The first task is fairly standard - we need to create each required Event type as a new doctype in Umbraco - InterviewEvent, LiveEvent and PrerecordedEvent. We rename Event to EventBase and move it to the doctype Composition folder for organisation. We already took off the Rating composition due to the previous change, so the EventBase doctype can now be used as a composition itself.

We compose all new Event types from the Event Base doctype, and set them all to use the existing Event template. EventBase itself no longer needs a template, as we're not going to use it as an independent page in future.

NB: Don't forget to give permissions to the Home node to allow these docTypes as children, and for neatness, switch off permission to Event Base since it is not going to be independent anymore. Also, don't forget to regenerate the models afterwards!

Luckily, Umbraco lets us change, or substitute, one doctype for another with a nice mapping feature, so we can change the existing EventBase doctype to a LiveEvent:

We are also told by our wily Business Analyst that each Event Type has their own way of getting rated:

Interview events are rated by radio listeners

Live events are rated on a third party review site

Prerecorded Events are rated by manual data entry after a questionnaire is submitted post event

To handle this, we need to use our own classes rather than directly referencing the generated models builder classes. We decide to create an IEventDetail interface that extends the auto-generated IEventBase and IEventRating:

eg.skrift.data.Models { public interface IEventDetail : IEventBase, IEventRating { } }

Then we create an EventDetail class that implements the auto-generated EventBase and the custom IEventDetail interface. It contains a property for the Event Rating and inherits from EventBase to gain those Umbraco-driven properties. IEventRatinginterface remains the same, and Event Title and Event Summary are not shown because they're already present (via inheritance from the EventBase class):

using Umbraco.Core.Models; namespace eg.skrift.data.Models { public class EventDetail : EventBase, IEventDetail { public EventDetail(IPublishedContent content) : base(content) { } /// <summary> /// Get the rating from Umbraco /// </summary> public int EventRating { get; set; } } }

It is the responsibility of the sub-class to give a suitable definition appropriate to the Event type. So, we have to create our own implementations of a concrete event. These new Event types have already been generated by the ModelsBuilder tool as partial classes, but we set the EventRating properties on each one as follows (you can return real ratings later):

namespace eg.skrift.data.Models { public partial class LiveEvent : IEventDetail { public int EventRating => GetEventRating(); /// <summary> /// Gets the rating for an event. /// </summary> /// <returns></returns> private int GetEventRating() => 5; } } namespace eg.skrift.data.Models { public partial class PrerecordedEvent: IEventDetail { public int EventRating => GetEventRating(); /// <summary> /// Gets the rating for an event. /// </summary> /// <returns></returns> private int GetEventRating() => 3; } } namespace eg.skrift.data.Models { public partial class InterviewEvent : IEventDetail { public int EventRating => GetEventRating(); /// <summary> /// Gets the rating for an event. /// </summary> /// <returns></returns> private int GetEventRating() => 4; } }

We have:

Created our own partial class implementations of each new Event Type

Made each new event type implement the IEventDetail class

Made each new event type return a required value for the EventRating int property

Our Event template only needs one change - it is still inheriting from the original EventDetail class via UmbracoTemplate. To enable this template to render the model for any type of Event we change it to inherit from IEventDetail:

@inherits UmbracoTemplatePage<eg.skrift.data.Models.IEventDetail>

These pages will still be hit without a constructor due to Umbraco rendering the page using the defined template. However, if we want to customise the behaviour further, we'll need a controller. The controller that was previously hit was EventController, but this needs to be renamed to the relevant {DocTypeName}Controller in order to follow the Umbraco auto-routing convention. The solution we'll choose for now is to create a controller for each docType and then inherit from a shared base controller called EventBaseController. The new Event Type controllers don't need to call the method GetEventRating as this is dealt with in the concrete implementations via the models. They simply inherit from the EventBaseController and return the view with the given Umbraco page values populated in it:

using eg.skrift.businesslogic.Factories; using eg.skrift.data.CMS; namespace eg.skrift.data.Controllers { public class PrerecordedEventController : EventBaseController { public PrerecordedEventController() { } } }

We can change the docType of an Event in Umbraco in future, e.g. a content editor may want to change a Live Event to a Prerecorded Event once the Event has passed, swapping it out with no ill effects and therefore following LSP. We can also easily add new Event types in future by simply creating the equivalent classes that inherit from EventDetail, only needing to alter how the Rating is accessed. All the doc types can share the same Event template if they wish - a new EventController will be created if the controller logic needs to be accessed, and the concrete implementation of the doctype subclass will be used.

The key takeaway here is that:

Changing the docType will not break our implementation when getting an Event Rating, because by using the IEventDetail interface, we can switch out the classes for each other

The LiveEvent doctype doesn't need to know about PrerecordedEvent. If it did, it would violate the Open-Closed principle, because both LiveEvent and PrerecordedEvent classes would need to be modified whenever a new Event doctype composed of the EventBase doctype was created in Umbraco.

Another example of LSP is the handy Umbraco Ditto Package. This enables us to easily convert any Umbraco doctype into a strongly-typed class that implements IPublishedContent, by simply doing:

var typedModel = model.As<ClassName>();

This implemention works for any sub class that implements IPublishedContent, and therefore I reckon it follows LSP. Swapping stuff out without breaking it.

Interface segregation principle

We've already covered the concept of interfaces in the Open/Closed principle earlier, but to recap:

Interfaces are essentially contracts, and any class that implements an interface must follow it's contract

Like abstract base classes, classes/structs that implement an interface must implement all the members, be it events, indexers, methods, or properties. Bearing this in mind, and how many members this could end up being in large systems, the Interface Segregation Principle, described in Robert Martin's "Design Principles and Design Patterns" (2000), states that:

"many client-specific interfaces are better than one general-purpose interface"

We have shown through our earlier examples that we naturally use many client-specific interfaces by using inheritance of doctypes in Umbraco. IEventBase is generated as soon as the EventBase doctype becomes part of a composition of another doctype. Ultimately, in Version 7 at least, every node in Umbraco implements IPublishedContent, which is likely a very familiar face if you are working with back-end code and using Umbraco. Every node, whatever doctype it is, has the same members as the IPublishedContent contract dictates. Here are a few of them you'll probably recognise:

int Id { get; } int TemplateId { get; } int SortOrder { get; } string Name { get; } string UrlName { get; } string DocumentTypeAlias { get; } int DocumentTypeId { get; } string WriterName { get; } string CreatorName { get; } int WriterId { get; } int CreatorId { get; } string Path { get; } DateTime CreateDate { get; } DateTime UpdateDate { get; } Guid Version { get; } int Level { get; } string Url { get; } ...

Taking one as an example from Umbraco, UpdateDate is found in the Umbraco backoffice:

Interfaces should always be focused

So, now we need to get Event Ratings for reals, rather than our temporary hard-coded solution. We decide to create an IRatingsService that allows the retrieval and updating of ratings. This can be logically split into two parts, neither of which need to know about each other:

Getting a rating (IHaveRatings)

Setting a rating (IStoreRatings)

We create an IRatingsService that extends both the IHaveRatings and the IStoreRatings interfaces, allowing us to only pass the narrow functionality required by the consumer.

Lets add these interfaces into the sample code:

public interface IRatingsService : IHaveRatings, IStoreRatings { } public interface IHaveRatings { /// <summary> /// Get a rating for a given event /// </summary> /// <param name="eventId"></param> int GetRating(int eventId); } public interface IStoreRatings { /// <summary> /// Store a rating for a given event /// </summary> /// <param name="rating"></param> /// <param name="eventID"></param> void SetRating(int rating, int eventID); }

So how should we access these services in code in a SOLID way? The concrete implementation of these service interfaces can be created using Dependency Injection, which brings me onto the last, but by no means least, of the five SOLID principles - Dependency Inversion.

Dependency Inversion principle

The Dependency Inversion principle is also discussed in "Design Principles and Design Patterns" by Robert C. Martin (get the feeling this is a good book to read?), and states that:

one should "depend upon abstractions, [not] concretions"

You'll most likely have come across Inversion of Control (IoC) principle and the Dependency Injection (DI) design pattern in any back-end software development environment. There are hundreds of articles and books on each topic alone, but essentially, by using a proper DI container, like AutoFac or Ninject, you can help implement DI in your application. So? Well, imagine never having to hard-code concrete objects again!

Disclaimer: After learning Dependency Inversion techniques, you may become severely phobic of hard-coding dependencies in code

Abstracting away from the CMS

As you have seen throughout this article, we're working with abstractions wherever possible. We've learned that the concept of Umbraco nodes is built upon an IPublishedContent interface - an abstraction of the content node itself, and a shift that has enabled Umbraco to separate concerns from concrete classes in code.

Umbraco itself is based on the MVC pattern, allowing a separation of concerns between the Views (usually Razor files), the Controllers (usually inheriting from relevant Umbraco base controllers), and Models (either custom or auto-generated via something like Models Builder, or a combination).

Like most CMS systems, Umbraco exposes a Client Context in the form of UmbracoContext, which encapsulates the Umbraco-related information of the HTTP request. When building an Umbraco site, whilst the website is naturally quite tightly coupledto Umbraco (the nuget package itself builds the website with all Umbraco dependencies required), the rest of the application doesn't need to be. Umbraco provides the UmbracoCms nuget package, along with an independent UmbracoCms.Core nuget package without the "web stuff" and containing the key components of Umbraco. This means you can increasingly work away from the website, and move the models into a separate data project and logic into a separate business logic project.

Dependency Inversion Container example using AutoFac

Ideally, we would not hard-code the dependencies to Umbraco and the access to UmbracoContext in our controllers. We would instead use a DI Container like AutoFac to define the dependencies in the object composition root of the application, an example being the Global class below that extends UmbracoApplication and will replace the default Global.asax reference to Umbraco.Web.UmbracoApplication.

First using Package Manager Console, we can install the required DI Container of choice:

> install-package Autofac.Integration -project eg.skrift.data > install-package Autofac.Mvc5 -project eg.skrift.data > install-package Autofac.WebApi2 -project eg.skrift.data

Next, setup the DI Container in a custom Global class that inherits from UmbracoApplication (more information in the Umbraco IoC docs:

using System; using System.Reflection; using System.Web.Http; using System.Web.Mvc; using Autofac; using Autofac.Integration.Mvc; using Autofac.Integration.WebApi; using eg.skrift.businesslogic.Factories; using eg.skrift.data.Factories; using Umbraco.Core; using Umbraco.Web; namespace eg.skrift.data.CMS { public class Global : UmbracoApplication { /// <summary> /// Registers our controllers and Umbraco controllers, plus the types we need to resolve, /// and sets up MVC to use Autofac as a dependency resolver as our DI Container of choice /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected override void OnApplicationStarted(object sender, EventArgs e) { base.OnApplicationStarted(sender, e); var builder = new ContainerBuilder(); builder.RegisterInstance(ApplicationContext.Current).AsSelf(); //register all controllers found in your assembly builder.RegisterControllers(Assembly.GetExecutingAssembly()); builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); builder.RegisterApiControllers(typeof(Global).Assembly); //register umbraco MVC + webapi controllers used by the admin site builder.RegisterControllers(typeof(UmbracoApplication).Assembly); builder.RegisterApiControllers(typeof(UmbracoApplication).Assembly); builder.RegisterType<UmbracoLoggerFactory>() .As<ILoggerFactory>(); builder.RegisterType<UmbracoContentFetcherFactory>() .As<IContentFetcherFactory>(); builder.RegisterType<UmbracoContentFetcher>() .As<IUmbracoContentFetcher>() .WithParameter((paramInfo, ctx) => paramInfo.Name == "umbracoHelper", (paramInfo, ctx) => new UmbracoHelper(UmbracoContext.Current)); builder.RegisterType<RatingsServiceFactory>() .As<IRatingsServiceFactory>(); var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); } } }

The updated Global.asax now references this Global.cs class:

<%@ Application Inherits="eg.skrift.data.CMS.Global" Language="C#" %>

The above code follows Dependency Inversion by binding a context interface to its concrete implementation. IUmbracoContentFetcher is bound to UmbracoContentFetcher, IRatingsServiceFactory is bound to RatingsServiceFactory. Notice that we use a custom class called UmbracoContentFetcherFactory - we use this in order to spin up our own UmbracoContentFetcher. This is essentially a class that wraps the default UmbracoHelper class but it implements its own IContentFetcher interface, and adds some additional Umbraco content-related methods that use the helper, or their own Examine-driven content queries.

We also use an UmbracoLoggerFactory, which we inject into our controllers using DI - it implements an ILoggerFactory. Through Dependency Injection, our logger can log any separate errors that come from an Angular application in the same site to the same centralised Umbraco logs. This is despite the fact that our Angular API knows nothing about Umbraco - you can inject the ILoggerFactory into the relevant API Controller, and log away like you're a lumberjack. It also means that at any time we could choose to swap out this logger from using Umbraco logging to something different, by simply changing one line in the OnApplicationStarted method above.

Decoupling code from the CMS

At Rock Solid Knowledge (SOLID being very apt), we have built a lot of business logic that uses Umbraco, but is completely decoupled. That has the huge advantage of allowing us to:

Unit test without worrying about UmbracoContext

Migrate aspects of functionality away, or into, the CMS as we desire

Use Mocks for unit testing

Having the interfaces injected in also means that if we want to replace Umbraco with another CMS system (again, promise we won't!), we can just modify the bindings. Theoretically, and I know this is idealistic because there are a lot of moving parts to consider, there would be no further changes to the MVC application and controllers required.

Back to the Ratings Service. We have created the IRatingsService.cs, IHaveRatings.cs and ISetRatings.cs interfaces in a new Business Logic project eg.skrift.businesslogic. We also have followed the Factory Pattern and created an IRatingsServiceFactoryand a RatingsServiceFactory, the sole purpose being to create a RatingsService. We decided via the AutoFac DI Container that the concrete factory for IRatingsServiceFactory will be the concrete RatingServiceFactory, which will create a concrete RatingService for us when we inject it and call Create() on it.

We have built up a suite of unit tests that create the required functionality for the IRatingsService by following the red, green, refactor unit testing principles. As a reminder, these are:

Create a failing unit test (RED) Write code to make that test pass (GREEN) Make it pretty (REFACTOR)

The example code uses Nunit but it is personal preference. We haven't started to use Mocks here yet - this is just a starting point for now:

using System; using eg.skrift.businesslogic.Services; using NUnit.Framework; namespace eg.skrift.businesslogic.tests { [TestFixture] public class RatingsServiceTests { private const int testEventId = 4047; private const int rating = 5; [SetUp] public void SetUp() { //TODO: setup code here } [TearDown] public void TearDown() { } public RatingsService CreateSut() { return new RatingsService(); } [Test] public void GetRating_WhenCalledWithEventId_ReturnsCorrectRating() { var sut = CreateSut(); var result = sut.GetRating(testEventId); Assert.That(result, Is.Not.Null); Assert.That(result, Is.EqualTo(rating)); } [Test] public void SetRating_WhenCalledWithRatingAndEventId_ShouldNotThrowException() { var sut = CreateSut(); try { sut.SetRating(rating, testEventId); } catch (Exception ex) { Assert.Fail("Expected no exception, but got: " + ex.Message); } } } }

These tests have led to the interface having the methods:

namespace eg.skrift.businesslogic.Services { /// <summary> /// Service to get and set ratings for an event /// </summary> public class RatingsService : IRatingsService { /// <summary> /// Get an event rating /// </summary> /// <param name="eventId"></param> /// <returns></returns> public int GetRating(int eventId) { //TODO: implement functionality return 5; } /// <summary> /// Set an event rating /// </summary> /// <param name="rating"></param> /// <param name="eventID"></param> public void SetRating(int rating, int eventID) { //TODO: implement functionality } } }

As you saw earlier, we've already created a concrete RatingsService that implements IRatingsService, and we've registered the factory that creates it, IRatingsServiceFactory, via our DI Container. Now we're ready to inject the factory into our controller in order to create an instance of the RatingsService and use it. We'll also inject the ILoggerFactory and create an instance of our own logger (NB: you don't have to do it this way in Umbraco - you can use the built-in logging functionality):

using System; using System.Reflection; using System.Web.Mvc; using eg.skrift.businesslogic.Factories; using eg.skrift.businesslogic.Services; using eg.skrift.data.CMS; using eg.skrift.data.Models; using Our.Umbraco.Ditto; using Umbraco.Web.Models; using Umbraco.Web.Mvc; namespace eg.skrift.data.Controllers { public class EventBaseController : RenderMvcController { internal new ILogger Logger; private IRatingsService ratingsService; public EventBaseController(ILoggerFactory loggerFactory, IRatingsServiceFactory ratingsServiceFactory) { if (loggerFactory == null) throw new ArgumentNullException(nameof(loggerFactory)); InitaliseLogger(loggerFactory); if (ratingsServiceFactory == null) throw new ArgumentNullException(nameof(loggerFactory)); InitaliseRatingsService(ratingsServiceFactory); } private void InitaliseRatingsService(IRatingsServiceFactory ratingsServiceFactory) { ratingsService = ratingsServiceFactory.Create(); } private void InitaliseLogger(ILoggerFactory loggerFactory) { Logger = loggerFactory.Create(MethodBase.GetCurrentMethod().DeclaringType); } /// <summary> /// Renders the Event page /// </summary> /// <param name="model"></param> /// <returns></returns> public override ActionResult Index(RenderModel model) { var typedModel = model.As<EventDetail>(); if (ratingsService != null) typedModel.EventRating = ratingsService.GetRating(typedModel.Id); return CurrentTemplate(typedModel); } } }

Note that in the sample code, the eg.skrift.web project references the eg.skrift.data project, the eg.skrift.data project references the eg.skrift.businesslogic, and the eg.skrift.businesslogic project has no knowledge of either project, nor does it have knowledge of Umbraco at all. You can compile and run the code, and the Event view will be able render the Event Rating from the service we injected using DI, which has no knowledge of the CMS at all. 😺

DI for the absolute win

Summary

We've covered each of the five SOLID principles, and considered some ways that they could be applied to CMS Development. We've built up our code to try and follow SOLID principles using realistic scenarios, and we've thought about ways in which it can become tricky, and how to try and get around that. I imagine that this topic could create a lot of debate and consideration, so I am keen as mustard to be told of other peoples implementations of SOLID in CMS development. There are many other ways of following SOLID and a lot of additional design patterns and subjects that I've not covered in here (I'll leave describing the many advantages of unit testing Umbraco to Lars-Erik Aabech!) - I'm sure that I'll be refactoring the sample code shown in future.

I'd love to chat about this topic in person, as it is definitely something that needs to be regularly revisited, whether via additional articles or talks in future - especially as Umbraco and other CMS products continually evolve and get optimised. Hopefully this article has got you thinking and talking about how to adhere to SOLID principles when developing with a CMS.

Just because the CMS is boxed up and ready to go doesn't mean that we should stop thinking outside the box and trying to follow SOLID development principles

References and Useful Reads

Shout Outs