Architecture Deployment API

Vendor Persona Flows

1. Vendor Onboarding

sequenceDiagram
  participant Admin as Platform Admin
  participant API as REST API
  participant KC as Keycloak
  participant DB as PostgreSQL
  participant Email as EmailService

  Admin->>API: POST /api/v1/admin/vendors {name, type, email, ...}
  API->>DB: Create Vendor aggregate (FlowerShop or FreelanceFlorist)
  API->>KC: KeycloakAdminClient.CreateUserAsync(email, role=VendorAdmin)
  KC-->>API: userId
  API->>DB: Create User {role=VendorAdmin, vendorId}
  API->>Email: Send welcome email with verification link
  API-->>Admin: 201 {vendorId}

  Note over Admin,Email: Vendor receives email, verifies account
  Admin->>API: POST /api/v1/admin/vendors/{id}/assign-technician
  API->>DB: Assign technician to install vases

2. Bouquet Lifecycle

sequenceDiagram
  participant VP as Vendor Portal
  participant API as REST API
  participant DB as PostgreSQL
  participant QR as QrCodeGeneratorService

  VP->>API: POST /api/v1/bouquets {name, price, vaseId, photoUrl}
  API->>DB: Create Bouquet (status=Available)
  API->>QR: GenerateQrCode(bouquetId)
  QR-->>API: qrCodeData
  API->>DB: Update Bouquet.QrShortCode
  API-->>VP: 201 {bouquetId, qrCode}

  VP->>API: PUT /api/v1/bouquets/{id}/price {price}
  API->>DB: Update Bouquet.Price (only when status=Available)
  API-->>VP: 200 OK

  VP->>API: DELETE /api/v1/bouquets/{id}
  API->>DB: Bouquet status → Expired (soft delete)
  API-->>VP: 204 No Content

3. Bouquet Status State Machine

stateDiagram-v2
  [*] --> Available : POST /bouquets (created)
  Available --> Reserved : Order created (bouquet reserved 15min)
  Reserved --> Available : Reservation expired / order cancelled
  Reserved --> Sold : Payment confirmed (OrderPaidEvent)
  Available --> Expired : DELETE /bouquets or freshness=0
  Sold --> [*]
  Expired --> [*]

4. Order Fulfillment

sequenceDiagram
  participant VP as Vendor Portal
  participant API as REST API
  participant DB as PostgreSQL
  participant Bus as InMemoryEventBus
  participant Hub as BouquetHub (SignalR)

  VP->>API: GET /api/v1/vendor/orders?status=Confirmed
  API->>DB: Orders for vendorId where status=Confirmed
  API-->>VP: 200 [{orderId, customerName, lines, deliveryAddress}]

  VP->>API: PUT /api/v1/vendor/orders/{id}/status {status: Preparing}
  API->>DB: Order status → Preparing
  API->>Bus: Publish OrderStatusChangedEvent
  Bus->>Hub: SendAsync("OrderStatusChanged", orderId, status)
  API-->>VP: 200 OK

  VP->>API: PUT /api/v1/vendor/orders/{id}/status {status: InDelivery}
  API->>DB: Order status → InDelivery
  API->>Bus: Publish OrderStatusChangedEvent
  Bus->>Hub: SendAsync("OrderStatusChanged", orderId, status)
  API-->>VP: 200 OK

  note over DB,Hub: Error: vase offline → bouquet still sold, fulfillment continues