Skip to content
Sponsor: NimblePros Software architecture, modernization, and training for high-performing development teams. Work with us.
Model-View-Presenter (MVP) Design Pattern

Model-View-Presenter (MVP) Design Pattern

What is the MVP Pattern?

Model-View-Presenter (MVP) is an architectural pattern derived from MVC that separates an application into three components:

  • Model — represents data, business logic, and rules. The model is entirely independent of the UI.
  • View — displays data and forwards user input to the Presenter. In MVP the view is intentionally passive — it contains no logic of its own, only rendering and delegation.
  • Presenter — mediates between Model and View. It retrieves data from the model, formats it for display, and pushes it to the view. It also handles all user input events forwarded by the view.

The defining structural difference from MVC is that the Presenter holds a direct reference to the View through an interface. This makes the entire presenter — including all presentation logic that in MVC would be scattered across controllers and views — fully testable without a UI framework.

User Input ──▶ View ──▶ Presenter ──▶ Model
                 ◀──────────┘
                 (via IView interface)

MVP originated at Taligent in the 1990s and was popularised by Martin Fowler’s refinement into the Supervising Controller and Passive View variants.

MVP Participants

Model

The same as in MVC: domain entities, services, repositories. The model has no knowledge of the view or presenter.

View (Passive View)

The view in MVP is deliberately kept dumb. It exposes only:

  • Properties for each piece of data it displays (the presenter sets them).
  • Events or method calls that notify the presenter of user actions.

The view implements an IView interface that the presenter depends on. This is what enables the presenter to be tested in isolation — a test can supply a fake view.

public interface IOrderListView
{
    IReadOnlyList<OrderSummary> Orders { set; }
    string StatusMessage { set; }
    event EventHandler RefreshRequested;
}

Presenter

The presenter holds a reference to the IView interface and to the model/service layer. It subscribes to view events and pushes data back to the view.

public class OrderListPresenter
{
    private readonly IOrderListView _view;
    private readonly IOrderService _orderService;

    public OrderListPresenter(IOrderListView view, IOrderService orderService)
    {
        _view = view;
        _orderService = orderService;
        _view.RefreshRequested += OnRefreshRequested;
    }

    public void Initialize()
    {
        LoadOrders();
    }

    private async void OnRefreshRequested(object? sender, EventArgs e)
    {
        LoadOrders();
    }

    private async void LoadOrders()
    {
        var orders = await _orderService.GetRecentOrdersAsync();
        _view.Orders = orders;
        _view.StatusMessage = $"{orders.Count} orders loaded.";
    }
}

The Concrete View

The concrete view (a WinForms form, a web page code-behind, a Razor Page, etc.) implements IView, wires up event handlers, and sets properties:

public partial class OrderListForm : Form, IOrderListView
{
    private readonly OrderListPresenter _presenter;

    public OrderListForm(IOrderService orderService)
    {
        InitializeComponent();
        _presenter = new OrderListPresenter(this, orderService);
        btnRefresh.Click += (s, e) => RefreshRequested?.Invoke(this, EventArgs.Empty);
        Load += (s, e) => _presenter.Initialize();
    }

    public IReadOnlyList<OrderSummary> Orders
    {
        set => orderGrid.DataSource = value;
    }

    public string StatusMessage
    {
        set => lblStatus.Text = value;
    }

    public event EventHandler? RefreshRequested;
}

Two Variants: Passive View vs. Supervising Controller

Martin Fowler identified two flavours of MVP:

Passive View

The view is completely inert — all logic lives in the presenter. The view exposes only primitive setters and events. The presenter reads from the model and writes every individual property of the view. This maximises testability: you can test all presentation behaviour through the presenter alone.

Supervising Controller

The view retains simple data-binding to the model for straightforward display tasks, while the presenter handles only complex logic that plain binding cannot express. This reduces boilerplate at the cost of some testability — some behaviour lives in the binding and is harder to test independently.

Most modern guidance favours Passive View because it draws a clean line: the presenter is fully testable, the view is a thin rendering shell.

Testing the Presenter

Because the presenter depends on IView and IOrderService interfaces, both can be replaced with mocks:

[Fact]
public async Task Initialize_LoadsOrdersIntoView()
{
    var mockView = Substitute.For<IOrderListView>();
    var mockService = Substitute.For<IOrderService>();
    mockService.GetRecentOrdersAsync().Returns(new List<OrderSummary>
    {
        new OrderSummary { Id = 1 }
    });

    var presenter = new OrderListPresenter(mockView, mockService);
    presenter.Initialize();

    mockView.Received().Orders = Arg.Is<IReadOnlyList<OrderSummary>>(
        o => o.Count == 1);
}

No UI framework, no browser, no form — pure in-process unit test with full coverage of presentation logic.

MVP vs. MVC vs. MVVM

MVC MVP MVVM
Mediator Controller Presenter ViewModel
View updates Controller pushes to a selected view Presenter pushes via IView interface Binding pulls automatically
View–mediator coupling Controller does not reference the view Presenter holds IView reference ViewModel has no view reference
Testability of UI logic Medium High (mock IView) High (ViewModel has no UI deps)
Best suited for Server-rendered web, REST APIs WinForms, WebForms, Razor Pages, Android (classic) WPF, WinUI, MAUI, Blazor, Angular, Vue

ASP.NET Core Razor Pages is structurally close to MVP: the Page Model (code-behind) acts as a Presenter, the .cshtml file is the view, and the two are tightly paired — unlike MVC where any controller can render any view.

Intent

Separate presentation logic from the view by introducing a Presenter that communicates with a passive view through an interface, making all presentation logic independently testable.

References

Pluralsight - Design Patterns Library

Martin Fowler - GUI Architectures: Passive View

Martin Fowler - GUI Architectures: Supervising Controller