When it comes to the decision where you need to choose your macro-architectural pattern for your application, then it usually ends by choosing one of the three most popular:
- Model View Controller (MVC)
- Model View Presenter (MVP)
- Model View ViewModel (MVVM)
All of those are great and do some type of a job better than other, but like everything in the life, they also come with some pros and cons. Well, that is the main reason why we have several macro-architectural design patterns.
Of course, you can write your application without of any macro-architectural patterns, but trust me you do not want in the most of cases.
In this article, I will try to explain briefly all of them and give some comparison between each of them.
Model View Controller (MVC)
This is the most known design pattern and I guess that all of you have heard of it and probably used it one of your projects. Originally it was developed for Smalltalk GUI applications, but it became so popular that now you have a lot of web frameworks that are using this pattern. The original idea of MVC changed during the time since it was used in so many different languages and frameworks. So, now you have MVC implementations in some frameworks like Spring MVC, .NET MVC, Laravel, etc. The idea behind MVC is to split the system into three main layers.
The model represents a state of the system which we can change through system operations. Usually, domain models, use cases, abstraction, and other business logic.
The view represents user interfaces like HTML, XML, etc. In MVC pattern View monitors the model for any state change and displays updated model. Model and View interact with each other using the Observer pattern.
All inputs from user interactions are directed to the Controller first. Controller coordinates between model and view. It gets user interaction and sends it to the model to apply some business logic. There is one to many relationship between Controller and View. This means that one controller can handle multiple Views if needed. We can represent that by the diagram from the above.
- Separation of concerns
- Testability – Controller is usually tied to the framework, language or some library and it can be hard to unit test it.
- Modularity – There is a tight coupling between controllers and views. If we change the view, we have to go back and change controller.
- Maintenance – Over time more and more code starts getting transferred into the controllers making them bloated.
Model View Presenter (MVP)
MVP is quite similar to MVC, but there are some key differences and advantages over it. We still split our system also into three layers.
It is the same as on MVC. The model represents set of classes about business logic.
All user events are directed to the view. The key difference is that here we have Passive View. Passive View is an abstraction over User interfaces. The view gets user event and sends that event to Presenter. The view has the reference to Presenter and View is not aware of the model.
It is similar to Controller from MVC, but there is no tight coupling with View since View is just an interface. This usually solves all testability issues. Ideally, Presenter should not have any dependency on some framework. Why? Testing. It is usually a way easier to test the app without any external dependency, except of course its core language. Presenter calls Model to manipulate data and Model returns data to Presenter. The presenter then updates View. There is one to one relationship between View and Presenter. So each View in our application can have only one Presenter related to it.
MVP is great since testing is even easier than with MVC. We have great separation of concerns since our presentation and business logic are loosely coupled. It is really easy to maintain applications with MVP since codebase is well structured and defined. MVP has a few problems, like too much boilerplate code and Presenters can collect a lot of business logic over the time.
- Easier testing
- Separation of concerns
- Maintenance – Presenters are prone to adding additional business logic over time. A lot of interfaces and classes can create a large codebase that is difficult to understand.
- Boilerplate code
Model View ViewModel (MVVM)
Model is the same as with MVC or MVP, just domain model, business logic and abstractions. Model returns result to ViewModel and then via data binding view is updated with the latest data. There is some kind of observable relation between View and ViewModel, so MVVM goes very well with Reactive Programming.
User input starts with View and then it goes to ViewModel and from there it goes to Model. When we look at the MVVM diagram above it looks very similar to MVP, except one key difference. The key difference between the MVP model and the MVVM lies in the way they update the view. The MVVM uses data binding to update the view whereas the presenter uses traditional methods to update the view.There is one to many relationships between View and ViewModel. View have the reference to the ViewModel and View is not aware of the Model.
It is responsible for exposing methods, commands, and other properties that help to maintain the state of the view, manipulate the model as the result of actions on the view, and trigger events in the view itself. ViewModel has no information about the View. There is many to one relationship between View and View-Model means many Views can be mapped to one View-Model
MVVM is great since it reduces boilerplate code and it offers easier testing also. The main problem with is also maintenance since it is not prone of collecting additional business logic in ViewModel.
- Easier testing
- Less boilerplate
- Maintenance – Usually when as your application grows also your view model grows. You start adding more logic here and it does much more than it should.
There is no silver bullet. Each of those architectural patterns has pros and cons. Their common goal is to create a separation of concerns between business logic and user interfaces logic. The good thing about those is that you help you to create well structured and organized codebase.
You need to fully understand those patterns so you can decide what pattern fits best with your use case. It is really up to you, your application and your team. You might also notice that all of those architectural patterns have common cons and that is maintenance. Maybe we should talk about how to solve that in the next blog post.