WTF Does Model-View-Presenter Actually Mean?
This article is intended to be an fairly comprehensive, but still accessible explanation of three layer architectures (emphasizing Passive View), and why you might want to try using something similar in your projects. It turns out that the why behind this question is quite important.
The most important shift in my understanding of software design and construction, was when I realized that software could be represented as objects, with consistent names and responsibilities. That may sound like a trivial and obvious point to others, but it certainly wasn’t for me as a beginner. Instead, I tended to see software development as a game of remembering disparate lines of commands; neither perfectly suited for computers nor humans. This was unfortunate, as this rather lifeless and rote perspective is quite unsuited to my brain. I also did not have any grasp of separation of concerns, hammering out unmaintainable “God Objects” (an Object/Class which does too much) at a steady pace. Something in me knew that this was not optimal (I’ll get to why later), but I quite literally couldn’t conceive of a better way to structure my code.
About a year into coding on my most familiar platform (Android), I caught a glimpse of an open source project which loosely organized it’s files into an MVC style architecture. It was a bit confusing what the names meant, but I immediately tried to apply a similar style to my first application, which necessitated rebuilding it from scratch. As I began to try and separate my code into distinct layers (not even knowing what the end goal was), things started to drastically change for me. I was no longer thinking of my software as disparate collections of commands, but rather as Objects, with specific jobs (responsibilities), that come alive and talk to each other at runtime. All of the sudden, I saw (quite literally) how my architectures could be abstractly represented as boxes and arrows on paper (read: UML), or as 3D models in my head. I finally had a way to visualize, design, and reason about my software which took advantage of more natural ways of thinking.
I mention this as both a preamble and a warning to you, dear reader, as we don’t all think about software the same way. My goal is not only to throw a bunch of descriptions of key concepts such as, separation of concerns, cohesion, coupling, and Model-View-Presenter at you. There will be some of that, but I will attempt to provide you with what may be a brand new way to think about your software; should you still be unclear as to what Object Oriented Software Design means.
Although this guide is meant to be somewhat language agnostic, I’ll assume that you’re somewhere in the realm of OO languages, and you’re building something which talks to a user. Mileage may vary otherwise.
What’s In A Layer?
If you’ve ever spent an evening asking the google about “Software Architecture”, chances are that you’ve heard about one of the classic three tier architectures such as:
- MVC: Model-View-Controller
- MVP: Model-View-Presenter
- MVVM: Model-View-ViewModel
While this guide will focus on Model-View-Presenter (Well, Passive View, to be exact), I would like to spend a moment explaining the two things I believe you really need to understand about all of these variations:
Firstly, the difference between these, and other similar styles (was geht ab, MVI?), is primarily in how they interact and depend on each other. The responsibilities themselves remain fundamentally the same whatever you call them.
Secondly, there is a somewhat objective metric to determine whether or not you truly have something like an MVP/MVC/MVVM Architecture. This metric, referred to as cohesion, will help you to evaluate whether your designs (and the designs of others), are actually helping you achieve the ultimate goal of separation of concerns. We’ll address why you want SoC throughout (this article is secretly just a big lecture on SoC in truth).
Whatever name we refer to, we’re really talking about a Single Responsibility (at the architectural level), or a set of Responsibilities all converging on one broader responsibility (at the implementation level).
A View, is a View, is a View
You’ll notice that a common theme among three layer architectures, is having a View Layer and a Model Layer. We’ll discuss the View for a moment, as I find it to be the least ambiguous entry point into discussing the three generic responsibilities which define a three tier architecture.
Objects in the View Layer are responsible for direct interaction with the User Interface. Does it set the text of an onscreen widget? Can it make an underline red when some smarter part of the architecture determines it to be so? That would be a View. In fact, I’d treat the name “View” as essentially a pseudonym for “User Interface”. Exactly what goes in the View may differ slightly between styles (for example, whether the View listens to touch/click/key events or a separate controller listens), but it always relates to the UI.
To me, it follows logically that since almost every practical application conceived of, needs to have some kind of UI, we can safely treat such classes as being a necessary and regular part of an architecture. This is also true of the Model (I actually prefer the word Data, but I guess the Model ‘Models the Data’), as it’s hard to conceive of an application which doesn’t need to work with and store information of some kind.
We’ll explore the three layers in detail soon, but the key takeaway is the realization that while we can argue definitions of names until the pocket protectors start flying, the primary concerns of almost any application with a UI, remain fundamentally the same.
Sticking Feathers up your butt does not make you a ViewModel
I hope you’ll forgive the not so subtle Chuck Palahnuik reference above. The last thing we’ll discuss before diving into MVP, is the way in which we determine whether something that may be called MVP/MVC/MVVM/MVPCVMCVM (and derivatives therein), actually achieves the entire point of having distinct layers; that being separation of concerns.
Let’s say we’re looking at two files, from two different developers, both called LoginView (the files, not the developers). My first thought would be, these files must handle drawing the User Interface for a typical login screen.
Upon having a look at LoginView from the first project, we see a fairly typical scenario:
- Code which inflates and binds to a User Interface.
- Code which binds a listener to a login button, but when this listener is called, it just tells some other object which observes/depends on the View.
- Code which can show a loading bar, grey out the login button while loading, and display an error message if the input is invalid. Again, this flow is dictated by some other ‘smart’ object.
- Overall, LoginView contains code snippets which may have different responsibilities at a micro perspective (listening for UI events and drawing the UI are not the same operation), but they all have something to do with the UI (macro perspective).
- Importantly, the LoginView doesn’t actually make decisions about UI events, it’s just the dumb but efficient renderer of the UI.
At this point, I like what I see, and don’t find any reason to argue that this is most certainly a View of some kind; and nothing else. Unfortunately, upon opening the next Developer’s LoginView, we get much the same UI implementation, but a lot of extra baggage on top of that:
- Copious amounts Control Flow Logic. The View decides for itself when to show loading screens or set font colors, and it handles UI events and validation with masses of if/then clauses.
- It depends on a concrete (not an interface/abstraction) Network Adapter (usually a bridge to a cloud database of some kind), and a concrete local Database to cache the user’s session data; even though these two backend services return the same kind of data. (Repository Pattern… what’s that nonsense?)
- Plenty more Control Flow Logic to handle responses from the Network Adapter, and the local Database, including error cases.
Hopefully I’ve painted a picture that tells you example #2 has not actually achieved SoC (quite the opposite really). The View has a hand in just about everything, and has a particularly expansive amount of logic to handle this overflow of responsibilities.
I feel, know actually, that the following principle has far deeper implications than software design, but I’ll try to keep my philosophical tangents practical here:
Cohesion, in the context of Software Architecture, is a term which describes the degree to which parts of your implementation (read: line(s) of code), belong in their given function, class, layer, or module of a given application. There are different ways to talk about cohesion, but my preferred choice is that a Class which contains many things that don’t belong, is said to have weak-cohesion; whereas a class which follows one clearly defined responsibility, and everything within it is related to that responsibility, could be described as having strong-cohesion. I’ve also heard the terms High/Low Cohesion, in case you run across those them.
This can be difficult for some to accept, as I’ll admit that we must necessarily agree on and define what a View is before we can say that something within it doesn’t belong. I can expand on this in comments, but my practical counter argument is that: while these definitions are admittedly somewhat subjective, I maintain that it’s easy to tell when a definition doesn’t make any objective sense. That would be a UI Object administering a Database, for example.
To reiterate, LoginView #1 contains implementation, all of which unambiguously has something to do with the UI. LoginView #2 has implementation which unambiguously has a part in almost every responsibility present in the problem domain (the data and functions that an application requires). It makes decisions, coordinates the UI, coordinates the Model (Network/DB), and is utterly antithetical to the idea of SoC. Therefore, LoginView #1 has strong-cohesion, and LoginView #2 has weak-cohesion (almost like I planned it that way).
My goal with this section, is simply to give you a tool which you can use to evaluate whether something actually fits the definition it is given. If it doesn’t, we either have a bad definition, or an Object which has so many qualities/responsibilities that a clear definition isn’t possible. Whenever we see this occur, there’s a very strong chance that some refactoring is in order.
**Note: There are certainly special occasions where an Object/Class must have a hand in multiple layers and break SOLID Principles to some degree. They’re principles, not rules, for a reason.
Model-View-Presenter (Passive View)
Although I think I do a decent job at it, I must implore you to check out the work of Martin Fowler on GUI Architectures. Martin has more experience coding that I have living, and his blog on “Passive View” was well and truly the first time I read an explanation of it that made sense. I link to his blog in acknowledgements.
I’ve dabbled with a few styles over the past three years, but something about MVP seems to keep it in place as my standard preference for building applications. I’m not saying it’s the best (no such thing, all architectures have benefits/drawbacks), but I find it to be flexible, un-monolithic, and unambiguous. We’ve already discussed the View and Model somewhat, but I’ll spend a moment defining MVP (Passive View) from a bird’s eye view:
The View is the dumb but dutiful manager of the UI. The benefit of keeping it dumb (off-loading as much logic to the Presenter as practically possible), is that we don’t need to test it much. We can, and probably should now and again, but as long as the Presenter is actually doing the thinking, your View’s functions should generally be easy to write and hard to screw up.
Model is a term which gave me, and many others (I hear this often during my Q&A sessions), a lot of confusion. At first I thought the Model was basically a pseudonym for backend Services like Databases, System Services, Network IO, etc. Then I thought it actually meant “DataModel”, which is a fairly plain Class with getters and setters for primitive data (well, usually primitives), which get’s passed around the architecture like a baseball.
At this point, I actually think both of those things could be said to part of the Model. The Model refers to Classes which have the responsibility of storage and access to structured data. It can be big or small, temporary or persistent, but the functionality must be managing Data in some way.
The Presenter is the Control Center of your application’s front end. Essentially, whenever an event occurs in the View (clicking a login button), or the Model (Model returns result of login request), the Presenter will decide what to do with the event. It can also change incoming data and present it to other layers appropriately. The one thing it should not do, is store the application’s state itself. The Presenter has a bad memory, but it makes up for that by impeccable decision making capability (For a moment I felt like I was describing myself there).
It also should be kept as free from Frameworks and Libraries as practically possible. Since it doesn’t store state or actually make the network/database call (it just tells the appropriate object to do so), you really shouldn’t need many frameworks to build it. A notable exception might be a concurrency library to manage asynchronous interactions between layers, like Reactive Extensions for example. Most Platforms have Futures, Promises, and so forth which can also achieve asynchrony as well; whatever floats your boat.
Rules of Communication
While it helps to breakdown the responsibilities of MVP, we must also consider how these structures should to talk to each other. In my opinion, knowing how these Objects communicate is just as important as knowing what they do.
The View and the Model are like estranged coworkers who work in separate rooms and don’t want anything to do with each other; but rent is due next week and quitting isn’t an option (tabarnak, I’ve been there before). They simply don’t interact with each other. Period.
Instead, they’re both quite happy to talk to the Presenter, and vice versa. They know that as long as they tell the Presenter what’s going on, they’ll get paid. The Presenter is that rockstar coworker who knows the business, knows who’s best at doing what (delegation), and knows how to get along with everyone. Since it makes many decisions and passes messages between it’s surly team members, it is by nature the most complex role (in terms of logic). This is an important note for testing, which I’ll discuss later.
Cool story bro, but how does this actually help me build apps better?
Congratulations if you’ve made in this far; I know the ramble is strong with this one. Let’s quickly recap some main points before finally examining the benefits of SoC:
- Whatever names you pick to describe each responsibility, I would argue that the fundamental division of three layer front-end architectures, is between UI, Logic (Event Handling), and Data. We can divide things further, but this is at least a fairly unambiguous entry point.
- A quick way to tell if you have something like an architecture in front of you, is to look at each part and ask the question: “Does the code in here (implementation), actually belong here (weak/strong-cohesion)?” You must of course first define an Object’s responsibilities, which will generally be related to one of the three I made in the previous point.
- If you’ve separated your application (and features therein) into some form of architecture, and each part is strongly-cohesive to its given responsibility, then you have what is called “Separation of Concerns.”
The most common counter-argument I’ve heard regarding SoC, SOLID Principles, Clean Architecture, and so forth, is rather ironically presented as an argument of pragmatics. In simpler terms, I’ve come across several people who look at these principles as quite possibly being extra work for something that isn’t ultimately necessary. Extra work, as it really can be difficult to build and understand modular architectures (time is money). Isn’t ultimately necessary, as it is true to say that there are financially successful projects out there which follow the big ball of mud style architecture.
I believe the best rebuttal to this argument, is to actually experience what it is like to follow no architecture or SoC, versus following it reasonably well (try it before you diss it). My years in this field may be limited, but I don’t lack for those reference experiences. I didn’t even know what Software Architecture was for the first year. Let me explain how that was:
Trying to change anything was an absolute disaster
One of the primary benefits of separating UI, Logic, and Data, and having them talk to each other via interfaces (abstractions), as suggested by the Dependency Inversion Principle, is that they become easy to change. As long as the interface (publicly visible function declarations and variables) between objects doesn’t change, you can change the Object behind the interface whenever you want.
Ok, I know that last paragraph broke the jargon quota so here’s what this actually means: Let’s say I have a simple MVP note taking mobile app which stores notes on the user’s device (using a Database of some type). One day, my project manager decides that microtransactions are the answer the companies dwindling finances (the market for Note apps is rather saturated, I suppose). She/He then decides that I must implement an option to store notes on the cloud via Firebase (mult-platform remote back end service) for an extra price if the user wants it.
If the project was constructed as I described in the first paragraph of this sub-section (with all the jargon), then all I have to do is: Write a firebase service to talk to the server, and put it behind the original interface that the Presenter would call to save a note (Repository Pattern FTW!). This means that I can extend the functionality of my backend, without having to change the frontend at all. This is possible because saving notes to a disk, and a cloud service, is conceptually the same operation working on the same data. There’s no reason why the Presenter should have to care about that; and having it not care is one of the big secrets of flexible/modular software design!
Moments after I commit to the production branch, I’m informed that we are switching to Amazon Web Services instead of Firebase (doesn’t matter why). While this would be kind of annoying, guess what? All I need to do is: Write an AWS service, and put it behind the original interface that the Presenter would call to save a note. Starting to see a pattern here? Pun intended.
In a parallel universe, I built the same app, electing to handle most of the app’s functionality within one or two unintelligible blobs (God Objects), which depend on concrete implementations for the backend (not interfaces). I’m asked to add in the Firebase service, which sucks because I now need to make my already exceedingly large blob, even larger than before! Even so, I dutifully get to work adding a new dependency and all of the boilerplate to get it working. After struggling to get it to compile, and a quick test deployment on my phone, we commit to production.
“What? We’re switching to AWS? But I just spent a two weeks integrating Firebase in my blob! Now I have to go back through that rats nest and remove the Firebase implementation, just so we can add AWS back in?”
Begrudgingly, I set to work scrubbing the blob of the Firebase Implementation, while trying to slowly work AWS back into the complex logic necessary to save to both services at the same time and ensure their consistency. Unfortunately, AWS has different requirements and I need to change several other parts of the blob, while trying to keep those changes from breaking the blob entirely (which happens frequently). Finally, after another two weeks (half of which was spent fixing errors caused by deleting and modifying existing code within the blob), it appears to be working.
Hopefully I’ve painted a picture which highlights why a person shouldn’t do everything/most things in one class. I can almost guarantee that the time a person thinks they’ll save by not separating concerns, and coding to an interface, will be greatly eclipsed by the difficulties of maintaining a project which straddles the line of unmaintainable.
We don’t necessarily need these principles at all times within all projects. However, my personal goal is to apply them in most projects, most of the time.
Testing was difficult, tedious, and often impossible
Whether your testing goal is to a write few Unit Tests to evaluate application/business logic, or you want 100% code coverage; separation of concerns and applying Dependency Injection (giving an object it’s dependencies, instead of an object creating it’s own dependencies), will be your best friends. I’m simply not yet qualified enough to assert what the best testing strategies are, but at a bare minimum, I like to test any class which has complex logic. In MVP, this will be your Presenter.
The absolute beauty of SoC and DI, is that you can very easily test your architecture, by having a real object (the one to be tested), talk to test doubles:
The Test doubles are basically fake versions of classes which can be generated by various libraries and frameworks. Testing can be done after the fact, but you can start to apply Test Driven Development, which is made easier due to ease of testing overall.
Another benefit to SoC with regard to testing, is that you can safely and incrementally build your projects different layers in a modular way. To paraphrase an analogy given by Robert C. Martin, consider an application, with three layers, and each layer is built by a separate developer. As long as each developer knows what the others require from them (by writing interfaces between each layer, which can be kept quite polymorphic), they can hypothetically finish their layer and verify its integrity through test coverage, independently of the other’s progress! Even though my team generally consists of me, myself, and I, this analogy prompted me to think about designing my various modules (which need not be strictly three layers), in the same manner.
Writing God Objects is really annoying and difficult
I hinted at some of my past misfortunes with God Objects in my previous ramble about SoC making an application easier to build. I figured it might be worth exploring a few other unfortunate things that I dealt with while applying the CtaGaIM principle (Coding to a Gelatinous and Incoherent mess):
- I would look at segments of code written weeks/months ago by me, and not have a clue in hell about what they did (I know I wrote that, but did I really write that?).
- The names I gave things were at best vague, and at worst, unintelligible. Try giving a descriptive name to something which does everything; versus naming something which has one, cohesive concern.
- I hated adding new features for the aforementioned reasons of it being difficult to add new things to an inflexible structure. It follows that I frequently ran into situations where the only way to change my blob to include a new feature, was to rebuild it entirely.
I think you get the picture; fun and rewarding generally wasn’t something I associated with code.
I don’t know how much of that was useful, but I hope it at least gave you some new perspectives and ideas. Please do not mistake me; I have at no point said that is an easy endeavor to learn and follow these principles. Depending on your platform, it can actually be a major pain in the ass to decouple your architecture. Even so, I implore you to respect your craft and constantly try to improve it; You won’t regret it.
I must reiterate the fact that I don’t yet consider myself an expert in this field (neither do I think myself a beginner), and I greatly appreciate thoughtful feedback and discussion. The only flag I’ve really tried to plant with this article (other than SoC), is that I think this field suffers from an overload (technically and figuratively) of jargon, and my rambling and bizarre analogies are an attempt to make this stuff more accessible. Whether they struck you as helpful or obnoxious, please do me a favour and explain why; so that I can do better next time.
Acknowledgements and Further Reading
While my experiences and explanations are my own, many of these concepts were introduced and explained to me through the work of the following great teachers of our profession:
Martin Fowler - Martin’s explanations of the abstract usage and low level implementation details of design patterns, was the missing piece which told me how to actually build these things in code.
Robert C. Martin - Uncle Bob’s (no relation) lectures, content, and books on Clean Architecture and Test Driven Development should be treated like an intellectual gold mine. Each time I re-read/watch his work, I’m able to map it new and ever deeper concepts which have popped up in my career as a developer.