This is the third post about the clean modules. In this case I will discuss the User Interface Layer.
For most users, the UI is the «real system». The UI is the layer who interacts with the exterior, normally a human being that exchange messages with the System. But the part that interacts with the user is only one—the view—of several other modules this layer contains. Other modules are hidden to users creating the view aspects, transforming the business data and preparing what the user sees.
In the above example I show you a peculiar MVC Web implementation. In this example, instead of having a single model implementation we have a view model implementation that consolidate the model interface with a view interface (IProfileMode and IProfileView). That’s why this implementation is sometimes called MVVM. In this model we include a Presenter module too. All these objects interact as the text at the right of the image explains. Notice also that the handy use of the view model would allow us to implement more than two models and view interfaces for a more complex view instance.
Also, in this first example, I use a static presenter class instead of separated interface and implementation modules. When we need more control and flexibility of the presentation, it’s convenient to allow the instantiation of a concrete presenter and the model views it creates. This implementation uses two packages for all the UI layer:
In this other example, the difference is that I locate the concrete instances of the presenters and the view models in a separate package. This implementation is more flexible because allow us to test different types of presenters and view models onto different presenters’ packages. But another problem arises. Who make the concrete instance of the presenter? As the model is quite simple, a dedicated factory for the presenters doesn’t seem necessary. The controller class, as the information expert (GRASP) of the required view models, might have the factory methods to create the presenters—which behave as helper modules to create those view models. If those methods are data-driven, based on the execution context, the concrete presenter instance might change as the testing or environment change.
Below there is an enumeration of all the module types:
Represent the business data. Their interfaces are defined in the use case layer, but their implementation is a role of the UI layer. They could be implemented with a single concrete model class or as a view model instance.
The visible mechanism used by end-users and the system. In the example above it’s an HTML page constructed with a dynamic creator mechanism as Razor, for example. The view templates are bounded with the models or view models to provide the content and formatting aspects of the view.
The mechanism the application use to receive the messages from the views and control the responses from the interactors. The controller also creates the views.
The modules that construct the data structure that the views will display and use to determine is visible state. They create the view models. In the first example above is defined as a static factory class.
A data structure which contains business data from models and visual instructions or specifications for the views. It implements the model and view interfaces.
In the following sections I discuss each module type in more detail:
Model classes and business data
The model is the concrete implementation of an interface DTO defined in the use case layer, as we see in a previous section under the use case layer discussion. There are two options to implement this concrete instance: create a single implementation of the model interface or create a view model instance.
In the first case we are implementing a pure MVC structure where the model—pure business data—is enough and the displaying features are defined in the view types themselves. In the other case, the view model combines the business aspects with the visual constraints and formats. This is, in my opinion, a more flexible implementation because it allows us to implement multiple models and view interfaces at once. Of course, we might combine the two module variations, using the single model implementations when the exchange of data and visual aspects is simple, and reserve the view models implementations for the more complicated views.
In both cases the concrete instances aren’t created by the controllers but by a presenter class that acts as a factory most of the time, although in a pure MVC structure, this should be a controller’s task. Depending of the UI framework used, there are opportunities for the concrete view objects to be also an implementation of those model interfaces. In those case, there’s no need for a presenter as the creation of the classes use inheritance (of classes or types) of the views directly. The controller then, pass the view as a model type to the arguments of the interactor functions.
By principle, these modules don’t have any operation. We should avoid to «format» or «adapt» the visual aspects of the model, letting those responsibilities to the presenters and views.
View interfaces and the output channels
The view is the only module in contact with the end-users. They show the visual gadgets or features and business data. It’s tempting to define these modules based on the UI technology and framework we use, but we should declare them independently of those aspects.
As the view is a combination of visual and data attributes tAs the view is a combination of visual and business-data attributes there are two approximations about how to access the business data: by composition or by inheritance. In the first case, the view uses as internal components the models and view-models for its business content. In the other instance, the view inherits or implements the model or view-model interfaces or abstracts. Both implementations have its values and its restrictions.
How we decide for one mechanism over the other? The final decision might be influenced by the capabilities of the UI framework we will finally use. If the view objects are standard classes, then the possibility of inheritance is available, otherwise the composition is the alternative. By principle, I prefer to follow the GoF axiom in favor of object composition over inheritance. The composed module brings more flexibility than the inheritance-dependent module. For example, if we declare the inner variables at the most useful abstract level, we leverage the possibility to use any concrete version of the types available, varying them by context or any other criteria. The combination of multiple objects in a class replaces the need of multiple inheritance of classes (implementations), impossible in languages such as C# or Java. This decision also eases the independent development of the of both objects (the embedded and the composed).
The view as representation the UI itself should be a simple reader of the data and settings the application defines. In this role the view should only arrange the layout of the data it shows but not the specifications of this content and its format.
Controller classes and event-handlers
The Controller is the class that acts as the director of operations or mediator between the user and the use case layer. It manages the user messages from the views, calls the interactors, and request the models and views creation. There is not a coincidence that controllers interfaces resemble the interactor interface. They have a clear connection, because in some ways the Controller acts as an adapter from the UI layer to the Use case layer translating the user messages to the system.
The Controller might be viewed also as an event or message handler. The user sends a message through the view elements and the Controller should respond to the request querying the interactor and managing the traffic of data from the use case layer and the views. Seeing from this point of view, the controller is a kind of mediator between interactors and views, but also an observer expecting the view calls and updates the view state.
Presenters as factories
In the example at the right, the Presenter is defined as a Factory static class with no state data. This is not casual. The presenters are creators or builders of view models. They serve the request of the controllers for such types, that communicate business and visual data. As builders they also define the constraints of such constructions and the concrete implementation they return.
That last affirmation is that allow us to define multiple implementations and test them in different context or environments based on the dynamic input data the Presenter receives.
Notice that the examples above are different of the MVP version of the Presenter. In that instance, the Presenter replaces the Controller and combines its responsibilities with the standard responsibilities of creating the models and view models.
As the examples at the beginning of this section about UI layers, the other possibility to instantiate a Presenter uses an interface defining the type of the presenter and a concrete instance in another package. That’s the case of the left example in the above diagram. The implementation is very similar to the other example with two important differences. The first one is that the Controller should implement a factory method for each Presenter type. The other is that the concrete instances of the presenters and the view models are defined in another package. There is a benefit with such implementation as the presenters’ package is a plugin and we can create as many as we need to our solution, based on the execution context or any other criteria.
The view models are DTO implementations with no operations. They contain two kind of data: business and visual properties. The first one is the data that come from the use interactors. The other is defined in the UI layer itself and varies the format, color, fonts and any other visual aspect of the view. These are simple but very relevant types because contains all the data the user interchange with the system.
Depending on the decision about the Presenter implementation, the ViewModel concrete instance is defined on the same UI layer package or in a separate presenters’ package. This separation allows to develop multiple packages with different implementations based on the view characteristics. For example, we might have a view model implementation for web, for windows and for print views. For the view, all differences are transparent as the View access the concrete instance through the view model interface definition.
The last part of this serie will be dedicated to the Technical Service Layers.