Architecture Deployment API

Real-time Flows

SignalR Hub Group Map

flowchart LR
  subgraph BouquetHub
    RG["radius:{lat}:{lng}:{radius}\n(Customer: nearby bouquet updates)"]
    OG["order:{orderId}\n(Customer: order status updates)"]
    VG["vendor:{vendorId}\n(Vendor Portal: order + vase dashboard)"]
  end

  Customer -->|JoinRadiusGroup| RG
  Customer -->|JoinOrderGroup| OG
  VendorPortal -->|JoinVendorGroup| VG

Outbox → EventBus → SignalR Dispatch

sequenceDiagram
  participant Handler as Command Handler
  participant UoW as UnitOfWork (EF Core)
  participant DB as PostgreSQL (outbox_messages)
  participant Dispatcher as OutboxDispatcherService (BackgroundService)
  participant Bus as InMemoryEventBus
  participant Hub as BouquetHub (SignalR)
  participant Client as Connected Client

  Handler->>UoW: SaveChangesAsync()
  UoW->>DB: INSERT aggregate change + INSERT outbox_messages in same transaction

  loop Every 5s
    Dispatcher->>DB: SELECT top 20 WHERE processed_at IS NULL ORDER BY created_at
    DB-->>Dispatcher: [OutboxMessage]
    Dispatcher->>Dispatcher: Deserialize IDomainEvent from payload
    Dispatcher->>Bus: InMemoryEventBus.PublishAsync(event)
    Bus->>Hub: Matching event handler calls Hub.Clients.Group(...).SendAsync("OrderStatusChanged", ...)
    Hub-->>Client: Real-time message (WebSocket)
    Dispatcher->>DB: UPDATE outbox_messages SET processed_at = now
  end

⚠️ Known gap (EP-09): RabbitMQEventBus.PublishAsync returns Task.CompletedTask — all events are silently dropped when RabbitMQ bus is active. InMemoryEventBus is the real implementation currently in use. Fix: wire real RabbitMQ publish in EP-09 S-09-06.

⚠️ Known gap (EP-06): DeviceRegistration aggregate and repository are implemented. FCM push notification dispatch (INotificationServiceClient) is a stub — push notifications are not delivered to mobile devices. Fix planned in EP-06.

Bouquet View Counter

sequenceDiagram
  participant App as Mobile App
  participant API as REST API
  participant Counter as BouquetViewCounterService
  participant Hub as BouquetHub (SignalR)
  participant Vendor as Vendor Portal (subscribed to vendor group)

  App->>Hub: SignalR: StartViewingBouquet(bouquetId)
  Hub->>Counter: BouquetViewCounterService.IncrementViewCount(bouquetId, connectionId)
  Counter->>Hub: SendAsync("BouquetViewCountChanged", bouquetId, count)
  Hub-->>Vendor: Real-time view count update on dashboard