February 2010

Volume 25 Number 02

Beyond MVP - Extending the MVP Pattern for Enterprise-Class Application UI Architecture

By Haozhe Ma | February 2010

Model-View-Presenter (MVP) represents a breakthrough in thinking about UI patterns, and makes it clear that UI designers should maintain separation of concern in their applications.

However, there are many different interpretations of the MVP pattern. For example, some people take for granted that the MVP pattern explicitly represents the UI architecture pattern. This is not exactly true for enterprise-class applications. Compared to other types of UI applications, enterprise-class applications need to deal with many different requirements, more parties involved, more complexity, and many cross-dependencies on other systems such as services, other applications and so on. These particular characteristics have required the UI architecture of enterprise-class applications to have more emphasis on flexibility, maintainability, reusability, implementation consistency, and decoupling business functionality from the underlying technology to avoid dependencies on specific products and vendors.

If only the MVP pattern itself is applied as the UI architecture pattern for enterprise-class applications, some questions will be raised. Here are just a few:

A typical enterprise-class application contains many views, and events occurring in one view could impact other views. For example, clicking a button in one screen could cause a pop-up window to show up, and another screen’s data may be updated at the same time. Who is responsible for controlling such screen-flow logic? Should this be controlled by each view’s pairing presenter?

In a Service-Oriented Architecture (SOA), the application UI generally gets information through services. For example, the UI would need to call a generated WCF service client proxy in order to call the WCF service to get data. Is it a good design for presenter to call this service client proxy directly? If these services are implemented with different technologies or if service models are changed, how do you design the UI architecture so that the impact of these changes on the UI implementation can be minimized?

Following this train of thought, some implementations might use generated service client proxy models across the application. Are there any risks of doing that? If a dedicated UI model is needed, which part will be responsible for providing the mapping between the service client proxy model and the UI model?

These are not new questions, and many other patterns have been introduced to fill in the gap. For example, the Application Controller pattern was introduced to assume the responsibility for controlling navigation flow.

I thought it would be helpful to pull some of these disparate MVP-extending discussions together and draw a holistic view of UI architecture design. Looking at the problem from an enterprise-class application perspective, this will help UI architects recognize what key parts are needed for UI design and define a consistent pattern to guide UI application implementation.

The term “MVP pattern” will be used throughout the article, but actually the original MVP pattern has been retired and two variations of the original MVP are currently in place. One is the Passive View pattern and the other is the Supervising Controller pattern. Although either one fits certain scenarios and both have pros and cons, the UI architecture described in Figure 1 is primarily based on and extended from the Passive View pattern. This certainly doesn’t mean UI architecture couldn’t be constituted based on the Supervising Controller pattern, but this is my personal preference.

Architecture Based on the Passive View Pattern
Figure 1 UI Architecture Based on the Passive View Pattern

Let’s begin the discussion with a clear understanding of what constitutes the UI architecture by extending the MVP pattern. Figure 1 shows what key parts are needed from a high-level view of UI architecture. In this diagram, seven major parts are introduced: View, Presenter, UI Model, Process Flow Controller, Service Agent, Service Client Proxy Model, and Service Client Proxy.

View

View basically follows the View role in the Passive View pattern. View assumes a simple list of responsibilities, starting with handling UI display layout and presentation-specific logic.

View’s second responsibility is to raise events to the Presenter, and it requires several implementations to handle this responsibility. First, View needs to implement the IView interface. To ensure there are few if any implementation impacts on Presenter logic, and to provide unit testing capability, the Presenter should interact with View through an IView interface.

Second, View needs to define public properties that Presenter can interact with. In the Passive View pattern, View does not pass data to Presenter. Instead, it is up to Presenter to choose data it is interested in from View. This type of implementation further reduces the contract binding between View and Presenter and separates their responsibilities more clearly.

One question, though, is about what data type the View properties should use. The ideal way is to have View define these properties using only simple types, such as string, integer and so on. However, in a real-world implementation, it can become tedious if the View defines data this way. It is acceptable to expose a data property by referencing complex types, such as UI Model definitions. This balances architectural purity with implementation considerations.

Third, View also needs to call Presenter operations when events happen in the View. The View can call the Presenter directly without passing any data. There is no loosely coupled design between View and Presenter because View and Presenter are always paired. It is good design to have one Presenter operation dedicated to one View event.

View’s last responsibility is to respond to property value updates. Presenter updates View properties to indicate a change. View has the knowledge to decide how to respond to such changes. It could go ahead to refresh the view to reflect the data change, or it could decide not to take any action.

Presenter

Presenter essentially follows the Presenter role in the Passive View pattern. However, here, the Presenter does not determine process flow. The Presenter takes event requests from the View and publishes event requests to Controller to let Controller decide the next step. Because Presenter will not handle process flow logic, it can’t know whether the event requests from View will have any impact on other Views. So when Presenter receives event requests from View, it will immediately publish corresponding events so that Controller can respond to these event requests and decide on the next process step. Presenter never assumes it can go ahead and perform some actions until it is instructed by Controller.

Controller decides whether Presenter operations need to take place. When a Presenter operation is called by Controller, Presenter then performs the actions, such as to retrieve data through a Service Agent. If Presenter needs to take actions against services, it does that through Service Agent. It will pass required parameters to the Service Agent and get results back from the Service Agent.

When Presenter is ready to notify View about data changes, it will do so by updating View’s property values. Then it is up to View to decide how to display them. As mentioned previously, Presenter interacts with View through the IView interface rather than accessing the View object directly. Since the View instance has already been passed to Presenter when initiating Presenter, Presenter already has a View instance to deal with.

Finally, Presenter can access the UI Model and can put it in a cache if UI Model data needs to be accessed later.

Process Flow Controller

The Process Flow Controller is close to the Application Controller pattern. The difference is that the sole responsibility of the Process Flow Controller discussed here is to control process flow based on typed events raised by Presenter. Process flow is not limited to screen navigation flow. It also includes controlling the order of Presenter actions related to event requests.

Process Flow Controller subscribes to events published by Presenter, and responds only to events published by Presenter. These events are typed events. In other words, Process Flow Controller doesn’t respond to a general event.

Because Process Flow Controller only responds to a typed event, a process flow actually has already been predetermined when an event occurs. This simplifies Process Flow Controller’s logic. Each event published by Presenter contains the data needed for Process Flow Controller to carry on when initiating other Presenter operations.

Process Flow Controller will initiate Presenter and related View instances if they haven’t been initiated yet. Inversion of Control (IoC) will be needed due to cross-reference concerns. This also provides a loosely coupled design between Presenter and Process Flow Controller.

UI Model

UI Model basically follows the Model role in the Passive View pattern. In Passive View, Model is really not doing much work and it simply provides the model structure definition. In addition, as described in the Presenter section, Presenter is responsible for maintaining Model state.

The reason I call this UI Model rather than simply Model is to differentiate it from Service Client Proxy Model, which will be described later the article.

UI Model defines the model structure that is suitable for UI application logic handling. The UI model definition may look exactly the same as Service Client Proxy Model. However, in some situations—especially if the UI needs to display data from multiple service sources—a restructured UI Model is needed that will be different from the Service Client Proxy Model.

Service Agent

Service Agent plays an intermediary role between Presenter and Service Client Proxy. The service in the name is not necessarily a Web service. It represents any resources that will provide data or perform business logic. This could be a Web service, but could also be simply file I/O.

Service Client Proxy has specific meaning in Web service technology. Here, I use Service Client Proxy to represent the gateway for a service.

The Service Client Proxy implementation is technically specific. From Presenter’s perspective, it would rather not know how data is transmitted or provided. Such technically specific details could be hidden inside of Service Agent. So the layer of Service Agent protects Presenter from being affected by service implementation technology changes, service versioning, service model changes, and so on.

Service Agent provides operations for Presenter to interact with. If a complex type needs to be passed to these operations, you need to define a complex type under UI Model. This is also true for the operation return type. You then pass these operation calls to corresponding Service Client Proxy operations. In some cases, one Service Agent operation may initiate several Service Client Proxy operation calls.

Because Service Agent operations take in complex types defined under UI Model, Service Agent operations need to map from UI Model to Service Client Proxy Model when calling Service Client Proxy operations. When Service Agent operations need to return results back to Presenter, they would map from Service Client Proxy Model to UI Model after getting results from Service Client Proxy operations.

This could be a tedious job. However, there are tools available to map from one model structure to another model structure easily, so this become more of a one-time design job.

Service Client Proxy and Service Client Proxy Model

Service Client Proxy in Web service technology provides local access for the service client even though the service is hosted remotely. In this article, I would describe Service Client Proxy as the gateway to the service. Service Client Proxy Model represents the service contract model definition.

Service Client Proxy passes calls to services and returns service responses. If the service is implemented with ASP.NET Web Services (ASMX) or Windows Communication Foundation (WCF) technologies, the Service Client Proxy can be generated automatically.

Service Client Proxy Model will reflect the service contract model structure definition.

Implementation Example

To illustrate the UI architecture described in Figure 1, let’s take a look at a Windows Forms application that demonstrates the implementation. This sample app is included in the download for this article.

This sample application first needs to load a list of regions. When a region is selected, customers that belong to the region are displayed. When a customer is selected, a time range query window will pop up. After a start time and end time are entered, a list of orders that belong to the selected customer will be displayed on the data grid in the main screen.

I will use the scenario of displaying a list of regions to explain how the UI architecture in Figure 1 is implemented in the sample app. Figure 2 shows the call sequence for this scenario.


Figure 2 UI Call Sequence

When the Main Screen form is loaded, it first initiates the Presenter interface and passes the current View instance to Presenter’s constructor:

private void MainView_Load(

  object sender, EventArgs e) {



  _presenter = new MainPresenter(this);

  ...

}

The MainPresenter instance initiation will cause MainPresenter’s constructor to first assign the passed-in MainView instance to a private variable of type IMainView. It then initiates a Controller instance and passes the current Presenter instance to the Controller constructor:

public MainPresenter(IMainView view) {

  _view = view;

  _controller = new Controller(this);

}

Controller instance initiation will cause the constructor to assign the passed-in MainPresenter instance to a private variable of type IMainPresenter. This constructor also defines the event handler to be prepared to respond to MainPresenter’s published events, such as RetrieveRegions:

public Controller(IMainPresenter presenter) {

  _mainPresenter = presenter;

  ...

  _mainPresenter.RetrieveRegions += (OnRetrieveRegions);

}

Back in the main screen form load event, Presenter is called to retrieve regions after the Presenter object is initiated:

Private void MainView_Load(object sender, EventArgs e) {

  ...

  _presenter.OnRetrieveRegionCandidates();

}

When Presenter receives this call, it first publishes the event RetrieveRegions instead of going ahead to retrieve the regions. The RetrieveRegions event has been defined in the IMainPresenter interface and is implemented in MainPresenter:

public event EventHandler<RetrieveRegionsEventArgs> 

  RetrieveRegions;

  ...



public void OnRetrieveRegionCandidates() {

  if (RetrieveRegions != null) {

    RetrieveRegions(this, 

      new RetrieveRegionsEventArgs());

  }

}

In the Controller class, since an event handler for RetrieveEvents has been registered, it can respond to the RetrieveRegions event:

private void OnRetrieveRegions(

  object sender, RetrieveRegionsEventArgs e) {



  _mainPresenter.HandleRetrieveRegionsEvent();

}

Controller decides that the process flow should return control to MainPresenter and asks it to continue to retrieve regions. If Controller needs to initiate presenters other than MainPresenter, it can employ Unity Framework to perform that task.

In MainPresenter’s HandleRetrieveRegionsEvent operation, it calls Service Agent to retrieve regions. For simplicity, my example doesn’t actually implement the service. It just writes some dummy data to make the application function. After a result is returned from Service Agent, note that MainPresenter doesn’t pass data to MainView. Instead, it updates the MainView’s RegionCandidates property:

public void HandleRetrieveRegionsEvent() {

  RegionAdminServiceAgent agent = 

    new RegionAdminServiceAgent();

  List<Region> regionCandidates = agent.RetriveRegions();

  _view.RegionCandidates = regionCandidates;

}

In MainView’s RegionCandidates property, it handles the display of regions:

public List<UIModel.Region> RegionCandidates {

  set {

    _regionCandidates = value;

    PopulateRegionCandidates();

  }

}

This is the whole sequence of retrieving regions and displaying them in the MainView. It definitely involves more steps than simply calling Service Agent to get regions. However, when thinking from the perspective of an enterprise-class application, it not only introduces a loosely coupled design, it also promotes a consistent implementation pattern. This can greatly simplify maintenance and knowledge-transfer for a development team.

Just one more comment about this code example: the whole sequence starts with the first Windows Forms load event. A more advanced implementation could start with Controller and let Controller decide what the first form is to be loaded.

Wrapping Up

In this article, I introduced one approach to UI architecture design based on extending the MVP pattern. UI applications can be complicated and there are many different flavors of UI application design. The technique I present in this article represents one of these many solutions. This is a useful technique in many situations, but be sure it suits your requirements before implementing it.

There are already many UI frameworks on the market and many are based on MVP, Model-View-Controller or patterns developed as extensions of these two. A good first step is to see what major parts are implemented by these frameworks—for example, like the way I abstracted the UI architecture in this article. Falling into implementation details without first considering the big picture is not good architectural thinking. Starting with a broad architectural understanding of the problem at hand ensures not only that the foundational problems of system architecture are resolved, but also that a repeatable and well-thought-out design can be followed.

Finally, in the example of this article, the Controller implementation was created with C#. A better approach might be to use a process flow technology such as Windows Workflow Foundation, which may allow a more flexible design and implementation. However, this technical implementation detail will not affect the underlying principles behind the UI architecture described in this article.

For further discussion of the MVP pattern, see the August 2006 issue of MSDN Magazine.


Zhe Ma is a technical architect of Enterprise Architecture at Unum Group, based in Portland, Maine. He can be reached at zma@unum.com.

Thanks to the following technical expert for reviewing this article: Don Smith