Real-world Demo: Blazor Book Library
Real-world Demo: Blazor Book Library
Section titled “Real-world Demo: Blazor Book Library”The Blazor Book Library is a fully functional reference application demonstrating the Sharpino event-sourcing backend working together with a Blazor frontend.
- GitHub Repository: tonyx/blazorBookLibrary
- Live Demo Website: www.biblionet.eu
Architecture Overview
Section titled “Architecture Overview”This project demonstrates a production-grade tech stack:
- Frontend: Blazor WebAssembly/Server displaying book catalogs, reservations, and inventory.
- Backend: F# API layer built on Sharpino.
- Database: PostgreSQL storing events and snapshots.
- Real-time Sync: SignalR integrated directly with Sharpino’s DetailsCache to push updates to client browsers immediately when read-model details are refreshed.
SignalR & DetailsCache Integration Flow
Section titled “SignalR & DetailsCache Integration Flow”To ensure client pages update in real-time when underlying aggregate states change, Sharpino triggers a SignalR notification whenever dependent composite Details are refreshed.
sequenceDiagram
participant Client as Blazor Client Browser
participant Hub as SignalR Hub (BookHub)
participant API as Backend API
participant DC as Sharpino DetailsCache
participant DB as Postgres (EventStore)
Client->>API: 1. Send Command (e.g., Borrow Book)
API->>DB: 2. Append Event & Snapshot
DB-->>API: Success
API->>DC: 3. Trigger Clean / Evict
DC->>DC: 4. Recompute Refreshable Details (Refresh())
DC->>API: 5. Invoke OnRefresh Callback (registered in v6.0.2)
API->>Hub: 6. Send Refresh Message (DetailsCacheKey)
Hub->>Client: 7. Broadcast Refresh Notification
Client->>Client: 8. Reload Details View & Re-render UI
Implementation Details
Section titled “Implementation Details”1. Registering the Cache Refresh Side-Effect (F# Backend)
Section titled “1. Registering the Cache Refresh Side-Effect (F# Backend)”In Sharpino 6.0.2+, you can register a callback on cache updates. The backend hooks into this callback to publish messages to the SignalR hub context:
// Example backend setup registering the OnRefresh callbacklet setupDetailsCacheRefreshNotification (hubContext: IHubContext<BookHub>) = DetailsCache.Instance.RegisterOnRefreshCallback(fun (key: DetailsCacheKey) -> // When a DetailsCache item is refreshed, notify all SignalR clients task { do! hubContext.Clients.All.SendAsync("ReceiveDetailsRefresh", key.Type.Name, key.Id) } :> Task )2. Reacting to Refreshes (Blazor C# Frontend)
Section titled “2. Reacting to Refreshes (Blazor C# Frontend)”In your Blazor components (e.g., BookCatalog.razor), configure a HubConnection to listen for refresh events. When a refresh notification is received, invalidate local states and trigger a re-render:
@using Microsoft.AspNetCore.SignalR.Client@inject NavigationManager Navigation@implements IAsyncDisposable
<h3>Book Catalog</h3>
@code { private HubConnection? hubConnection;
protected override async Task OnInitializedAsync() { hubConnection = new HubConnectionBuilder() .WithUrl(Navigation.ToAbsoluteUri("/bookhub")) .WithAutomaticReconnect() .Build();
hubConnection.On<string, Guid>("ReceiveDetailsRefresh", async (typeName, id) => { if (typeName == nameof(BookDetails)) { // Re-fetch the updated details view from the API await LoadBooks();
// Request component re-render StateHasChanged(); } });
await hubConnection.StartAsync(); }
public async ValueTask DisposeAsync() { if (hubConnection is not null) { await hubConnection.DisposeAsync(); } }}