Gå till innehållet

Realtid för bokningar

Mål: en ändring av en bokning på en klient ska omedelbart synas på alla andra aktiva klienter (tyst auto-uppdatering, utan toast).

Beslut: endast boknings-CRUD (BookingsController), en API-replik (in-process SignalR, ingen Redis-backplane), minimal payload + refetch på klienten.

Design

sequenceDiagram
    participant A as Klient A
    participant API as ASP.NET Core API
    participant B as Klient B

    A->>API: POST / PUT / DELETE /api/bookings
    API->>API: EF Core SaveChangesAsync
    API->>B: SignalR BookingChanged { action, id }
    API-->>A: 201 / 204
    B->>API: GET /api/bookings?from&to (synligt intervall)
    API-->>B: 200 [bokningar]
    B->>B: Hydrate + AppState.Refresh()

Händelsekontrakt

  • Hubb: NotificationHub (/hubs/notifications, [Authorize], JWT via access_token-query — redan förberett i Program.cs).
  • Händelse: BookingChanged med { action: "created" | "updated" | "deleted", id: int }.
  • Sänds till Clients.All efter lyckad SaveChangesAsync i Create/Update/Delete. Avsändaren får också händelsen — en extra refetch är ofarlig och håller logiken enhetlig.

Klientbeteende

  • SignalRService (singleton) ansluter med AccessTokenProvider = () => AuthService.Token, WithAutomaticReconnect().
  • Home (kalenderskalet) startar anslutningen efter inloggning, prenumererar på OnBookingChanged och gör debounced (~500 ms) tyst refetch av synligt DateFrom/DateTo via ApiClient.GetBookingsAsync + State.HydrateBookings() + State.Refresh().
  • Ingen toast/banner. Samtidig redigering av samma bokning: last-writer-wins; formulär som sparar en raderad bokning får 404 med befintligt felmeddelande.
  • Utanför intervallet eller under pågående fetch: händelsen slås ihop/ignoreras — nästa intervallbyte hämtar ändå färskt.

Avgränsningar

  • DataController-import sänder inget (CRUD-only).
  • Ingen Redis-backplane — vid fler än en API-replik krävs backplane för fan-out (framtida arbete).
  • Ingen närvarolista eller per-rum-grupper — broadcast till alla anslutna.

Filer

Område Fil Ändring
Backend backend/Salieri.Api/Controllers/BookingsController.cs Injicera IHubContext<NotificationHub>, sänd BookingChanged efter create/update/delete
Klient clients/Salieri.Client/Services/SignalRService.cs Auth-aware anslutning, typad BookingChangedEvent, idempotent start/stopp
Klient clients/Salieri.Client/Pages/Home.razor(.cs) Injicera SignalRService/AuthService, start/stopp + debounced refetch, unsubscribe i Dispose

Verifiering

  1. Två webbläsare mot samma API: skapa/redigera/flytta (drag)/radera i den ena — syns i den andra inom ~1 s utan omladdning.
  2. Utloggning stoppar socketen (ingen 401-loop); omstart av API återansluter automatiskt.
  3. dotnet build Salieri.slnx grön; backend-test med mockad IHubContext verifierar SendAsync("BookingChanged", …) vid POST/PUT/DELETE.