How to MVVM that you know is compiler correct

MVVM is considered by many developers as the defacto design pattern to be used for creating GUI apps. Due to the nature of it being a design pattern, sometimes developers do not implement it correctly, or the architecture of the app simply deteriorates over time. Utilising the hierarchical nature of the pattern, a single pass compiler enforces the pattern, which is the type of compiler that F# adopted. If a developer wrote a GUI app with F#, than the MVVM implementation will be compiler checked. This post highlights why a code base might not be following MVVM correctly. Secondly, an MVVM compiler checked example is then shown. The last section covers a few practical steps that can be taken for individuals that have a C# code base.

What is MVVM?

MVVM is a design pattern for UI applications. Check out the documentation from Xamarin for a full explanation.

Who has a code base that implements MVVM correctly?

Developers that have had some experience with MVVM will know that not every code base follows the pattern. Developers sometimes make mistakes (also known as bugs), and unless the developer writes unit tests for every line of code, it can can be hard for the developer to know that code base adheres to MVVM. Unfortunately the lack of enforcement is that nature of design patterns, since the compiler does not understand the pattern.

Unit tests increase the developer’s confidence that the code base correctly implements MVVM. Although, this assumes that the developer of the unit tests wrote them correctly. Additionally, many developers use a framework to abstract away the boilerplate code. Sometimes the frameworks can make it hard to correctly unit test.

To the individuals that have a code base covered with unit tests great work, but consider the event where another developer is brought into the team. The lead developer must now check the new developer’s code as well as his/her own. Time for a tech lead/management to add some more process with pull requests (PR).

The teammate is now now writing unit tests and following MVVM with a PRs for the senior dev to keep and eye on. However, the senior dev needs to take a holiday, it’s been a while! Who will keep an eye on MVVM now?

How to invert the problem to make it easier

All of the above, though being very good, are process heavy tools. At any stage of development, the architecture of the code base can deteriorate, without a clear sign to the developer. As an alternative, it would be better if the language/APIs/compiler guided the developer to write the app with the correct architecture. At a high level, Mark Seemann addresses the architecture problem and highlights a solution known as the pit of success. Here is the talk that Mark gave on this and is worth the watch: Pit of success.

A compiler can check an MVVM implementation

MVVM has a clear hierarchy, resulting in a layered architecture. Each layer only has access to the items above it. The model layer only has access to itself, including Services/Managers and any data objects. View models have access to all the models and services. Finally the UI container element (Page/Window) has access to everything.

Given the hierarchy of the design pattern, one way to enforce the pattern is to require that each layer be declared before using it. It turns out that a single pass compiler enforces this. For example, a class named Foo must first be declared. After it has been declared, only then, can an instance be created.

Searching for an appropriate compiler, highlights that F# utilizes a single pass compiler. F# runs on .NET as well as having Object-Oriented support.

MVVM that is compiler checked – 25 lines of code

To demonstrate a correct implementation of MVVM, the example will need to include all the right pieces. The following will be included:
– a model as a data type
– a service that operates on the model and has an interface with an implementation
– a view model that takes in the interface
– a page element to glue it all together

Xamarin Forms has been used to demonstrate this. A link to the repository with all of the code is at the end of this post. Here is the code:

namespace CompilerCheckedMvvm
open Xamarin.Forms
open Xamarin.Forms.Xaml

// An alias to show where the model as data is used
type Model = int

// Interface
type IDataLoaderService = 
    abstract member LoadData: unit -> Model

// Implementation of interface
type DataLoaderService() = 
    interface IDataLoaderService with 
        member this.LoadData() = 42

// View model with service as a constructor argument
type MainViewModel(_dataLoader: IDataLoaderService) = 
    member this.Data = _dataLoader.LoadData().ToString()

type MainPage() as this =
    inherit ContentPage()
    let _ = base.LoadFromXaml(typeof<MainPage>)
    do this.BindingContext <- MainViewModel(DataLoaderService())

Re-arranging the order of the code above will result in a compiler error. Additionally with such a short example, most developers will be able to learn that pattern. It starts with the model, then reading further down, the view model is declared. Finally at the bottom of the file, is the UI where everything is put together.

It is important to highlight that for teams and developer that know F#, using F# to correctly implement the MMVM design pattern is more cost effective than adding all of the processes stated earlier.

4 things to do if you have a C# app

The simplest solution is to use F# as a resource and training ground. After some practice with F#, take the knowledge back to the existing code base and cary on. Many developers will find this strategy extremely easy to implement, however the benefits are limited.

Another approach is to use F# script files to create simple port of the existing code base. The script file can then be compiled to check that everything is setup correctly. If the compiler identifies any problems, the existing code base can be modified and updated. The developer could also choose to commit the script file into a repo to be maintained on a regular regular cadence.

Building on the approach of a script file, a developer could create a simple port of the existing app. As with the script file, the developer only needs to port the important parts of the app as the goal is for understanding and compiler verification. Porting a basic version of the app to F# has additional benefits as well. This strategy is both safe and practical for any developer to learn and master F#. F# learning will be fast tracked as only the relevant parts of the language need to be understood. For those interested in using MvvmCross find out how to get started: How to mvvmCross with F# that will increase your productivity

The final strategy, though the most challenging, is to replace the C# app with an F# port. In many circumstances this will not be feasible but it may not be as challenging as one might first think. Before a developer starts such a large change, it is important that the developer includes the relevant people, and ensures everyone is in support. F# has many great benefits, but rushing into a port can result in Hype Driven Development, which ultimately has a negative effect on everyone involved. The goal is to have a significant positive impact for the end user of the software.

How to start a port that will be successful

For a developer to get started on such a port, small steps need to be taken. The app does not need to be ported all at once. F# and C# compatibility is very good. Create an F# project, move a subset of code to the project. Then reference the new F# project from the existing C# project. Consider starting with a unit test project as this also reduces the risk. One project at a time, is the key here.

What to remember

Here’s a bullet list of the important stuff

  • MVVM is hard to follow, as the compiler can not enforce it in C#
  • MVVM is enforced by the F# compiler
  • Here is a compiler verified example as a Xamarin Forms app
  • If you have a C# code base, here’s how to start with F#:
    • For learning, to apply to a C# code base
    • with a script file port of the existing code base
    • create a basic port in F#
    • In rare cases: Replace the app with an F# port.

Multiple items
val int : value:’T -> int (requires member op_Explicit)

Full name:

type int = int32

Full name:

type int<‘Measure> = int

Full name:<_>

type unit = Unit

Full name: Microsoft.FSharp.Core.unit

val typeof<‘T> : System.Type

Full name: Microsoft.FSharp.Core.Operators.typeof