preface

Although MVC is not a new topic, from the design of some excellent MVC frameworks in recent years, we can still find some new highlights in the architectural design of MVC. This paper will explain some disadvantages of traditional MVC architecture, understand how some excellent MVC frameworks solve these problems, and reveal the design ideas and design concepts reflected in them.

MVC review

As a classic architecture mode, MVC has its inevitable success, which may be interpreted differently by different people. The author most agrees with the following view: By grouping the components with similar responsibilities and properties together and isolating those with different characteristics, MVC divides the system into three parts, model, view and controller. Each part is relatively independent and has single responsibilities. In the implementation process, it can focus on its own core logic. MVC is a reasonable sorting and segmentation of system complexity, and its essence is “separation of concerns”. As for the responsibility division and mutual relationship of the three ELEMENTS of MVC, I will not repeat it here. The following figure gives a very detailed explanation.

Figure 1: Functionality and relationships of MVC components

View and Controller decoupling: Mediator + secondary event delegate

In the early years of developing SWing-based GUI applications, the author deeply realized the tight coupling between view and controller in the practice of MVC architecture. In many event-driven GUI frameworks, such as Swing, any user action to the view triggers an event, which is then processed in the response method of the listener. If you want the view to register itself as an event listener, you have to include a reference to the Controller in the view, which MVC doesn’t want, because the view and controller are tightly coupled. If the Controller is registered as a listener, the event response will be taken by the Controller, which in turn causes the Controller to handle presentation logic that it should not be involved in. This makes the view and controller difficult to decouple because: Most user requests contain a certain part of presentation logic and a certain part of business logic, and the two kinds of logic are combined in one request. In the process, it is difficult for view and controller to reasonably divide their work. The key to solve this problem is to establish a mechanism between view and controller that can effectively separate presentation logic from business logic. In this respect, the design of PureMVC[II] is worthy of reference. It solved the problem of tight coupling between view and controller by introducing Mediator + secondary event delegation mechanism.

Mediator is a design pattern that seems to be widely used in componentized GRAPHICAL interface frameworks. Even in the Gang of Four’s book Design Patterns, an example of a GRAPHICAL interface program is used to explain Mediator. Mediators are designed to complete the interaction of a group of objects through a media object, avoiding mutual reference and complex dependency between objects. Mediators, when applied to graphical interface programs, often act as a medium for a group of closely related graphical components to coordinate their work (for example, by pressing a button, other components will not be available). In PureMVC, Mediator is widely used and its positioning has changed subtly. It is no longer just a medium between graphic components, but also between graphic components and command, which makes it no longer optional, but a required facility in the architecture. Corresponding to the traditional MVC architecture, Mediator is the mediator between the View and controller (of course, it is still the mediator between the view). All user requests from the View are passed through Mediator to the Controller. It alleviates the tight coupling between view and controller to some extent.

Once the View, Mediator, and Controller were defined and their responsibilities clearly delineated, the problem remained to link them together to fulfill a user request, and event mechanisms played a crucial role in this regard. Event mechanism can make the object focus on handling affairs within the scope of their duties, and don’t need to care about who to deal with excess part and how to deal with the current object only needs to broadcast an event that would be interested in this event with the other objects come out to take the next step of work, there is no direct dependence between the current object and take over, even perceive the existence of each other, This is an important reason why the event mechanism is widely regarded as a loose-coupling mechanism. As an aside, in domain-driven Design, the famous Domain Event pattern is also created based on this feature of the Event mechanism, which is intended to ensure the purity of the Domain model. Avoid direct dependency of domain models on Repository and Service. Going back to PureMVC, let’s look at how the event mechanism connects the View, Mediator, and Controller in the process of handling a user request. In PureMVC, when a user request is made, the graphical component implements its own presentation logic in its event response method, then collects the data, puts it into a new event, and broadcasts it out. This is the first event delegate. This event is listened for by a Mediator, and if processing the request requires the assistance of other graphical components, mediators coordinate their handling of the presentation logic that is their responsibility. The Mediator then sends another event(this time called notification in PureMVC) that causes a command to execute and complete the calculation of the business logic. This is the second event delegate. In the first of the two event delegates, the event delegate allows the graphical component to “handle presentation logic within its scope of responsibility” and “get out of the way” of “coordinating other graphs to process the remaining presentation logic” and “selecting and delegating business objects to process the business logic.” It is the obvious responsibility of Mediator to “coordinate with other graphical components to process the remaining presentation logic”, so the first broadcast event is delegated to Mediator. After completing the coordination of the graphical component, mediator does not “select and delegate the business object to process the business logic”, which is not its responsibility. Therefore, A second event delegation occurs. A new event is broadcast by mediator, which is then responded to by a command that does the final work of “selecting and delegating the business object to process the business logic”.

Figure 2: Mediator + Secondary event delegation mechanism

To sum up, PureMVC introduces mediators between view and controller to make view and controller become indirect dependencies. The user requests to delegate from View to Mediator, and then from Mediator to controller in the way of events. The combination of Mediator + secondary event delegate can be described as a “powerful” decoupling mechanism, which achieves complete decoupling between view and controller.

The return of natural granularity from Controller to Command

At present, the mainstream MVC framework of many platforms has introduced command mode in the design. The introduction of Comman mode has changed the structure of traditional MVC framework, and controller is the most affected. In the old MVC architecture, a controller might have multiple methods, and each method would correspond to one user action. Therefore, a controller would correspond to multiple user actions. In the Command-based MVC architecture, A command usually corresponds to only one user action. The process of delegating a useraction to a method of a Controller in a traditional MVC architecture becomes the process of binding userAction to command in a command-based MVC architecture. Whereas the traditional controller management approach is to establish a “centralized” mapping between user actions and model, the command-based management approach is to establish a “point-to-point” direct mapping between user actions and model.

Figure 3: Evolution of the architecture from Controller to Command

There is a reason for the mainstream MVC framework to transition to Command. In addition to the advantages of Command itself, a very important reason is that the granularity of controller is difficult to determine due to the lack of reasonable organization basis. Controller is different from View and Model in that both view and Model have their own natural granularity organization basis. The organizational granularity of View directly inherits user interface design, while the organizational granularity of Model is the result of domain modeling based on certain analysis and design ideas (such as OOA/D). The controller needs to coordinate view and Model at the same time, but the organizational structure and granularity of view and Model are not equal, which makes the controller face a problem of “how many domain objects can be communicated and coordinated within the scope of view”. As there is no reasonable organizational basis, Designers often feel at a loss when designing controllers. Command, by contrast, has no controller confusion at all, because command has a natural basis for organization, which is user actions. It is very natural and simple to design a command for a user action and then map the two together. However, it is important to note that this does not mean that all commands are of the same granularity, because different user actions represent different volumes of business, thus determining whether commands are “large” or “small.” Following good design principles, it is recommended to decompose some “large” commands and encapsulate some “small” commands with reusable parts. Many MVC frameworks define interfaces and abstract classes to support composit-pattern-based command assembly.

Whether based on controller or command, the controller responsibility of “coordinating view and Model interaction” defined in MVC architecture will not change, and corresponding components and mechanisms are required to carry and implement. In the command-based architecture, command takes part of the responsibilities of controller in the past. In a sense, Command is a fine-grained controller, but command is passive in nature. On the one hand, its control over the View and model is much weaker than that of controller. For example, In general, Command does not directly manipulate the View. On the other hand, it does not know what user action it is mapped to or under what circumstances it will be triggered to execute. Supporting command operations requires additional registration, binding, and triggering mechanisms that, along with command, fulfill the controller’s responsibilities. Since most command-based MVC frameworks today implement and encapsulate these important mechanisms, in a sense these frameworks themselves act as controllers.

conclusion

This paper mainly analyzes the two drawbacks existing in the traditional MVC architecture in the past: the close coupling between view and controller and the problem that the granularity of controller is difficult to control. It introduces how some MVC frameworks deal with these problems. The excellent design ideas reflected in these design schemes are worth learning.

By the way, I recommend an architecture exchange group: 617434785, which will share some videos recorded by senior architects: Spring, MyBatis, Netty source code analysis, high concurrency, high performance, distributed, microservice architecture principle, JVM performance optimization, which have become necessary knowledge system for architects.