In this post I’ll talk about the MVPC (Model-View-Presenter-Controller) design pattern I use in client applications such as progressive web apps and mobile clients. It can be best understood as an evolution of the MVC (model-view-controller) pattern past the MVP (model-view-presenter) pattern.
The basics: MVC and MVP
You might want to skip this section if you are already familiar with the MVC and MVP patterns.
The model-view-controller [MVC] pattern places business logic in the controller and view-specific logic into the view. The controller is agnostic of the view implementation and exchanges messages with it over the model. Examples of such interactions could be the controller instructing the view to render a list of addresses or the view informing the controller that the user clicked a button.
Because it will become relevant later, I’d like to focus on the view a bit more. The view contains all the logic necessary to render the model and inform the controller of user interactions. The model is meant to be view-agnostic, so it is usually a generic description using domain entities. This means that the view is a rather heavy adapter which transforms between the domain-specific API (the model) and the view technology’s API such as a browser DOM or iOS widgets. Since stubbing the view API is often impossible, view implementations are rarely unit-tested which is a dangerous blind spot in QA.
The model-view-presenter [MVP] is an architectural design pattern similar to MVC, but with a difference in focus: whereas MVC tries really hard to keep the view logic out of the controller, MVP moves much of the view logic into the controller which it calls presenter. The view is reduced to a thin adapter between the presenter API and the underlying view technology API and the model is tailored to the view. This means that a presenter is well aware of the technical detail of a view implementation and thus cannot be reused for different view implementations (more on that later).
The diagram isn’t much different from MVC’s, but the model of the update event is quite – and so is the view interface. The interaction between the presenter and the view is typically much more chatty than what is the case with MVC. The presenter undertakes a series of incremental modifications of the view. MVP view APIs often contain explicit references to their implementation technologies, like “setCss” or “setWidthInPx”. The obvious down side is that MVP offers virtually no abstraction from the view domain other than isolation from the underlying view technology API. The upside however is that as all relevant rendering logic lies now in the presenter without dependencies on the view technology API, that logic can now be unit tested by stubbing the view API.
The model-view-presenter-controller pattern is badly arranged chain of words which brings back MVC’s clean separation of view and logic into the MVP pattern. The base idea is that the presenter from the MVP pattern is split into a controller which uses domain-specific models and a presenter which uses view-specific models. Thus, the controller can interact with other components of the application with large data objects which make sense in the application domain, including the presenter for it’s own view. The presenter unravels the domain objects into a sequence of finely attuned API invocations to the view. The benefits of MVPC are:
- Clean architecture
- Isolation of concerns
- Honouring of domain concepts
- Promotes reusability
- Supports multiple view technologies
- High test coverage