The MVP (Model View Presenter) Architecture

and how it relates to the MVC and all his variants

The Taligent proposal for a new architectureIn his primordial form the MVC pattern had the main intent of modularize a graphical application or a graphical component in three main sub-units each with a separate set of concerns. The value of such division is to improve code clearness of intent and thus a higher maintainability.

In the earlier MVC we had a complete circle in which the controller processed the input and fiddled the model to change its state, this change is messaged to the view that update itself and waits for further interaction from the user which will be passed to the controller and so another cycle begins.

Our typical everyman MVCIn the earlier MVC we had a complete circle in which the controller processed the input and fiddled the model to change its state, this change is messaged to the view that update itself and waits for further interaction from the user which will be passed to the controller and so another cycle begins.

I would argue that even if the system is now more modular, the three components are still coupled. In fact they have a cyclic dependency that theoretically is a bad symptom if you are looking for low coupling.

With time the MVC evolved, especially thanks to the web and the Struts 2 framework it morphed into a horseshoe, lowering coupling between the model and the view through the use of more generic ways for the view to retrieve data from the model, adding a layer of indirection between the two. This way you can switch between views and expect a common interface.

The MVP architectural pattern was born about 20 years later and featured a lot of further modularization of the MVC beyond separating the model and the view even further.

It originally replaced the controller with the presenter and added Commands, Selections and the Interactor in the data flow. The presenter is seen as a collection point of events. The Interactor is placed between the view and the presenter and is a filter that turn raw interface events like “user clicked the save button” into user intentions by contextualising them. The Commands specify the available operations one can perform on a specified Selection. Selections answer to the question “how do I specify my data?” This using a DDD repository would translate for example in a method like findByColour so I can retrieve a set of object to operate on all of the same colour.

According to this early model, the view still communicates with the model. Of course using a distributed notification system is nice but it still requires a data access from the view. This means? Yep, you guessed it, coupling between the model and the view. This is sooo practical right? I want the name of a user displayed on the screen, I just write

public void displayUser() {
   print(model.getUserFullName());
}

and don’t forget we also have mobile user who don’t want to see the full name because they have small screens so it’ll be enough for them to print the first name.

public void displayUser() {
    print(model.getUserFirstName());
}

Right? Except now we support Chinese, Spanish and Russian and the API for getting the name changed. The concept for name is more generalized and specialized at the same time: there is longName, shortName, surname, patrnonimic, name[0], name[3], etc. We update the mobile view but we forget to update the web view and so our view is partly broken. The interface guys try to fix it but they don’t have permission to do so because displaying a legal name in the invoices is not part of their knowledge, it’s part of the domain knowledge. We mixed some domain knowledge in the views. What if we did

public void displayUser(String name) {
    print(name);
}

we would have a view with most of the specification left at deeper level, so that one would have to write at least an if statement to distinguish between different view types. This would achieve full decoupling but would leave some view concerns inside the model. A good compromise could be to establish a Name concept at the presentation level and have it shared as Ubiquitous Language term.

public void displayUser(Name name) {
    print(name.short());
}

and in the invoice view:

public void displayUser(Name name) {
    print(name.legal());
}

This way a reasonable amount of knowledge is expected to be shared by everyone, leaving much of the detail on how a legal name is formed to some domain class. Now the model can change abruptively and the only thing that gets changed would be the presenter, views can remain completely unchanged.

Selectors are really some facilities built upon repositories like the aforementioned findByColour that could be something like this:

public List findByColour(String aColour) {
    Colour colour = new Colour(aColour);
    List allIceCreams = getIceCreamRepository().fetchAll();
    return  allIceCreams.stream()
        .filter(iceCream -> iceCream.colour.equals(colour)
        .collect(Collectors.toList());
}

Commands in their simplest form are a list of things I can do with the data I gathered. When I have all my red coloured ice-creams I want to put them in the truck. I extend a Command interface for doing this:

public class LoadIceCreamCommand implements Command {
    private final List iceCreams;
    private final Truck truck;

    // some factory methods to create a proper command
    // and populate the attributes above...
    
    @Override
    public void execute() throw IceCreamLoadingException {
        truck.load(iceCreams);
    }
    
    @Override
    public void undo() throw UndoException {
        truck.unload(iceCreams);
    }
} 

Commands are thus a finite set of classes each of which contains domain knowledge. They directly manipulate domain classes and can be passed around sufficiently easily. It’s worth to note that the command does not contain logic to select data from the database. Whether we chose the ice-creams by color or by flavour is totally irrelevant to the command. LoadIceCreamCommand can load any set of ice-creams into any truck. A cons is that we have to make explicit whether these arguments are already validated (or we can decide to double-check.)

Let’s put everything together to see how the whole system works and let’s also try to interpret this pattern with DDD.

Iscream is a food firm that sells ice-creams, it’s business mission statement is to scare his customer in as many ways as possible. One of the things this company does is to sell ice-creams of one flavour instead of the requested flavour, and it tricks customers by swapping flavours with the same colour like mint and pitachio or alemond and butter pecan.

The presenter is a receptacle of business methods, and is also at application level, so is a natural place to initiate things like loadSimilarColorOnTruck presented here as it was written with the java Spring framework

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class IscreamPresenter implements UserEventListener {

    @Transactional
    @PreAuthorize("hasRole('ROLE_EMPLOYEE')")
    @Override
    public void loadSimilarColorOnTruck(String aTruck
                                        String aFreezer) {
        Truck truck = new Truck(aTruck);
        Freezer freezer = new Freezer(aFreezer);

        List iceCreamsTruck = truck.iceCreams();
        List iceCreamsFreezer = freezer.iceCreams();
        
        Colour matchingColour = Utils.findMatch(iceCreamsTruck, iceCreamsFreezer);
        List iceCreamsTruckColour = truck.findByColour(matchingColour);
        List iceCreamsFreezerColour = freezer.findByColour(matchingColour);
        Flavour firstTruckFlavour = iceCreamsTruckColour.get(0).flavour();
        Flavour firstFreezerFlavour = iceCreamsFreezerColour.get(0).flavour();

        List iceCreams1 = iceCreamsTruckColour.stream()
            .collect(Collectors
                .partitioningBy(i -> i.flavour() == firstTruckFlavour));
        List iceCreams2 = iceCreamsFreezerColour.stream()
            .collect(Collectors
                .partitioningBy(i -> i.flavour() == firstFreezerFlavour));

        commandStack.push(new TakeIceCreamsCommand(iceCreams2, freezer);
        commandStack.push(new UnloadIceCreamsCommand(iceCreams1, truck);
        commandStack.push(new LoadIceCreamsCommand(iceCreams2, truck);
        commandStack.push(new PutIceCreamsCommand(iceCreams1, freezer);
        commandStack.push(new CommandSeparator("Load Similar Colour On Truck");

        iscreamView.truck().displayIceCreams(truck.iceCreams());
        iscreamView.freezer().displayIceCreams(freezer.iceCreams());

    }

}

Here we see how everything fits together, the presenter, selectors, commands, the view and the model. We leave leave the interactor for a part two…