The model view controller architectural pattern is used in presentation logic to separate state (the model), layout (the view) and behavior (the controller).
Related concepts are model view view-model (MVVM) and model view presenter (MVP).
Problem
The presentation layer must do several things at once:
-
Show the user interface on the screen: not only functional components but also images and labels of a visually appealing user interface
-
Accept user input and handle user actions, perhaps with animations and other attractive transitions
-
Process the user input, translating the actions into the domain layer
-
Translate the domain layer responses back into feedback on the user interface
When user interfaces grow in complexity, the presentation layer code may become unmanageably complex. For example, the React code in src/ui/ShoppingList.jsx
of the React team shopping list already has over 150 lines of code even though it is simple and unattractive. [1] Separating the shopping list component into sub-components (e.g., list viewer, list item and a new item creator) will spread these lines of code over smaller and more manageable files. However, the complexity arising from the combination of tasks that each component performs will continue growing.
Solution
The model view controller architecture (MVC) is one of the oldest architectures for graphical user interfaces. [2] The idea of MVC is to separate the user interface into three responsibilities:
-
Model: Responsible for storing the underlying data and handling update commands on the data (i.e., the model holds the data shown in the component)
-
View: Responsible for translating the data in the model into a visual representation (i.e., the view makes the component ‘attractive’)
-
Controller: Responsible for validating and translating user inputs into update commands for the model (i.e., the controller handles the behavior)
These three components work together to handle user input. The controller accepts input, updates the model, which the view renders for the user:
In practice, these responsibilities are imprecise guidelines. The MVC architecture comes from desktop graphical user interfaces where these responsibilities are sharply delineated. [3] On the web, the differences between the responsibilities are less precise. Still, the fundamental principle of MVC remains: as far as possible separate the visual layout, the input processing and the underlying manipulation from each other.
The terms model view view-model (MVVM) and model view presenter (MVP) are related approaches for separating responsibilities in a user interface. In MVVM, the framework provides a single system controller for all pages. The view-model provides a simplified mapping of the model. The view-model is used by both the view and the system controller. In MVP, a presenter replaces the controller. Rather than the controller performing a one-way update of the model based on user input in MVC, the presenter performs a two-way translation from user input into the model and then an appropriate update on the view.
Implementation
Both Angular and React support MVC architectures. They make it easy to separate responsibilities in the presentation layer.
MVC in Angular
Several features in Angular directly map onto MVC concepts:
-
The
ng generate component
command generates a TypeScript file for handling user input (i.e., the controller) and an HTML template file to create a visual layout (i.e., the view) -
The
ng generate service
command creates a separate TypeScript file that is independent of any component. Non-graphical application logic belongs in services (i.e., the model).
Applying the principles of MVC in Angular requires consideration of the purpose of any given code. Identify the responsibility of each line of code and then place that code into the appropriate files:
-
Place the layout, presentation, styling and visual logic in the template files.
-
Embed user input handling into the component’s controller.
-
Place code that does not directly reference to a user interface component in service files (e.g., code that handles communication with a web API).
MVC in React
React does not provide a recommended structure for MVC code. Instead, it provides a general-purpose framework to assist in developing MVC applications.
Some strategies to improve the separation of responsibilities in React code include:
-
Extracting code into separate files (e.g., moving code that communicates with a web API into a ‘service’ or ‘helper’ module)
-
Ensuring that
render()
remains a simple function that only produces HTML -
Separating event handling callbacks from component state update in separate sections of the same file, or in distinct files
-
Using sub-components to handle the UI’s visual features, and higher-level parent components to act as controllers
-
Implementing the visual sub-components as React Function Components: components that only return a JSX, but do not have any internal state
One popular approach to applying MVC principles to React is the use of Redux. Redux uses a store and reducers that together provide a systematic way of implementing the model of MVC. The introduction of Redux into a project will increase the initial complexity. However, Redux helps isolate the behavior and state of the presentation layer from the purely visual responsibilities of the presentation layer. This additional isolation ensures a more maintainable system, by reducing complexity in the long-run.
It helps to be familiar with both Angular and React. Good React code is similar to an Angular project. Conversely, it helps to draw inspiration from React when writing good Angular code.