The Blazor Butterfly

Introduction

In the past I have created and maintained user interfaces with many different technologies. Technologies I have come across are Windows Forms, classic ASP with HTML and CSS, ASP.NET MVC, WPF/XAML, AngularJS with HTML5 and recently Blazor [1]. When working with WPF/XAML I came across the Mvvm pattern [2], which I have used extensively from that time on. Pure Mvvm, in a moderately complex application, usually needs some application / controller or a similar concept like you have in the Model View Controller pattern [3]. This concept is needed to hold application or other state that transcends viewmodel state. It also has a different responsibility than viewmodel state [4]. I found it appropriate to keep the viewmodel and controller ‘in control’ while the views would follow any changes in the model. Keeping the views and the viewmodels separate obviously requires some sort of library to bind them back together again. For WPF this can be done with a custom written library or a library like Prism [5]. When I encounter a new technology like AngularJS at the time or Blazor now, I always try to see if I can reimplement the Mvvm pattern as described above that I nicknamed “The Butterfly pattern”.

The butterfly pattern

When I create software I always look at the concept first and see how I can implement that concept with the technology at hand. Obviously, the “butterfly pattern” is just a specialization of the Mvvm pattern. To explain how it works I’ll go into some more details about the dynamics of the parts that I have mentioned in the introduction. This is merely an example of how these parts come into existence. It may not be the only way to construct them during run-time. But for the technologies that I have used, the pattern is roughly constructed as shown in Figure 1.

Butterfly Construction

Figure 1: Loading the Mvvm application

The application usually starts by starting a browser (Angular / Blazor) or a fat client in the case of WPF. After that, it downloads / loads the application and loads and shows the initial Startup View (1). This would be the only time that a View is in control. The next thing that needs to be done is load the Mvvm component that in turn loads the MainViewmodel. This transfers control to the viewmodels and application / controller and results in a load of the MainView (2). The MainViewModel and MainView are still rather static and act more as placeholders for other viewmodels and views. The MainViewModel accesses any application / state that it needs (not shown in Figure 1). Note that the application / controller is a conceptual entity that may be composed of a single, or many classes or code constructs depending on the complexity and further design of the application. Depending on the application or controller state, the MainViewmodel can load any containing viewmodels (3). These viewmodels can be single items or collections of similar or different viewmodel types. Each viewmodel is represented by one view (4). The static parts of the application represent the body, and the dynamic viewmodels and views the wings of the "butterfly".

The way that the view is selected is something that is decided in specific classes provided by the Mvvm component. These classes can be extended to select the appropriate view depending on viewmodel or other additional state. This is similar to the data template approach in WPF [6]. The viewmodels do not know what views are used to represent them and the views are just loaded and bound to their model. In this pattern, the viewmodels together with the application state form the interaction model of the application. In case you prevent any other application interaction besides this model, you can completely test the application by testing the interaction model. This may reduce any additional integration testing with the user interface attached.

When interacting with the applications views, actions are delegated through the Mvvm layer to the viewmodel and / or the application. The action can either change the local state of the viewmodel or send a command that completely reloads or even replaces all loaded viewmodels with new ones. In this pattern, the specialization of Mvvm entails that views follow any viewmodels in a master-slave pattern, a separate application or controller concept, the restriction of references from the viewmodel to the view and vice versa as much as possible and translation between application data and view data in the Mvvm layer.

Blazor

With the advent of dotnet core [7] [8], combined with the work of the mono team [9] and the introduction of web assembly (WASM, [10]) the Blazor team [1] have given us the ability to write our front-end with C# once again. (And this time let’s hope this initiative does not suffer the same fate as Silverlight [11]). Because you can use Blazor on the client or server side, when I mention Blazor here, I am always referring to the client-side hosting model [12]. At the moment of writing, Bazor is still in an experimental phase, but you can already do some cool things with it [13]. For me, this presented an opportunity to see if I could implement a butterfly pattern with Blazor on the client. The reason for starting with Blazor was that I needed a user interface for another study project I was working on. I initially started with the Angular template in Visual Studio 2017. When I compiled the newly created application It gave me an error which was rather annoying. Because of my impatience at that time, I decided to try Blazor. The application that was created looked very small compared to the application created with the Angular template. But best of all, it worked immediately. From then on, I decided to create the front end with Blazor. But I also wanted to see if I could apply an Mvvm pattern. After a few days I created a crude library for the Mvvm plumbing, separated the views from the viewmodels and found a way to properly start the application and load all necessary components correctly. I still needed a reference from the views to the viewmodels to create the proper bindings and make the application compile. This was still in version v0.6.0 of Blazor (at the time of writing it’s at v0.7.0), and the hardest part was the fact that debugging was still so rudimentary that it was not very useful. For this reason, I decided to use debug writeline statements everywhere. The projects of the prototype solution are show in Figure 2.

Figure 2: Blazor projects

The UI.Server project is the web server where the application can be downloaded from and does not contain that much code. All other projects are downloaded to the browser. The UI.Client contains the startup code and starts the application contained in the Application project. The UI.Mvvm project contains classes that provide the plumbing between the UI.ViewModels and the UI.Views. The Entities contain the classes that represent the domain. This project could be merged with the Application project depending on whether you want to expose these classes directly to the Views or not. When you start the application in the browser you can see how it is loaded and created. (Figure 3)

Figure 3: Blazor application loading

When the application starts it loads several css and javascript files to start the Blazor application. It then continues to load the mono runtime compiled as a Web Assembly (WASM) file and subsequently loads both the application and Blazor .Net DLLs. (See also the blog post by Scott Hanselman for more details [14]).

When you refresh the application with the console window open you can see what happens when the application is started. This resembles the startup as shown in Figure 1.

Figure 4: Startup sequence in the console from debug writelines

Conclusion

Though still in an experimental phase, it is clear that Blazor already provides some nice hooks to implement a specific Mvvm pattern. Even with my limited experience I was able to create the crude prototype implementation of the Mvvm pattern described here. I am sure that with subsequent Blazor versions, more options and features will become available to allow enhanced implementations of this or other front-end patterns.

Resources

You can check out the prototype at github.com/JAICT/BlazorButterfly . To run the application select "Multiple startup projects" in the solution property pages and change the JAICT.Blazor.Recipes.UI.Client and JAICT.Blazor.Recipes.UI.Server projects to "Start". The application startup screen should look like the one in Figure 5.

Figure 5: Startup screen Blazor Recipe prototype

Note that you can click the Login button right away as an existing key has already been prefilled.

References

[1]

"Blazor," [Online]. Available: https://blazor.net/. [Accessed 3 24 2019].

[2]

Wikipedia, "Model–view–viewmodel," [Online]. Available: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel. [Accessed 24 3 2019].

[3]

Wikipedia, "Model View Controller," [Online]. Available: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller. [Accessed 24 3 2019].

[4]

"Responsibility of a ViewModel," [Online]. Available: https://stackoverflow.com/questions/3497891/responsibility-of-a-viewmodel. [Accessed 24 3 2019].

[5]

"Introduction to Prism," 24 3 2019. [Online]. Available: https://prismlibrary.github.io/docs/.

[6]

30 03 2017. [Online]. Available: https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/data-templating-overview.

[7]

Microsoft, "Download .NET," [Online]. Available: https://dotnet.microsoft.com/download. [Accessed 24 3 2019].

[8]

"Welcome to .NET Core," [Online]. Available: https://dotnet.github.io/. [Accessed 24 3 2019].

[9]

Mono, "Cross platform, open source .NET framework," [Online]. Available: https://www.mono-project.com/. [Accessed 24 3 2019].

[10]

"WebAssembly," [Online]. Available: https://webassembly.org/. [Accessed 24 3 2019].

[11]

Wikipedia. [Online]. Available: https://en.wikipedia.org/wiki/Microsoft_Silverlight. [Accessed 08 04 2019].

[12]

D. Roth, "Razor Components hosting models," 29 01 2019. [Online]. Available: https://docs.microsoft.com/nl-nl/aspnet/core/razor-components/hosting-models?view=aspnetcore-3.0. [Accessed 30 03 2019].

[13]

Blazor Community, [Online]. Available: https://blazor.net/community.html. [Accessed 01 04 2019].

[14]

S. Hanselman, 16 11 2018. [Online]. Available: https://www.hanselman.com/blog/CompilingCToWASMWithMonoAndBlazorThenDebuggingNETSourceWithRemoteDebuggingInChromeDevTools.aspx. [Accessed 08 04 2019].

[15]

Microsoft, "ICommand Interface," [Online]. Available: https://docs.microsoft.com/en-us/dotnet/api/system.windows.input.icommand?view=netframework-4.7.2. [Accessed 06 04 2019].