Blazor for Large Scale Systems

Taking your application to the cloud or building a large scale application from scratch? Blazor technology lives up to its promise.

 

Melbourne Blazor and Xamarin Meetup Presentation

Rod Hemphill recently presented to the Melbourne Blazor and Xamarin Meetup on our experience in implementing large scale Blazor applications.

 

Blazor for large scale systems

It is May 2020 and we have implemented 3 production Blazor systems. Server Side Blazor has only been production released for 5 months and Client Side WASM is still in pre-release.

Our largest system was one that manages all levels of motor sport events across Australia, except for formula 1:

www.evententry.motorsport.org.au

54 razor pages

53 database tables

4 full time and 3 part time developers

We started in June 2019 and went live in February 2020.

We did a lot well and some things not so well. The following is how we are going forward given what we learnt.

 

Large scale systems

The main difference between any large system and a small system is the complexity related to having multiple developers in the team, and then the complexity of the maintenance over time. Any mistakes in architecture cause big problems that go on and on.

 

Server Side vs Client Side Blazor

Since Client Side Blazor runs in the client’s browser, you don’t have control over the computer that it runs on. Correspondingly with Server Side Blazor, your application will be limited by the internet speed of your users. Server Side Blazor may not be your first choice if you intend to deliver your solution to a mining company in Wiluna WA. Equally, you would question the wisdom of delivering a Client Side Blazor app to racing car drivers with low end computers.

Therefore we will build all our future Blazor solutions as dual ClientSide/ServerSide  solutions because:

  • It’s simple to do.
  • You don’t have control over the computer your clients use.
  • At run time you can switch from client side to server side, if appropriate.
  • Client Side and Server Side have different tools that have their own development advantages. Being able to switch during build provides productivity improvements.

 

MVC vs MVVM

If your solution will EVER have a mobile component (all ours do) then going MVVM allows you to share your code on your mobile build.

Not all of our mobile solutions duplicate the functionality required on a Web app, but when they do, it is usually not trivial and comes with significant savings. We expect that more and more often we will be able to enrich the experience for a mobile user because we have that function available.

For those of you unfamiliar, the MVVM design pattern is quite different to the MVC pattern but from a practical stand point you put your page based business logic in a class separate to the page (called a View Model), you add View Model based navigation (Blazor’s navigation is page based), and you add Property Notify Change functionality so that the page can respond to a property change.

Remind me to write a blog about how to do this with a Blazor app.

 

Database Access

Remember that even for your Client Side WASM implementation, your database will sit on your server.

We implemented SQL Server on Azure, but this is probably irrelevant for this discussion.

On our ServerSide implementation, our pages access the database directly, whereas with our ClientSide implementation we access via an API which then accesses the data directly.

But A and B are the same piece of code. Therefore at all three ‘x’s we implement an IDataServices interface which, through dependency injection, resolves to different implementations.

Note that ServerSide and the API resolve to the same direct code, offering a single database optimisation point.

.

 

API Access

The API in the above diagram is the same API we use for our Data Transfer Objects coming to and from our mobile apps, with some caveats. Mobile apps:

  • usually want a subset of the records types,
  • sometimes want a simplified and demoralised view, and
  • always want an optimised “only send once” copy of the data.

This is a work in progress for us.

So far we have been able to use a single API for both because:

  • any mobile API needs to deal with different app versions (you don’t get control over when the user upgrades their app) so you already have “if” tests in there, and
  • our standard process for sending only data that hasn’t been sent before to a mobile handles the ClientSide situation.

So far so good. But we have a number of fall-back options that don’t impact our architecture.

 

Async Programming

It is unusual to use async programming on the server, but this isn’t just a server side solution.

Although we haven’t proved this, in theory, for a reasonable sized implementation, ClientSide Blazor could quite easily block the UI thread. For this reason alone we choose to go async programming. You can’t add it in later.

 

Components

We wrap third party components in our own components. Sounds wasteful but there are only 20 or 30 of them, and the upsides are:

  • We can reliably trap and handle crashes using a couple of strategies (I have another blog article to write on this – it’s a subject in itself).
  • Third party controls often don’t do exactly what you want so you often add css styles or class to them. For example, the checkbox in a checkbox control may not support being a different colour to the checkbox label. Simple and easy to fix, but by having it in our own component we can manage consistency.
  • Blazor components give you the power to change the look and feel of your pages after the event. Build quickly, enhance the UI later. For example, we built a “modal” component. We are now about to add animation to it.

 

The User Interface

Early on we chose to base our user interface design on the Material Design. That decision simplified our transition to Razor pages considerably. We chose and recommend MatBlazor (https://github.com/SamProf/MatBlazor) and thank Vladimir for his considerable efforts.

Our first major challenge, like with any large project, was standards. Different programmers doing slightly different things. Blazor projects add new dimensions to this problem.

Our second challenge, which we didn’t realise the extent of until we went live, was the user experience with respect to bugs. ServerSide Blazor sites on SignalR, which is effectively a continuous socket connection. If the SignalR gets killed the user is left in a horrible dead screen state with a “Press Reload” message. No option to give you a screen print, and if you are not careful the only details of the error are hidden in the console of their browser. Horrible and expensive.

 

CSS

Large site.css files are a nightmare on a large project. It eventually got to the point that it was easier to create new css code rather than try and work out the impact of changing the existing. Hence this small note … we now use SASS. Our implementation is simple: align the SASS folder structure to our Blazor folder structure, which means css is easy to find and change and the bootstrap and ionic type css are out of harms way with no temptation to change. 

Base holds our variables (site colours, panel sizes, font sizes), our fonts and functions.

Components holds a sass file for each of our components.

Vendors holds any vendor supplied css or sass file.

Views holds our page specific styles and classes.

     

    Dynamic Page Updates

    These are pages that change because of underlying data changes, as distinct from user actions. Two examples:

    • A background process runs to transcode videos. The status field will change from “Job Running” to “Video transcoded” upon completion.
    • A dashboard page is progressively updated as each user issues with an “Incident Report” opens it on their mobile app.

    Blazor is really good at doing this type of thing and we bake it into our architecture. Without going into too much detail, which would be an entire blog by itself, we do this:

    • All our models are saved to the database through a single base class.
    • Each model knows what its ‘children of interest’ are from a business perspective. That is, on a 100 item object model everything is related, but we only manage children in this way where there is a distinct business interface need.
    • We created a simple, optimised Message Bus for handling model change event notifications.
    • When a model is saved it notifies both its parent that it has changed and places it on the message bus.
    • Any screen that would like to update itself if a particular model or one of its children are changed, subscribes to those changes on the message bus.

    We call this the ModelStateChanged event notification.

       

      Razor Pages

      Code Behind

      Razor pages are used for two different purposes: Components and Pages. There are also two different styles for creating Razor code: @Code{} and code-behind. Which should I use?

      In components we use the @Code {} style because they are generally small and we can see all the code on a single screen.

      In pages we use the code-behind style:

      Maintenance is a lot easier if razor syntax is separated from business logic code, especially when the programmer changing it is not the one who wrote it.

       

      Razor Page Example

      The learning examples for creating razor pages, where accurate, often don’t reflect what we are aiming at in a large system. Below is a snippet which I hope illustrates some key points which help us create systems that are more maintainable:

      • Limit the amount of C# code in your razor pages. Either encapsulate them in components or embed them in your View Model or code behind.
      • Have your CSS tied to your components not your pages, causing consistency across pages.
      • HTML needed to format a component should be embedded in that component. Only add HTML in a page if it is for formatting the component with the page. This was a particularly important learning for us … originally we placed third party component directly on our pages, and when the client wanted style changes, we tried to do them on the pages (which quickly gets ridiculous when your have 20 pages to change with 30 to 50 components).

       

      Finally

      Having built large systems on many platforms, we understand the importance of a good architecture. Blazor is no different. The principles of good architecture don’t change, so the question is simply how do you do this with Blazor in a way that maximises the wealth of benefits that Blazor offers.

      We consult in Blazor and Blazor Architecture, and we offer a pragmatic approach to taking a desktop app to the cloud using Blazor.