Comparing Blazor Server with Blazor WebAssembly, I find one striking difference that might/should have a great impact on the application design. Let me outline that difference on the basis of a simple requirement:
Clicking a button to increase a counter and whenever the counter reaches 10, the button will be disabled.
Let's compare:
- Blazor WebAssembly: The click event handler executes synchronously directly in the browser right after the user has clicked the button. The counter is increased and the condition gets checked. If the counter is 10, the button will be disabled using property binding. There is no way the user could have clicked an eleventh time.
- Blazor Server: The click event handler executes asynchronously on the server, only after the click event has been published to the channel and received by the server. This might take an arbitrary amount of time, depending on the internet connection. The counter gets increased on the server and the condition gets checked. However, before the browser gets informed about the potential state change of the button, the user can click the button one or many more times, causing the event handler on the server to be executed again, potentially increasing the counter more than is allowed.
As a programmer using Blazor Server, I can no longer rely on the synchronous uninterrupted handling of user interaction events that I may be used to in JavaScript applications. I have to clutter all my event handlers with additional sanity checks just to make sure nothing unexpected happens. This also prevent a straightforward migration from Blazor WebAssembly to Blazor Server (and frankly, vice-versa).
Note that I am not convinced by the argument that the latency is so small that no real user is able to click again before the GUI is updated. Even for very controlled network conditions delays might happen unexpectedly. Also, it is never a good choice to rely on chance and external factors. Remember Murphy's Law?
My question is: How do you deal with this in practice? Do you bother writing all these sanity checks in your event handlers?
I concluded that in order to treat Blazor WebAssembly and Blazor Server equally at the source code level, you have to apply certain patterns and restrictions for yourself, as otherwise the runtime behaviors might differ.
One of these things is the input delay happening in Blazor Server. Even if it is – or should be – small, you still have to address it if you prefer to prevent nasty bugs when switching from WebAssembly to Server.
One way to address it is to guard each and every event handler (that reacts to a user interaction event) with code assuring that the state of the frontend is indeed such that this handler can safely be executed. In a "traditional" SPA web application (like Angular) you should guard against undesired operations on the server-side anyway, so do you in Blazor applications. However, in a Blazor application, you also additionally have to guard your frontend much more tightly, all because you can not rely on uninterrupted synchronous handler execution.
In my opinion, Blazor is a leaky abstraction, as the decision between WebAssembly and Server is not merely an implementation detail.