Saturday, October 24, 2020

3.5 MVC Architecture Pattern

 Patterns in software development are discovered when arranging higher or lower-level components into one entity. Some arrangements work better than others, and these get documented and prepared for reuse. The Model-View-Controller (MVC) is one of the most known and used architectural patterns in programming languages. It was introduced to the world of software development in 1970s as a part of the object-oriented programming language Smalltalk. Later, the principles of MVC evolved into a general concept, easily adapted by programming languages, and was enforced into the programming community as part of various frameworks. MVC played an influential role in mainly user interface frameworks, and is still relevant and used in interactive desktop and web applications today.

Note

A framework in programming is a project, providing you with the skeleton code for building an application that can be extended by the programmer to create a specific implementation. It usually comes with already implemented handlers for user authentication, database connection, and similar frequently used actions.

MVC describes the implementation of software components in terms of their responsibilities.

The MVC patterns introduce three object roles:

  • Model

  • View

  • Controller

These three roles enable the separation of concern and independent development of each. Views have the logic for displaying information to the user, and the models encapsulate what that information is. If the model, for example, defines user data, the view renders that data on the display, which can be a web browser or a mobile or desktop application. Any changes that users make on the view are handled in the controller component. The controller takes input, makes changes on the model if necessary, and informs the view component to update the presentation accordingly.

The dependencies between the components govern the behavior of the system. The view depends on the model and obtains data from it, which enables you to develop the model without knowing what will be in the view component. The view component makes development easier, allowing you to have multiple views for different platforms, and it simplifies adding a new presentation view later without changing the model. It also enables you to test business logic without having to worry about the presentation of the data. A model should be independent of the presentation output and input behavior.

The controller sits between the model and the view, and it depends on both, while the model is not dependent on the controller. Typically, there is one controller for each view. For example, a user view that has the presentation logic for user information is using a user controller that handles the communication between the user view and the data model. Controllers receive inputs from the interaction with the view component, which are translated into requests to the model component or view component, or both. In this case, you might even create a user model, representing user-specific information, ways of storing data, and dependencies to other models.

One of the primary features of MVC architecture is the separation between the view and the model, which is considered a very important design principle in software and should be followed every time that your application has some sort of dynamic behavior.

In MVC, the view component is the entry point for a user accessing an application. The view component has the implementation of how the data is presented to the user. Development of the view requires a different set of technology bits than the controller or model components. You may hear the term front end being used to denote something as the application display that is used for interaction with the system. In the case of the MVC, the front end is developed in the view component. Because of the clear separation and difference in technologies, some people specialize in front-end development only, making user interfaces that are pleasant to look at and to use.

The controller and model components are developed using different sets of technologies. In their case, this is referred to as back-end development, and again, some people prefer working with this mindset and technologies, solving different kinds of problems than the ones in the view. The separation between the components enables you to program the code in different programming languages and technologies, as long as you are capable of connecting everything together using the desired tools.

The tasks of the controller are to accept the input and translate that to a request either to the model or view. The model defines the state of an object in the application and implements the functions for accessing the data of modeled objects. It is responsible for registering dependent views and controllers, and notifying related components when internal data is changed. There can be multiple views, representing different displays in the application, and usually, each view will have its own controller.

Consider the flow of requests in the following diagram.

A user interacting with the application view initiates an event that the controller receives as an input (1). An example of an event is a user logging in to the system with their credentials, or it can be registration to a website, providing all the necessary information in the request, or simply changing from one view to another in the application. The controller receives the request that the user initiated from the view and interprets it against the set of rules and procedures that are defined inside the controller for that view. The controller then sends the state change request to the model component, which should manipulate the model data (2). The controller could, for example, send the registration information to the user model after it concluded that the information that the user entered is valid.

After the request was processed, as a result, the controller can ask the view to change the presentation (3). This change can happen to route the successful registration to the home page view, for example, or in general to change the view after a successful or unsuccessful request. The model responsibility is to interpret the requests coming from the controller and store the new state. Communicating with underlying storage technology is mostly implemented in the model component.

As the consequence of the state change, a change-propagation mechanism is initiated to inform the observers of the model that they should update their presentation based on this new state (4). The view component will, after the notification, start the update process and request the new state directly from the model (5). After the response of the module, the view is redrawn using the new data. The view component might also request the module for the state after the controller requested a change on the view. Likewise, you can register the controller components to listen to the change propagations so that they get updated with the new state on changes to the module. When the state of the module controls when something is available in the view or not, then it is the responsibility of the controller to update the view in respect to the module.

Sometimes, it might seem that you are complicating the application with defining a controller, when you could have the logic for controlling requests to the model inside the view component. The reason that you want to keep the controller separate is that if you do not, the view becomes responsible for two different things. The lines between front-end code and back-end code get blurred, and the view becomes more tightly coupled to the model, because it will be very difficult to reuse it with another model without rewriting your code. The controller helps with separation of concerns and makes the view decoupled from the model, resulting in a more flexible design.

The MVC architectural pattern is composed from multiple software design patterns that maintain the relationship between the components and solve problems that MVC introduces by proposing some of its features.

Common design patterns used in MVC:

  • Observer pattern

  • Strategy pattern

  • Composite pattern

  • Factory method

  • Adapter pattern

MVC lets you change how a view responds to user input without having to change or develop another view component. As an example, imagine having a registration screen for your application. The view implementing that screen sends the request to the registration controller. Currently, the registration controller reads the input and translates that to a request for a model that stores a new registration. Later, you decide you want the registration controller to perform a different set of actions, such as validating the input and attaching additional data to the request before it proceeds to the model. MVC enables you to create a new controller with such features and change the view to use this instead.

The relationship between the view and a controller is an example of the strategy design pattern. This behavioral strategy design pattern suggests taking a class that has many different related implementations and deriving all the implementations into separate classes, which are then called strategies. Strategies can be, as the previous example suggests, interchangeable objects that are referenced by a class—in your example, the class implementing the view. This view class, which requires a strategy, works with the strategies through the same generic interface, making the view class independent of the specific strategy class implementation of the algorithm, and is capable of replacing the strategy whenever that is needed.

When your user interface can be combined hierarchically using different elements, such as nested frames and buttons that can be represented as a tree, then you are probably using a structural composite design pattern. The MVC architectural pattern suggests using the composite pattern inside view components. There are other patterns that can be found in MVC, such as factory method, adapter, and decorator, but the main relationships in MVC are constructed using observer, strategy, and composite design patterns.

The benefits of having separated views and models in MVC are obvious, but they also introduce an issue. You can have multiple views that use the same model in your application, so you want to make sure that all active views get updated on a model state change. As seen in one of the previous figures, a change-propagation mechanism is initiated to inform all participants that the state has changed. This mechanism in MVC is typically designed using the observer design pattern, where the view acts as the observer of the model and starts the update procedure when the state changes—that is, when the model notifies the observers.

In this partial UML diagram showing the model component as a class implementation, you can see the required state and method fields. A model stores data of some sort or has at least an option to query it from an underlying storage technology when the view requests it. It also has a list of observers that subscribed to the changes that happen to the model. It implements methods for attaching and detaching new observers, and a method for notifying them on a state change. It must implement methods for responding to data requests coming from the view components.

As the next figure shows, an observable—in this case, the user model—will notify all observers on a state change. If a new observer is required in a form of a view or a controller, it should be easy to attach it to the group of observers.

There are many benefits of using the MVC architectural pattern, but there are also some drawbacks that need to be pointed out.

Benefits of the MVC pattern:

  • Separation of concern

  • Multiple views of the same model

  • Flexible presentation changes

  • Independent testing of components

  • Pluggable components

You can benefit from using MVC because it uses separation of concern by introducing component-based development. Each component performs its own specific role. The view component takes care of the presentation side of the application, the model defines the state of the application, and the controller governs the behavior of user actions against the view and the model. Components can then be developed and replaced independently when needed.

The advantage of MVC is that you can prepare multiple views that use the same model, and you can also change the presentation on those views without affecting any of the related components, if the changes are small. The separation of components allows you to test them independently by mocking a component. For example, instead of using a model that works with a complex database system that needs more time to set up, you could mock that database using some other lightweight implementation that would be sufficient for running tests.

Downsides of the MVC pattern:

  • Increased complexity

  • Excessive number of change notifications

  • View and controller tight coupling

  • Separate controller is sometimes not needed

One of the drawbacks of MVC pattern is increased complexity that comes when there are use cases that require nonoptimal implementation if you want to stay in the frame of MVC definitions. For example, your view has a form that can be explicitly enabled based on some state in the model. The correct way of enabling this in the frame of MVC is that an event is triggered that propagates to the model, then the model notifies observers to fetch the state, and after that, the form is enabled. This is a rather complex process for a simple use case, but because an MVC model does not know about the views directly, it cannot propagate the changes to the views when necessary.

Also, sometimes, there can be a lot of change propagations that do not benefit all the views that use the observed model. Even though the view and controller are separate components, they are closely related, which might complicate separate reuse. Some would argue that a controller separation is not needed, especially when the user interface platform already implements the event handling by itself.

A couple of variations of the MVC architectural pattern have been introduced; they propose similarly to MVC but handle some things differently. Examples of such patterns are the Model-View-ViewModel (MVVM) and Model-View-Presenter (MVP).

Implementing MVC

Now that you know the building blocks of the MVC architectural pattern, it is time to put this knowledge to work. You are going to observe how to build a basic structure of MVC in Python. As you have seen previously, there are three main components—model, view, and controller—that you implement together with connections between them. The important thing is to make sure that these components are not tightly coupled together. The view should be independent from the model; any changes that are done there should not affect other views that relate to the same model. The views and controllers should be easily exchangeable, even during runtime.

You should start designing your program on a higher level, using UML class diagrams that can specify how your program acts. The example covers creating an MVC-based application for creating users and searching for users.

Review the UML class diagram example. The program has one view, UserView, that uses a UserController interface and UserModel. The controller interface is implemented with SimpleController class, which defines create() and get() methods for working with users. The controller talks to the UserModel to store user information, and the view contacts the model after the state has been changed.

Now that the high-level design is represented in UML, you should be able write it all up in a programming language. MVC as an architecture pattern is not dependent on programming language or technology, of course. The components and design patterns are developed differently in different languages, but the idea stays the same. In this case, you write the program in Python, but you may take any programming language and simulate the same style of implementation. First, look at the code for the user model. The model is, as you already know, not dependent on any other component from MVC. Obviously, it can depend on libraries and other modules that help with the functionality implementation, such as database communication. In this case, the code simulates the database by storing the user data in a global variable, but it could be easily replaced by any other technology and the dependent components would not see the difference.

It is time to connect the theory with practice—first, the module component in the model.py module.

The responsibility of the model is to define what a desired object can have and what it can do. What a model can have, or the state of an object, is stored within the user model object. Whatever data is defined on the model usually also reflects how the data is stored in the underlying database of choice. The user model defines two properties, username and email. These two values are set on object creation, initiated from the controller, which gets the values from the input that the user enters to the view component. There are a couple of static methods, get_user() and get_users(). The first method requires an id argument that is used for searching for the user entry. The second simply returns the entire database of users. A third method, store_user, which is not static because it is referencing the current object instance through the "self" object, is used for storing new users, and it calculates a new ID for each entry request it receives. How to store new users depends on the database type. The model is responsible for storing it correctly; the dependent components use the model API.

Note

A static method in programming is a class method that is a part of a class but not related to a class instance directly, and can be called without object creation.

Next, look at the code for the view component inside the view.py module.

The view component in this program implementation is responsible for taking the user input and printing the results. It is, by definition, dependent on the model, but until you come to the point of implementing the observer, it is just a light dependency because of the module import. The view in this case is very simple. It is a CLI display application that takes the user input for the username and email and sends that to the controller for further processing. It also implements a display procedure for showing a user with a specific identification number in the database.

The two methods that communicate with the controller are loosely coupled. Instead of sending an object that the controller would need to know how to interpret, they simply send the input in a string format. The update_display() method is used for updating the view and represents the connection from the controller to the view. When a controller performs an action that the user requested from the view, it calls back the view to update the presentation accordingly. In the case of the UserView, it prints to the standard output whether the action was successful and a message for the user.

Note

The update_display() method is more tightly coupled because it deserializes a user object. It would be better in terms of design principles to make it less dependent on the user object. You can do this reimplementation.

In this example, the view is a CLI, which is not something that modern applications would use as the view. As already discussed, the idea behind MVC is that the components can be individually developed and reused as one would like. Once the CLI application works as desired, it should be trivial to replace the view component with another one that implements the presentation in desktop form or a web page. As long as the communication interfaces between the components stay the same, you can switch between different views or use multiple views simultaneously.

The last piece of the puzzle is the controller component located in the controller.py module.

The first class in the module, the UserController class, is an abstraction interface and serves as a contract between the view and the implementors of the controller. There could be many different controllers for the view component, but they all need to follow the UserController abstraction blueprint and implement the methods. In this example, the controller will have two methods—one for creating a user and another serving the view functionality of displaying a user by ID number.

The continuation of the previous code from the controller.py module is the SimpleController class.

This is the implementation of the strategy design pattern, where SimpleController is one of the strategies of the UserController interface. It implements the contract between them and the parent UserController class. The create() method implementation is reviewing the user input and preventing the user creation if the input is faulty. This is just a simple showcase of how a controller can be used as a mediator between the view and the model components. If the input was accepted, the data gets translated into a user object, and the not static method store_user() is called on that object. This starts the process of saving a user, which is implemented in the user model. If everything went right, the controller must update the view component that the request went through. Because the controller already has a reference to a view object, the code simply calls the update_display() method with the right values.

The get() method relies on the model component static method get_user() to retrieve the user from the database. The get() method does not need to know implementation details, nor what is the storage technology. It just requests the user by the ID and expects to get a positive or negative result based on the data in the database. What is also useful in this controller is the ability to change the output messages without having to alter the view code. Every method on the controller can prepare the correct response for the view to display. With multiple views, your code does not need to duplicate these messages across the views. If they are using the same controller, they will receive identical replies.

Now that you have the code that is written in MVC ready, you need something that will tie everything together and run the program. You could write another module—for example, app.py, in which you would import the view.py module and call the create_user() method with your set of values. You can call the method multiple times, and it should result in creating multiple users. Then, you could continue interacting with the view and call the get_user() method with an ID to retrieve the user information. You could also use the Python interactive shell and do the same interactively. The last option is showcased on the following figure.

Now you see that the program works. It would be possible to do the same in just one module, and much simpler, but that was not the point. Once you start development on bigger projects, where there are more views, controllers, models, and other moving parts in your program, you need to have a structure in your code; otherwise, you will lack the reusability, modifiability, and extensibility aspects of your application. Using an architectural pattern like MVC, you can achieve all that and avoid tangled code that is hard to maintain.

MVC Frameworks

With access to patterns such as MVC, you might find yourself using and reusing ideas all the time in different projects. You figured out that the application you wrote last month in Python has a nice structure, and you already implemented the database communication and separated the logic for the view, controller, and model into separate folders. You may have a place in your project to store additional libraries, and you found a nice way of including them into your application code. You feel comfortable taking this project and porting it to a new one over and over because you enjoy working in the project structure that you shaped. It just makes sense and works perfectly for the projects you are working on. It looks like you just created yourself a framework.

There are many frameworks out there that provide you with generic functionality and can be adopted by your own implementations. They propose a way of working and promise you great efficiency, stable design, loosely coupled codes, and more. You end up working inside a defined structure that guides you to write your code better and enforces you to use patterns without you knowing anything about them. There are many frameworks that incorporate the MVC pattern as their basis, and it could be that you already used some of them without even knowing that you wrote highly reusable and flexible code. Using a framework will not prevent you from writing tangled code in the parts where you must develop your own logic, but the structure should guide you to decrease such problems. There is a learning curve to every new thing in the technology world, and frameworks require your time as well. Luckily, frameworks are usually well documented, provided with example projects that you can use for discovering the features and way of working.

There are many MVC frameworks that are written in different programming languages, the first being Smalltalk-80, written in the 1980s.

The more modern list of frameworks that enable the MVC pattern:

  • ASP.NET MVC

  • Django, Pyramid, web2py

  • Symfony, Laravel, Zend Framework

  • AngularJS, Ember.js, Wakanda

  • Spring

Content Review Question

A user action changes the state of the system. How will the view component update the screen to accompany those changes?

No comments:

Post a Comment