Building a .NET MAUI App with the MVU (Model-View-Update) Pattern

If you've been in the .NET ecosystem for a while, you're undoubtedly familiar with the stalwart MVVM (Model-View-ViewModel) pattern. It's the go-to architecture for XAML-based frameworks like WPF, Xamarin.Forms, and now .NET MAUI. It's powerful, data-binding-centric, and well-supported by the tooling. But what if I told you there's another way? A pattern that promises simplicity, predictability, and a unidirectional data flow that makes your app's state easy to reason about? Enter MVU, or Model-View-Update. In this deep-dive, we'll explore what the MVU pattern is, why it's gaining traction, and how you can build a real .NET MAUI application using it. We'll build a simple but functional Task Tracker app from scratch. Let's get our functional programming hats on! 🎩


🤔 What is the MVU Pattern?

MVU stands for Model-View-Update. It's a functional architecture pattern popularized by the Elm programming language. Its core tenets are beautifully simple:

  • Model (The State of Your App): This is a single, immutable data structure that represents the entire state of your application at a given point in time. Think of it as a snapshot.
  • View (A Function of Your State): The UI is not a persistent, stateful entity but a pure function of the current Model. You give it the model, and it returns the UI description. No side effects here!
  • Update (The State Manager): This is another pure function. It takes the current Model and a "Message" (an intent describing what happened, like ButtonClicked), and it returns a new, updated Model. It does not mutate the existing model.

This creates a strict, unidirectional data flow that is incredibly easy to debug and test.

MVU vs. MVVM: A Quick Comparison

Feature MVVM (Model-View-ViewModel) MVU (Model-View-Update)
Data Flow Bidirectional (via data binding). The View and ViewModel communicate freely. Unidirectional. State flows down (Model → View), events flow up (View → Message → Update).
State Management State is often distributed across properties in the ViewModel. State is centralized in a single, immutable Model.
UI Declaration Typically uses XAML with data binding expressions. Often uses a declarative UI C# syntax (sometimes called "fluent" or "code-first" UI).
Boilerplate Can require significant boilerplate (e.g., INotifyPropertyChanged). Generally less boilerplate. The pattern is enforced by the function signatures.
Learning Curve Familiar to most .NET developers, but concepts like binding can be tricky. Requires a shift in mindset towards functional programming and immutability.
Predictability Can be harder to trace state changes due to two-way bindings. Very high. All state changes are centralized in the Update function.

🛠️ Step-by-Step Guide: Building a MAUI Task Tracker with MVU

Let's put theory into practice. We're building a Task Tracker where users can add and delete tasks.

Step 1: Create a new .NET MAUI Project

Fire up your terminal or IDE and run:

Step 2: Define the Core Types (Model and Message)

First, we define the "state" of our app and the "actions" that can change it. Create a new file Models/MvuState.cs:

Now, define the messages. Create Models/Messages.cs:

Step 3: Create the Update Function

This is the brain of our application. It handles all state transitions. Create a static class Services/StateService.cs:

Step 4: Build the View

Now for the fun part! We'll build the UI as a pure function of the state. We'll use .NET MAUI's declarative C# syntax for this, which aligns perfectly with MVU. Replace the content of MainPage.xaml.cs:

Important: You can delete the MainPage.xaml file, as we are building the UI entirely in C#.

Step 5: Run the App! 🎉

You're all set! Run the app on your preferred platform (Android, iOS, Windows, etc.).

You should see a functional task tracker where you can add tasks, mark them as complete, and delete them. The state is managed predictably, and the UI is a direct reflection of that state.


💼 Real-World Use Cases and When to Choose MVU

MVU isn't a silver bullet, but it shines in specific scenarios:

  • Data-Driven Forms: Applications with complex, dynamic forms where the UI needs to change based on user input benefit greatly from the single source of truth.
  • State-Heavy Applications: Apps like games, dashboards, or real-time monitoring tools where state consistency is critical.
  • Teams Embracing Functional Programming: If your team is familiar with F# or functional concepts, MVU will feel natural.
  • Prototyping: The lack of boilerplate can let you build a functional prototype incredibly quickly.

You might want to stick with MVVM if:

  • Your team is deeply experienced with XAML and data binding.
  • You are heavily relying on existing .NET MAUI community toolkits and libraries built around MVVM.
  • You find the declarative C# UI syntax less readable than XAML for complex layouts.

To dive even deeper into the concepts we've covered, check out these excellent resources:

  1. The Elm Architecture: The original source of the pattern. A must-read to understand the philosophy.
  2. .NET MAUI Documentation: Create a .NET MAUI UI with C# Markup: The official guide on using declarative C# for your UI.
  3. Comet (by Clancey): An early .NET experiment that heavily inspired the MVU support in MAUI.
  4. F# and the Model-View-Update Pattern: A great explanation of MVU in the context of F#, another .NET functional language.
  5. Immutability in C# with Records: Learn how C# 9's record type makes implementing immutable models easy.

✅ Conclusion

The MVU pattern offers a refreshing, robust, and highly testable alternative to MVVM for building .NET MAUI applications. By enforcing a unidirectional data flow and treating the UI as a function of state, it eliminates whole classes of bugs related to mutable, distributed state.

While it requires a slight mental shift away from the traditional MVVM approach, the benefits in code clarity, predictability, and developer ergonomics are profound. The tight integration of declarative C# UI in .NET MAUI makes trying out MVU easier than ever.

So, why not give it a try in your next project? You might just find that building apps this way is not only productive but also genuinely enjoyable. 🚀


An unhandled error has occurred. Reload 🗙