Creating maintainable applications has always been the real long-term challenge of building applications.
Not long ago, I worked for a company whose core business application was a SaaS platform, used by a few thousands of client companies. That crucial application was three years old and had code files with HTML, CSS, business logic and SQL mixed up. Of course, two years after being released, the company decided to rebuild it from scratch. Although these situations still happen, today many of us know these practices are wrong and know how to avoid them.
However, back in the 1970s, mixing responsibilities was the common practice and people were still trying to discover how to do it better. As application complexity grew, making changes to the UI would inevitably imply changes to the business logic as well, increasing the complexity of the changes, the time spent to do those changes and the likelihood of bugs (because there would be more code changed).
MCV came into play to solve those problems, by promoting "separation of concerns" between front-end and back-end.
With the intention of solving the issues explained above, in 1979 Trygve Reenskaug came up with the MVC pattern as a way to separate concerns, isolating the UI from the business logic. The pattern was used in the context of desktop GUIs, which existed since 1973.
The MVC pattern separates the code in three conceptual units:
- The Model represents the business logic;
- The View represents a widget in the UI: a button, text box, etc.;
- The Controller provides for coordination between the view and the
model. This means it:
- Decides what views to show, and with what data;
- Translates user actions (ie. click of a button) into business logic.
A model could be a single object (rather uninteresting), or it could be some structure of objects.
Trygve Reenskaug 1979, MVC
Other important concepts to know about the original MVC pattern is that:
- The view uses the Model data objects directly, to show their data;
- When the model data changes, it triggers an event that immediately updates the View (remember, in 1979 there was no HTTP);
- Each view is, typically, associated to one controller;
- Each screen can contain several pairs of view & controller;
- There could be multiple views for each controller.
Today's HTTP Request & Response paradigm, that I'm familiar with, does not use this original MVC flavor because, in this case, the flow goes from the View to the Controller, like what I am familiar with, but then in the other direction it flows directly from the model to the view without passing through the Controller.
Furthermore, in the current Request & Response paradigm, when data changes in the DB it does not trigger an update in a View being shown in a browser (although this could be implemented using web sockets). To see the updated data, the user needs to do a new request and the updated data would always return through the controller.
PAC, aka HMVC, provides for increased modularity in contexts of widgetization of sections of the UI.
For example, when we have a View with a section that is used in the exact same format throughout several other Views or even simply repeated in the same view. A practical example is a section of a web page with the contents of an RSS feed, which is reused in other pages.
Using HMVC, the controller that handles the main request will forward sub-requests to other controllers in order to get the renderization of the widgets and then incorporate it in the renderization of the main view.
Personally, I have encountered the need for this a few times in a context of HTTP Request/Response paradigm, but I find it a much simpler approach to have the UI make an AJAX call to the controller that can render the widget. This keeps the benefits of modularization without the added complexity of having nested controller calls, and the plus side that these sub-requests can be cached in something like Varnish.
The MCV pattern provided a great improvement in the programming paradigm of its time. However, as applications complexity grew, so it grew the need for further decoupling.
In 1996, the IBM subsidiary Taligent made public their MVP pattern, based on MVC. The idea was to further isolate the Model from the UI concerns:
- The View is passive and unaware of model;
- Focus on thin controllers (presenters) that contain no business logic and simply invokes commands and/or queries in the model, passing raw data to the View;
- A change in data does not trigger an update in a view directly: it always goes through the presenter, which in turn updates the View. This allows for the controller (presenter) to perform extra presentation-related logic before updating the view. For example, also update data related to the data that changed in the DB;
- There is a single presenter for each view.
This is closer what I am used to seeing in today's Request/Response paradigm: the flow always goes through the Controller/Presenter. Nevertheless, the Presenter still does not actively update the view, it always needs a new request to be performed for the changes to be visible.
In MVP, the Presenter is also known as a Supervisor Controller.
Again, derived from applications increased complexity, in 2005 the MVVM pattern was announced by John Gossman, one of Microsoft's WPF and Silverlight architects, with the goal to further segregate the UI design from the code and provide data binding from the View to the data model.
[MVVM] is a variation [of MVC] that is tailored for modern UI development platforms where the View is the responsibility of a designer rather than a classic developer. [...] the UI part of the application is being developed using different tools, languages and by a different person than is the business logic or data backend.
John Gossman 2005, Introduction to Model/View/ViewModel pattern
The Controller is "replaced" by the ViewModel:
[The View] encodes the keyboard shortcuts and the controls themselves manage the interaction with the input devices that is the responsibility of Controller in MVC (what exactly happened to Controller in modern GUI development is a long digression...I tend to think it just faded into the background. It is still there, but we don't have to think about it as much as we did in 1979).
John Gossman 2005, Introduction to Model/View/ViewModel pattern
The ideas behind MVVM were:
- One ViewModel corresponds to only one View and vice-versa;
- Move view logic to the ViewModel to simplify the view;
- One on one mapping between the data used in the view and the data in the ViewModel;
- Binding of the data in the ViewModel to the data in the View, so that when data is changed in the ViewModel it immediately reflects in the View.
Just like in the case of the original MVC pattern, this approach is not possible in a traditional Request/Response paradigm because the ViewModel can not actively update the View (unless using web sockets) and MVVM requires it. Also, the fact that the ViewModel has properties matching the data used in the View is not what is common practice in a Controller, in my experience.
When building complex Enterprise Applications for the Cloud, I prefer to rationalise the application UI structure as M-V-P-VM where the ViewModel is what Martin Fowler called a Presentation Model, back in 2004.
-
A set of classes that contain all the business logic and use cases;
-
A template, with which to generate the HTML using a template engine;
-
ViewModel (aka Presentation Model )
Receives raw data from a query (or Model entities from which to extract raw data), and holds that data to be used in the template. It also encapsulates complex presentation logic, in order to simplify the template. I find that the usage of the ViewModel is especially important because we will not be tempted to use entities in the template, which allows us to completely isolate the view from the Model:
- Changes in the Model (ie. changes in entities structure) might bubble up and impact the ViewModel, but not the template;
- Complex presentation logic will not leak into the domain (ie. creating methods in business entities that are exclusively related to presentation logic) because we can encapsulate it in the ViewModel;
- The dependencies of the template become explicit because they must be set in the ViewModel. Making those dependencies visible can help us, for example, to decide what should be eagerly loaded from the DB to prevent N+1 problems.
-
Receives an HTTP request, triggers a command or a query, uses the data returned by the query, a ViewModel, a Template and a template engine to generate the HTML and send it back to the client. All Views interaction goes through a Presenter.
Here's a simple (and naive) code example of how I do it:
<?php
// src/UI/Admin/Some/Controller/Namespace/Detail/DetailsViewModel.php
namespace UI\Admin\Some\Controller\Namespace\Detail;
// use ...
final class DetailsViewModel implements TemplateViewModelInterface
{
/**
* @var array
*/
private $mainEntity = [];
/**
* @var array
*/
private $relatedEntityList = [];
/**
* @var bool
*/
private $shouldDisplayFancyDialog = false;
/**
* @var bool
*/
private $canEditData = false;
/**
* @param SomeEntity $mainEntity
* @param RelatedEntity[] $relatedEntityList
*/
public function __construct(SomeEntity $mainEntity, array $relatedEntityList)
{
$this->mainEntity = [
'name' => $mainEntity->getName(),
'description' => $mainEntity->getResume(),
];
foreach ($relatedEntityList as $relatedEntity) {
$this->relatedEntityList[] = [
'title' => $relatedEntity->getTitle(),
'subtitle' => $relatedEntity->getSubtitle(),
];
}
$this->shouldDisplayFancyDialog = /* ... some complex conditional using the entities data ... */ ;
$this->canEditData = /* ... another complex conditional using the entities data ... */ ;
}
public function getMainEntity(): array
{
return $this->mainEntity;
}
public function getRelatedEntityList(): array
{
return $this->relatedEntityList;
}
public function shouldDisplayFancyDialog(): bool
{
return $this->shouldDisplayFancyDialog;
}
public function canEditData(): bool
{
return $this->canEditData;
}
}
The template and the ViewModel have a one-on-one match, which means that a View can only be used with a specific ViewModel and vice-versa. This actually even makes me think that maybe we could encapsulate the template and the ViewModel inside a View object, effectively decoupling the Controller from the template and the ViewModel, making it depend on a generic View Interface, but I never experimented with this.
We might find other variants of MVC on the web. However, these are the ones I find more interesting and/or relevant for my work.
Nevertheless, the patterns I referenced here were created for a context of desktop applications and/or rich clients and therefore they don't always fit 100% to the Request/Response paradigm.
If you are doing Enterprise Cloud Applications and you are using MVC, most likely you are actually using something closer to MVP, but in any case, my point is not about following a specific variant of MVC or know all the names and being annoyingly strict with it, my point is that we should learn from all of them, so we can use and adapt as we need. The end goal is, as usual, low coupling and high cohesion : separation of concerns.
1979 -- Trygve Reenskaug -- MVC XEROX PARC 1978-79
1979 -- Trygve Reenskaug -- MVC
1987 -- Joelle Coutaz -- PAC, an Object Oriented Model for Dialog Design
1996 -- Mike Potel -- MVP: Model-View-Presenter: The Taligent Programming Model for C++ and Java
2000 -- Jason Cai, Ranjit Kapila, Gaurav Pal -- HMVC: The layered pattern for developing strong client tiers
2003 -Trygve Reenskaug -- The Model-View-Controller (MVC): Its Past and Present
2004 -Martin Fowler -- Presentation Model
2005 -- John Gossman -- Introduction to Model/View/ViewModel pattern for building WPF apps
2006 -- Martin Fowler -- Supervising Controller
2006 -- Martin Fowler -- GUI Architectures
2011 -- Mārtiņš Tereško -- Architecture more suitable for web apps than MVC?
2017* -- Tracy-Gregory J. Gilmore -- Never the twain shall meet. The tale of MV*
2017* -- Tech notes -- MVVM vs MVP vs MVC: The differences explained
2017* -- Wikipedia -- Model--view--controller
2017* -- Wikipedia -- Presentation--abstraction--control
2017* -- Wikipedia -- Model-view-presenter
2017* -- Wikipedia -- Hierarchical model--view--controller
2017* -- Wikipedia -- Model--view--viewmodel
2018* -- Wikipedia -- History of the graphical user interface