MV* Architecture Explained
The MV* architecture is a common software architecture design pattern that has been made popular by web frameworks like Ruby on Rails and AngularJS with the rise of single-page applications. MV* is motivated by the separation of concerns (SoC) design principle as it splits up the application into three main components and defines how the components interact. This reduces the coupling between components and allows each component to be independently developed, tested, modified, reused and scaled. MV* is an umbrella term that encompasses variants of this architecture pattern, and they differ in terms of the type and level of coupling between the components.
The first two components in MV* are the
- Model: the business logic and data layer, mainly responsible for handling application state (computations, database)
- View: the presentation layer, mainly responsible for representing the user interface (structure, layout)
In essence, the Model specifies what the data is and the View specifies how the data is to be presented to the user. What happens when the data needs to be changed when a user interacts with the interface? How the Model and View interact with each other is determined by the third component, which depends on the MV* variant in question. In this post, I will discuss three popular MV* architectures, Model-View-Controller (MVC), Model-View-Presenter (MVP) and Model-View-ViewModel (MVVM), and highlight the main differences between them.
Model-View-Controller (MVC)
In its original form, the MVC has a Controller that controls the logic to handle user input. It processes input from the View and sends updates to the View and Model. The diagram below shows the interactions between the components.
Let’s walk through the interactions with a simple example. The bullet points correspond to the numbers in the diagram. Suppose an e-commerce application allows users to add items to cart.
- The user clicks on an ‘Add Item to Cart’ button in the application.
- The View forwards the event to the Controller.
- The Controller then informs Model to update the list of cart items in the application state.
- It could also inform View to update and display the ‘Item added successfully!’ message at the top of the screen to the user.
- After the Model has updated the list of cart items, it notifies the View that the list has been updated.
- The View then queries the Model to get the updated list and displays it to the user.
A quick Google search shows that there are slight differences in how the MVC interactions are defined, but in general they share a common property: the View interacts directly with the Model in order to get the updated state, so the View needs to know information about the Model. Also, the View and Controller are tightly coupled since the Controller needs to know information about the View in order to tell the View how to display.
Model-View-Presenter (MVP)
MVP attempts to reduce the coupling between the components as mentioned in the previous paragraph. The Presenter is now the middle-man between the View and the Model, so the View does not need to know information about the Model. It presents the input from the View to the Model and then present the response from the Model to the View. The diagram below shows the interactions between the components.
There are two main differences in the View-Presenter interaction as compared to the View-Controller interaction. Firstly, the View is more lightweight in MVP as the presentation logic is handled by the Presenter. (Note that the degree of presentation logic in the View may vary on application and implementation.) Secondly, the Presenter now interacts with the View via an interface instead of directly, so it no longer needs to know information about the View. Rather, the Presenter tells the View what to display, and the View that implements the interface will display it.
Let’s walk through the interactions with the same example. The bullet points correspond to the numbers in the diagram. Suppose an e-commerce application allows users to add items to cart.
- The user clicks on an ‘Add Item to Cart’ button in the application.
- The View forwards the event to the Presenter.
- The Presenter then informs Model to update the list of cart items in the application state.
- After the Model has updated the list of cart items, it passes the updated list to the Presenter.
- The Presenter then tells the View to display the updated list as well as a success message via the interface methods
showCart(List<String> cartItems)
andshowMessage(String msg)
.
The above diagram and example illustrates the Passive View variant of MVP, where the Model always passes the updated state to the View through the Presenter. There is also another Supervising Controller variant in which the Presenter passes the Model to the View for data binding to occur, such that simple state updates can be done without the Presenter. However, complex updates will still need to pass through the Presenter.
Model-View-ViewModel (MVVM)
The Supervising Controller variant of MVP brings us closer to the third MV* architecture, MVVM. Similar to MVP, the View is lightweight while the View Model handles the presentation logic. But more than that, the View Model also encapsulates the state of the view and interacts with the View via data binding, commands and notifications. We can think of the View Model as a subset of the Model, and so only the relevant state data will be exposed to the View. The diagram below shows the interactions between the components.
Let’s walk through the interactions once again with the same example. The bullet points correspond to the numbers in the diagram. Suppose an e-commerce application allows users to add items to cart.
- The user clicks on an ‘Add Item to Cart’ button in the application.
- The View forwards the event to the ViewModel (via a command).
- The ViewModel then informs Model to update the list of cart items in the application state.
- After the Model has updated the list of cart items, it passes the updated list to the ViewModel. The list of cart items in the ViewModel is now updated and the display in the View is updated accordingly due to data binding.
Note that the View Model no longer needs to tell the View to update since the state is synchronised automatically by the data binding. This also means that the View Model no longer needs to know information about the View or any interface, hence reducing coupling.
Conclusion
In this post, we have explored the motivation behind the MV* architecture and delved deeper into three major variants, MVC, MVP and MVVM. In writing this post, I have gained more clarity in how the variants differ and I hope that in reading this post, you did too.