MVC or MVP? Why not both?
Model-View-Controller, Model-View-Presenter. A lot of people use these terms interchangeably. You can see one or the other in the description of virtually any web application framework. But is there actually any difference between these two? Is Controller different from Presenter in any way or are they just a different name for the same thing?
I used to think of them as synonyms. If someone asked me to describe the MVC or MVP pattern, I would describe them the same:
- Model – the persistance layer. How and where the data is stored and retrieved.
- View – the display layer. Where the data is structured for use by the user(intentionally avoiding the word „presented“). Basically the application's templates (or ocassionally API definitions).
- Presenter/Controller – the logic layer. Desides what how to react to the user's request, what data to retrieve from the Model and how to present them using the appropriate View.
For the Model and the View, the responsibilities are pretty clear follow SRP – Single responsibility principle. However I take issue with the Presenter/Controller level. I believe they are doing two different things and it is where the confusion about the names comes from. What I believe is:
As the name suggest, it is in control of presenting things. The whole point of presenter is to normalize the inputs and deside the outputs. Here is what I mean:
If you have any application more complex than a simple one-page portfolio website, you will invariatly have more than one point of access to your application. Those might be the following:
- You will have the standard HTTP request from the browser of a website visitor.
- You might have some CLI script running on your application server.
- You might have a cron task running either as a hidden HTTP request or as CLI script.
- Your application might implement an HTTP or other API for communication with other applications.
The problem is, even if all of those requests to your application want to achieve the same thing, they will look completely different. Not only that, each and every one of them will expect a different output. And this is where you need the presenter. To normalize the input requests to the same request struct and after the request is processed to decide how the output response should be formatted by choosing the coresponding view.
If the presenter is not processing the request and has no bussiness logic in it, something or someone else has to. And this is the principal point of controller. The controller has to decide what to do with the normalized request from one of its presenters. The controller is request agnostic – that means once you are in the controller, you cannot differenciate whether the original request came from the website, CLI or some other source. And if your application is well designed I don't believe you need to have that knowledge. In either case, the controller is the place where all the bussiness related logic happens as it is the layer that comunicates with the model. And based on your model design the share of bussiness logic is skewed in favor of the controller or the model. However all the businness logic in encapsulated in the controller-model pair.
As this might not be completelly clear from all the abstract talk, let me show what I mean on an example:
I need to be able to add a user account to the application. For addition I need 2 pieces of information:
- e-mail – also serves as username
I want to be able to do it from 3 different access points:
- The web application – here it is obvious. If you want to register, you fill some kind of a registration form on the website itself.
- REST API – You need this because you also have a mobile app (not a mobile friendly version of your website) and you want users to be able to use the same credentials and accounts. You decided that the way to go is to have a private REST API that you mobile app can use to comunicate with the server.
- CLI – However the question remains, how do you create the first app admin? By direct insert into the database? Maybe the better way is to process a CLI request from the server with higher privilege. In the end, if somebody has access to your servers commandline, you have more pressing concerns than somebody able to add users to your app.
The logic of adding a user is quite complicated and you don't want to have to write it more than once. Especially since you want to keep DRY and be able to easily adapt to any changes to the registration process.
So you write 3 presenters, each for the different entry point to your application.
namespace Presenters\Web; class UserPresenter – Takes in a POST request from your application form. It validates the form for known WEB attacks (to do with sessions, cookies etc.), check that the 2 filled passwords are the same and extracts the 2 values you are actually interested in: e-mail, password. Then calls the
createUser($mail, $password) method on your
UserController Based on the response from the controller the presenter then decides maybe to redraw the form, if the there was some problem with the one of the values, will show a successful registration message or announce the user that for sume reason the request could not be finished (server fault). And of course redirect to some other page, so that you cannot F5 resend the form. In either case, as a response, the presenter will serve HTML page
namespace Presenters\Rest; class UserPresenter – also takes a POST request but this time, the body will be formatted as a JSON message. In contrast to the web presenter, there is only one password (you can check that the password are the same in the app and not use up the web traffic) and the presenter has to check, that the request came from a trusted source – this is a private API and should only accept requests from known senders. This check should occur as soon in the application as viable and presenter is the perfect way for it. once validated, it will again call the
createUser($mail, $password) controller method and base on the response will send a response of its own with wide variety of HTTP statu codes like:
- 201 – sucessful creation of user
- 444 – could not create user for some reason (probably invalid inputs)
- 429 – under load, retry later
In this case, there might be no response body or there might be a JSON response body. However unlike the web case, there is bigger emphasis on the content of the HTTP headers that have to be actually treated.
namespace Presenters\Cli; class UserPresenter – takes in script parameters and outputs to the console. In this regard it is very different from the other two, however the request is also processed by the
createUser($mail, $password) controller method.
As you can see from the example, there is an emerging pattern in the collaboration between the presenter and the controller. More about that pattern will be in the next article.
Continues by MVPC – Implementation