WCSF Application Architecture 3: Model View Presenter

This article is part of a series;

· WCSF Application Architecture 1: Introduction

· WCSF Application Architecture 2: Application Controller

Model View Presenter

The MVP pattern is effectively an evolution of Model View Controller, and is mainly geared at improving the structure and testability of User Interface code. There is a good article here that discusses UI patterns, and a previous post of mine that contrasts the MVP pattern with a pre-release version of the ASP.NET MVC Framework. The Web Client Software Factory (WCSF) uses the MVP pattern, and relies on Dependency Injection to wire up the class dependencies.

The following diagram should help you visualise how MVP is put together;

MVP

 

Note that each Presenter has exactly one View, and each View has exactly one Presenter. There is one Model for the whole application (ahem, see later posts on modularity J).

Let’s consider each item in turn.

 

Model

The model is often misunderstood. It sounds like it should just be data, but then where does my business logic sit? Well, I see the model as representing the whole of my business logic – entities, workflows, and components. It is the window into the real non-UI part of my application. Sticking to our standard architecture defined in the introduction post, the model should only expose business logic components. It should not surface data access components, web service proxies, or other private classes that are further down the architecture stack.

Note that in much of the WCSF documentation the “Model” in MVP is silent – mainly because you could argue it isn’t really a key part to the structure of the UI code, as it is business logic. Hence you might see MVP referred to as “the View-Presenter pattern”. It’s the same thing...

We will discuss the model, how to structure it, and how to implement it in later posts, but for now just think of it as a bunch of plain old .NET objects that “do business logic”.

 

View

A view is used to display data to the user, and therefore in the WCSF is an ASP.NET “aspx” page and its code behind. In the WCSF, this aspx page must implement an interface (e.g. “IHomePage” would define the Home Page view, “HomePage.aspx” would implement it, and it would have a matching presenter named “HomePagePresenter”). The code that exists in this class should be very simple – and should be focussed on two simple tasks;

1. Taking instructions from the Presenter and converting them into ASP.NET calls.

2. Taking events from the user (such as button clicks) and passing them on to the Presenter to field.

Effectively, the view is responsible for translating between the Presenter and ASP.NET concepts.

Note that on the pattern diagram above I really wish I could remove the arrow from the view to the model. This is because when I use MVP I enforce a rule that the View should under no circumstances call directly to the business logic. The only reason the arrow is there is because the view may have read-only access to some business entities for rendering purposes only. This is heading towards the “supervising controller” variant of the pattern described in the WCSF documentation here, which I find to be the only practical way to implement data-binding.

 

Presenter

A presenter instructs a view what should be displayed and when, and responds to events raised by the view. It should be unaware of ASP.NET concepts. It should also be the class that interacts with the business logic, potentially passing on single or multiple business entities to the view for rendering. In other words, it is the go-between for business logic and rendered UI.

In the WCSF the presenter has a reference to the view as defined by an interface, hence the two-stage and dotted link in the diagram above.

 

An example

So after all this theoretical talk, let’s see a really simple example of a Model-View-Presenter trio and how they work. Have a look at the following (simplified for clarity) code.

interface IMyView

{

  void DisplayText(string name);

}

class MyView : IMyView

{

    public MyViewPresenter Presenter { get; set };

    public void DisplayText(string name)

    {

        TextBox1.Text = name;

    }

    public void ExitButton_Clicked(object sender, EventArgs e)

    {

        Presenter.OnExit(Page.User.Identity.Name);

    }

}

class MyViewPresenter : Presenter<IMyView>

{

    void ViewLoaded()

    {

        View.DisplayText("Welcome");

    }

    void OnExit(string username)

    {

        BusinessLogic.ExitUser(username);

        View.DisplayText("You clicked Exit!");

    }

}

What we have here is the following;

1. An interface that defines the operations that our View exposes (just “DisplayText” in this case).

2. An implementation of the view (aspx mark-up omitted in this example). This receives instructions such as “DisplayText” and translates them into ASP.NET concepts – such as setting the Text property on a TextBox.

3. A presenter, which instructs the View what to do during initialisation, and responds to View events (such as Exit). Note that the Exit method calls into business logic and then updates the view.

I hope that is a nice clear simple example of the interaction between the classes.

 

How do I get my data?

This example contains a really interesting yet subtle point, and that is how to pass data from a view “up” to the presenter. In my code, I make the following call;

Presenter.OnExit(Page.User.Identity.Name);

What this means is that my view is responsible for collecting the relevant data and passing it on to the presenter at the appropriate time. Now this isn’t necessarily wrong, and in many cases is the easiest way to write code – but actually I think it is sub-optimal.

The issue is that we already have an interface defining what our view provides, and I feel that this should really be the contract between presenter and view for as much information as possible. Therefore, I would prefer the following implementation;

interface IMyView

{

    void DisplayText(string name);

    string CurrentUsername { get;}

}

class MyView : IMyView

{

    public MyViewPresenter Presenter { get; set };

    public void DisplayText(string name)

    {

        TextBox1.Text = name;

    }

    public void ExitButton_Clicked(object sender, EventArgs e)

    {

        Presenter.OnExit();

    }

    public string CurrentUsername

    {

        get

        {

            return Page.User.Identity.Name;

        }

    }

}

class MyViewPresenter : Presenter<IMyView>

{

    void ViewLoaded()

    {

        View.DisplayText("Welcome");

    }

    void OnExit()

    {

        BusinessLogic.ExitUser(View.CurrentUsername);

        View.DisplayText("You clicked Exit!");

    }

}

(note: changes are in italic)

What I’ve done here instead is expose the user’s login name as a property on the view, which is defined in the interface that serves as the “contract” between the presenter and the view. I then just raise the “OnExit” event from the view to the presenter, and it is the presenter’s responsibility to extract the information it needs from the view.

This example uses the username, but I would also use it for passing the contents of a textbox edited by the user through to the presenter – expose a property on the view that the presenter extracts the required text from when needed.

I personally think this is more elegant, plus I find it actually helps when writing unit tests. What do you think?

 

MVP + Application Controller

The final point to make is where the interaction with an Application Controller fits in. Hopefully the diagram below helps;

MVP plus AC

I believe that, if you go with my preferred approach to the pattern described in a previous post, the presenter should be the only point of interaction with the Application Controller. So in my simple example above, you might have code that looks like this;

class MyViewPresenter : Presenter<IMyView>

{

    void ViewLoaded()

    {

        View.DisplayText("Welcome");

    }

    void OnExit()

    {

        BusinessLogic.ExitUser(View.CurrentUsername);

        ApplicationController.CompleteExit();

    }

}

The CompleteExit method would more than likely redirect the user to the home page.

 

Summary

MVP is subject to less confusion once you have ironed out what an Application Controller does, but there are some key points to remember;

1. There should be no logic other than simple data binding or property access in a View.

2. There should be no ASP.NET concepts in a Presenter or the Model.

3. The View must not directly call Model (business logic) methods, but may have a reference to business entities to simplify rendering and data binding.

4. The Presenter should orchestrate the relationship between the View and the Model (business logic).

Following these guidelines should help enable you to unit test your presenters effectively, and should make you smile when you have to make a change to the code... meaning you know exactly where and how to make that change.

Keep listening to hear about modularity in the WCSF, and as always – chip in if you agree, disagree, or don’t think I’ve explained something sufficiently...