Architecture Deployment API

Customer Persona Flows

1. Browse Bouquets Nearby / QR Scan

sequenceDiagram
  participant App as Mobile App
  participant API as REST API
  participant Cache as Redis Cache
  participant DB as PostgreSQL
  participant GEO as Nominatim

  App->>API: GET /api/v1/bouquets/nearby?lat=&lng=&radius=
  API->>Cache: GetAsync(BouquetsNearby:lat:lng:radius:page)
  alt cache hit
    Cache-->>API: Cached result
  else cache miss
    API->>DB: Geo query — bouquets within radius, status=Available
    DB-->>API: Bouquet list with vendor + vase location
    API->>Cache: SetAsync(result, ttl=5min)
  end
  API-->>App: 200 [{bouquetId, name, price, distance, vendorName, photoUrl}]

  App->>API: GET /api/v1/bouquets/{id}/detail
  API->>DB: Bouquet + vendor + vase details
  API-->>App: 200 {detail, feedbackSummary, vendorProfile}

  Note over App,GEO: QR Scan path
  App->>API: GET /api/v1/bouquets/qr/{code}
  API->>DB: Resolve bouquet by QR code
  API-->>App: 200 Bouquet detail (same as above)

2. Cart Management

sequenceDiagram
  participant App as Mobile App
  participant API as REST API
  participant DB as PostgreSQL

  App->>API: GET /api/v1/cart/me
  API->>DB: Cart for customerId
  API-->>App: 200 {items, subtotal, vendorId}

  App->>API: POST /api/v1/cart/items {bouquetId, quantity}
  API->>DB: Load cart + bouquet
  alt different vendor
    API-->>App: 409 vendor-mismatch
  else same vendor or empty cart
    API->>DB: Add CartItem, update subtotal
    API-->>App: 200 Updated cart
  end

  App->>API: DELETE /api/v1/cart/items/{itemId}
  API->>DB: Remove CartItem
  API-->>App: 200 Updated cart

  App->>API: PATCH /api/v1/cart/items/{itemId} {quantity}
  API->>DB: Update CartItem quantity
  API-->>App: 200 Updated cart

3. Order + Payment

sequenceDiagram
  participant App as Mobile App
  participant API as REST API
  participant DB as PostgreSQL
  participant Stripe
  participant Bus as InMemoryEventBus
  participant Hub as BouquetHub (SignalR)

  App->>API: POST /api/v1/orders/preview
  API->>DB: Check bouquet prices + availability
  API-->>App: 200 {lines, subtotal, deliveryAddress}

  App->>API: POST /api/v1/orders {deliveryAddress}
  API->>DB: Create Order, set status=PendingPayment
  API->>DB: Reserve bouquets (status=Reserved, reservedUntil=+15min)
  API-->>App: 201 {orderId, clientSecret}

  App->>Stripe: Confirm payment intent (clientSecret)
  Stripe-->>API: POST /api/stripe/webhook (payment_intent.succeeded)
  API->>DB: Order status → Confirmed, bouquets → Sold
  API->>Bus: Publish OrderPaidEvent
  Bus->>Hub: SendAsync("OrderStatusChanged", orderId, status)
  Hub-->>App: Real-time: order confirmed

  note over DB,Stripe: Error: payment declined → reservation expires after 15min
  note over DB,Stripe: Error: price changed between preview and create → 409 price-conflict

4. Delivery Confirmation + Feedback

sequenceDiagram
  participant App as Mobile App
  participant API as REST API
  participant DB as PostgreSQL
  participant Bus as InMemoryEventBus

  App->>API: POST /api/v1/orders/{id}/confirm-delivery
  API->>DB: Order status → Delivered
  API->>Bus: Publish OrderDeliveredEvent
  API-->>App: 200 OK

  App->>API: GET /api/v1/orders/me/pending-feedback
  API->>DB: OrderLines where FeedbackSubmitted=false, order=Delivered
  API-->>App: 200 [{orderId, bouquetId, bouquetName}]

  App->>API: POST /api/v1/orders/{id}/lines/{bouquetId}/feedback {rating, comment, isAnonymous}
  API->>DB: Create BouquetFeedback, mark OrderLine.FeedbackSubmitted=true
  API->>Bus: Publish BouquetFeedbackSubmittedEvent
  API-->>App: 201 Created