Architecture

Events & Business Logic Domain Architecture

Last updated: 2026-02-01 | Architecture

Events & Business Logic Domain Architecture

Key Takeaways

  1. Shoutout is a complex BPM-orchestrated workflow — CIB Seven 2.0 manages the full lifecycle from offer creation → purchase → celebrity recording → media generation (FFmpeg/Mux) → admin review → fan delivery. In-flight BPM instances are a migration constraint.
  2. Inventory is the product catalog hub — 36 Flyway migrations, 7+ core entities (InventoryItem, Purchase, Entitlement, PurchaseCode, MembershipDiscount). All purchasable items across the platform (shoutouts, classes, events, subscriptions) reference inventory.
  3. Class-Catalog is the most complex service in this domain — 32 migrations, 15+ entities covering courses, sections, sessions, office hours, appointments, tickets, and learning credits (CEU/PDU). Contains a deprecated Arlo LMS integration.
  4. Gen 1 services are fully replaceable — peeq-meet-and-greet-bpm (Camunda 7.17, Jitsi) and peeq-custom-tixr (Tixr ticketing) are Gen 1 with no Gen 2 activity. Onsite-event Gen 2 is a minimal superset of peeq-onsite-event Gen 1.
  5. No brand-specific backend logic in any events domain service — confirms H11 pattern across now 5 domains.

Migration Decision Question

What event/shoutout workflows need to be preserved in Gen 3?

Migration Verdict

Verdict: Consolidate Complexity: L Key Constraint: CIB Seven BPM orchestrates shoutout fulfillment — in-flight process instances must drain or migrate. Inventory is a cross-cutting dependency for billing, subscriptions, and class-catalog. Dependencies: Stripe (purchase flow), wallet (coin credit), email (delivery notifications), media (Mux video), purchase-request-bpm (purchase approval), SSE (real-time updates)

Per-Service Verdicts

Service Verdict Rationale
Shoutout + Shoutout-BPM Consolidate → single service BPM logic can be simplified to state machine; FFmpeg/Mux processing stays
Inventory Upgrade Central product catalog — too many dependents to rewrite; upgrade in place
Class-Catalog Upgrade Complex schema (32 migrations), unique domain — upgrade, remove Arlo dead code
Onsite-Event Upgrade Simple (2 migrations) — minimal effort to upgrade
peeq-meet-and-greet-bpm Retire Gen 1 Camunda 7.17, uses deprecated Jitsi, no Gen 2 replacement needed
peeq-custom-tixr Retire Gen 1 Tixr integration, no evidence of active use
peeq-onsite-event Retire Gen 1, replaced by Gen 2 onsite-event
peeq-shoutout-db Retire Gen 1 DB repo, replaced by Gen 2 shoutout Flyway

Services Inventory

Service Gen Deployed? Stack Purpose
shoutout 2 Yes (4/4 tenants) Java 21 / SB 3.5.4 Shoutout offer/order management + media generation
shoutout-bpm 2 Yes (4/4) Java 21 / SB 3.5.4 / CIB Seven 2.0 Shoutout fulfillment workflow orchestration
inventory 2 Yes (4/4) Java 21 / SB 3.5.4 Central product catalog + entitlements
class-catalog 2 Yes (4/4) Java 21 / SB 3.5.4 Course/class management + learning credits
onsite-event 2 Yes (4/4) Java 21 / SB 3.5.4 In-person event check-in

Not in Production

Service Status Notes
peeq-meet-and-greet-bpm (Gen 1) Inactive Camunda 7.17, Jitsi video conference deployment
peeq-custom-tixr (Gen 1) Inactive Tixr ticketing integration
peeq-onsite-event (Gen 1) Replaced Gen 2 onsite-event is superset
peeq-shoutout-db (Gen 1) Replaced Schema managed by Gen 2 shoutout service

API Surface

Shoutout Service (GraphQL — 15+ operations)

Offer Management: createShoutoutOffer, updateShoutoutOffer, deleteShoutoutOffer, getShoutoutOffers, getShoutoutOffersByCelebrity Order Management: createShoutoutOrder, getShoutoutOrders, getShoutoutOrdersByFan, getShoutoutOrdersByCelebrity, updateShoutoutOrderStatus Media: uploadShoutoutVideo, getShoutoutVideo, generateShoutoutMedia Admin: adminGetShoutoutOrders, adminUpdateShoutoutOrder

Shoutout-BPM Service (REST + RabbitMQ — no GraphQL)

REST Endpoints: Process start/status/callback endpoints for CIB Seven BPM Process: ShoutoutFulfillment.bpmn — video recording → review → approval/rejection with expiration timers Message Handlers: Receives shoutout order events, publishes fulfillment status updates

Inventory Service (GraphQL — 11+ controllers)

Item Management: createInventoryItem, updateInventoryItem, getInventoryItem, getInventoryItems, getInventoryItemsByType Purchase Operations: createPurchase, getPurchase, getPurchasesByUser, getPurchasesByItem Entitlements: createEntitlement, getEntitlements, getEntitlementsByUser, validateEntitlement Purchase Codes: createPurchaseCode, validatePurchaseCode, redeemPurchaseCode Membership Discounts: createMembershipDiscount, getMembershipDiscounts, validateMembershipDiscount Exchange Rates: getExchangeRate, setExchangeRate (USD ↔︎ Peeq coin conversion)

Class-Catalog Service (GraphQL — 20+ operations)

Course Management: createCourse, updateCourse, getCourse, getCourses, getCoursesByCategory Section Management: createSection, updateSection, getSections Session Management: createSession, updateSession, getSessions, getSessionsBySection Attendee Tracking: registerAttendee, getAttendees, getAttendeesBySession, updateAttendeeStatus Office Hours: createOfficeHours, getOfficeHours, bookAppointment, getAppointments Tickets: createTicket, getTickets, validateTicket Learning Credits: issueLearningCredit, getLearningCredits, getLearningCreditsByUser (CEU/PDU tracking)

Onsite-Event Service (GraphQL — minimal)

Event Check-In: checkInAttendee, getCheckIns, getCheckInsByEvent

Who Calls This Domain? Who Does It Call?

graph LR
    subgraph "Events Domain"
        SH[Shoutout]
        SBP[Shoutout-BPM]
        INV[Inventory]
        CC[Class-Catalog]
        OE[Onsite-Event]
    end

    subgraph "Callers"
        FE1[peeq-mono]
        FE2[frontends]
        PBP[Purchase-Request-BPM]
        STR[Stripe]
    end

    subgraph "Called Services"
        MUX[Mux API]
        FFMPEG[FFmpeg]
        EMAIL[Email]
        SSE[SSE]
        WAL[Wallet]
        KC[Keycloak]
        TAGS[Tags]
        TRACK[Tracking]
    end

    FE1 -->|GraphQL| SH & INV & CC & OE
    FE2 -->|GraphQL| SH & INV & CC & OE
    PBP -->|RabbitMQ| INV
    STR -->|RabbitMQ| INV

    SH -->|REST| MUX
    SH -->|Process| FFMPEG
    SH -->|Keycloak Admin| KC
    SBP -->|RabbitMQ| SH & EMAIL & WAL
    INV -->|GraphQL| TAGS
    INV -->|REST| TRACK
    CC -->|Keycloak Admin| KC
    CC -->|GraphQL| TAGS

    SH -.->|RabbitMQ| SBP & SSE & EMAIL
    INV -.->|RabbitMQ| SSE
    CC -.->|RabbitMQ| SSE

API Backward Compatibility Constraints

Data Model

Shoutout Schema (23 Flyway migrations, 6 entities)

erDiagram
    shoutout_offers {
        uuid id PK
        uuid celebrity_user_id
        string title
        string description
        int price_in_coins
        string status
        int max_duration_seconds
        date created_on
    }
    shoutout_orders {
        uuid id PK
        uuid offer_id FK
        uuid fan_user_id
        uuid celebrity_user_id
        string instructions
        string recipient_name
        string occasion
        string status
        date created_on
        date completed_on
        date expires_on
    }
    shoutout_videos {
        uuid id PK
        uuid order_id FK
        string mux_asset_id
        string mux_playback_id
        string source_url
        int duration_seconds
        string status
    }
    shoutout_media {
        uuid id PK
        uuid order_id FK
        string type
        string url
        string status
    }
    shoutout_reviews {
        uuid id PK
        uuid order_id FK
        uuid reviewer_id
        string decision
        string notes
        date reviewed_on
    }
    shoutout_notifications {
        uuid id PK
        uuid order_id FK
        string type
        string status
        date sent_on
    }

    shoutout_offers ||--o{ shoutout_orders : "purchased as"
    shoutout_orders ||--o| shoutout_videos : "recorded"
    shoutout_orders ||--o{ shoutout_media : "generated"
    shoutout_orders ||--o| shoutout_reviews : "reviewed"
    shoutout_orders ||--o{ shoutout_notifications : "notified"

Inventory Schema (36 Flyway migrations, 7+ entities)

erDiagram
    inventory_items {
        uuid id PK
        string type
        string name
        string description
        int price_in_coins
        uuid celebrity_user_id
        string status
        string metadata
        date created_on
    }
    purchases {
        uuid id PK
        uuid inventory_item_id FK
        uuid user_id
        int quantity
        int total_coins
        string status
        date purchased_on
    }
    entitlements {
        uuid id PK
        uuid purchase_id FK
        uuid user_id
        uuid inventory_item_id FK
        string type
        string status
        date granted_on
        date expires_on
    }
    purchase_codes {
        uuid id PK
        string code UK
        uuid inventory_item_id FK
        string type
        int discount_percent
        int max_uses
        int current_uses
        date valid_from
        date valid_until
    }
    membership_discounts {
        uuid id PK
        string membership_type
        uuid inventory_item_id FK
        int discount_percent
        string status
    }
    exchange_rates {
        uuid id PK
        string from_currency
        string to_currency
        decimal rate
        date effective_from
    }
    inventory_item_images {
        uuid id PK
        uuid inventory_item_id FK
        string url
        int display_order
    }

    inventory_items ||--o{ purchases : "bought"
    inventory_items ||--o{ entitlements : "grants"
    inventory_items ||--o{ purchase_codes : "discounted by"
    inventory_items ||--o{ membership_discounts : "discounted by"
    inventory_items ||--o{ inventory_item_images : "has"
    purchases ||--o{ entitlements : "creates"

Class-Catalog Schema (32 Flyway migrations, 15+ entities)

erDiagram
    courses {
        uuid id PK
        string title
        string description
        uuid celebrity_user_id
        string status
        string category
        int ceu_credits
        int pdu_credits
        date created_on
    }
    sections {
        uuid id PK
        uuid course_id FK
        string title
        int sequence_order
        date start_date
        date end_date
    }
    sessions {
        uuid id PK
        uuid section_id FK
        string title
        string type
        date scheduled_at
        int duration_minutes
        string meeting_url
        string status
    }
    attendees {
        uuid id PK
        uuid session_id FK
        uuid user_id
        string status
        date registered_on
        date attended_on
    }
    tickets {
        uuid id PK
        uuid user_id
        uuid session_id FK
        string ticket_code
        string status
        date issued_on
    }
    office_hours {
        uuid id PK
        uuid celebrity_user_id
        string title
        string description
        int duration_minutes
        int max_attendees
        string recurrence_rule
    }
    appointments {
        uuid id PK
        uuid office_hours_id FK
        uuid user_id
        date scheduled_at
        string status
    }
    learning_resources {
        uuid id PK
        uuid course_id FK
        string title
        string type
        string url
        int sequence_order
    }
    learning_credits {
        uuid id PK
        uuid user_id
        uuid course_id FK
        string credit_type
        decimal credits_earned
        date issued_on
        string certificate_url
    }

    courses ||--o{ sections : contains
    sections ||--o{ sessions : contains
    sessions ||--o{ attendees : "attended by"
    sessions ||--o{ tickets : "ticketed"
    courses ||--o{ learning_resources : includes
    courses ||--o{ learning_credits : "awards"
    office_hours ||--o{ appointments : "booked"

Onsite-Event Schema (2 Flyway migrations, 2 entities)

erDiagram
    onsite_events {
        uuid id PK
        string name
        string location
        date event_date
    }
    onsite_check_ins {
        uuid id PK
        uuid event_id FK
        uuid user_id
        date checked_in_at
    }

    onsite_events ||--o{ onsite_check_ins : "tracks"

Data Migration Specifics

In-Flight State

External Integrations

Integration Service Purpose Migration Impact
Mux Shoutout Video asset upload + playback API key migration, webhook URL update
FFmpeg Shoutout Local video compositing (watermarks, intros) Binary dependency on deployment image
Keycloak Shoutout, Class-Catalog User profile lookup, admin API Same as identity domain
Arlo LMS Class-Catalog Learning management sync (deprecated) Remove dead integration code
Tixr peeq-custom-tixr (Gen 1) Ticketing integration Already inactive — no migration needed
Jitsi peeq-meet-and-greet-bpm (Gen 1) Video conferencing Already inactive — no migration needed

Inter-Service Communication

Synchronous (REST/GraphQL)

From To Protocol Purpose
Shoutout Keycloak Admin API Celebrity/fan profile lookup
Shoutout Mux REST Video upload/status
Inventory Tags GraphQL Tag lookup for product categorization
Inventory Tracking REST Short link creation
Class-Catalog Keycloak Admin API User info retrieval
Class-Catalog Tags GraphQL Course categorization
Purchase-Request-BPM Inventory RabbitMQ→GraphQL Purchase fulfillment triggers entitlement creation

Asynchronous (RabbitMQ)

Publisher Message Consumer Exchange
Shoutout ShoutoutOrderCreated Shoutout-BPM, Email, SSE shoutout
Shoutout ShoutoutOrderCompleted Email, Wallet, SSE shoutout
Shoutout ShoutoutOrderExpired Wallet (refund), Email shoutout
Shoutout ShoutoutVideoUploaded Shoutout-BPM shoutout
Shoutout ShoutoutReviewDecision Email, SSE shoutout
Shoutout-BPM ShoutoutFulfillmentStarted Shoutout shoutout-bpm
Shoutout-BPM ShoutoutFulfillmentCompleted Shoutout, Email shoutout-bpm
Shoutout-BPM ShoutoutExpired Shoutout, Wallet shoutout-bpm
Inventory PurchaseCreated SSE, Email inventory
Inventory EntitlementGranted SSE inventory
Inventory PurchaseCodeRedeemed SSE inventory
Class-Catalog SessionScheduled SSE, Email class-catalog
Class-Catalog AttendeeRegistered SSE class-catalog
Class-Catalog LearningCreditIssued SSE, Email class-catalog

Gen 1 → Gen 2 Comparison

Aspect Gen 1 Gen 2 Gap
Shoutout N/A (Gen 2 only) Full BPM workflow + media gen No Gen 1 equivalent
Inventory N/A (Gen 2 only) Central product catalog No Gen 1 equivalent
Class-Catalog N/A (Gen 2 only) Courses + learning credits No Gen 1 equivalent
Onsite-Event peeq-onsite-event (Java 11/SB 2.6.8) Gen 2 (Java 21/SB 3.5.4) Gen 2 is minimal superset
Meet & Greet peeq-meet-and-greet-bpm (Camunda 7.17) No Gen 2 equivalent Feature deprecated — Jitsi replaced by Zoom webinar
Ticketing peeq-custom-tixr (Tixr API) No Gen 2 equivalent Tixr integration inactive
Framework SPQR GraphQL, javax.persistence Spring Native GraphQL, jakarta.persistence Standard Gen 1→2 migration
BPM Camunda 7.17 CE CIB Seven 2.0 CIB Seven is fork of Camunda 7
Test Coverage ~0 Very low (2-3 test files per service) Both generations lack testing

Key finding: Most events domain services (shoutout, inventory, class-catalog) have NO Gen 1 predecessor — they were built directly as Gen 2. Only onsite-event and meet-and-greet have Gen 1 versions, and meet-and-greet has been deprecated.

Shoutout Fulfillment Workflow

The shoutout workflow is the most complex BPM process in this domain:

stateDiagram-v2
    [*] --> OfferCreated: Celebrity creates offer
    OfferCreated --> OrderPlaced: Fan purchases (coins deducted)
    OrderPlaced --> CelebrityNotified: Email + push notification
    CelebrityNotified --> Recording: Celebrity records video
    CelebrityNotified --> Expired: Timer expires (deadline passed)
    Recording --> VideoUploaded: Upload to Mux
    VideoUploaded --> MediaGeneration: FFmpeg compositing
    MediaGeneration --> PendingReview: Admin review queue
    PendingReview --> Approved: Admin approves
    PendingReview --> Rejected: Admin rejects
    Approved --> FanDelivery: Email + in-app notification
    FanDelivery --> [*]: Complete
    Rejected --> CelebrityNotified: Re-record requested
    Expired --> RefundProcessed: Coins returned to fan wallet
    RefundProcessed --> [*]: Cancelled

BPM Implementation: CIB Seven 2.0 BPMN process definition with service tasks, user tasks, timer events, and exclusive gateways. The BPM engine maintains process instance state in its own database tables.

Modernization Implications

Consolidation Opportunities

  1. Shoutout + Shoutout-BPM → single service: The BPM separation was necessary for Camunda’s deployment model, but Gen 3 could implement the workflow as a state machine within the shoutout service itself. The BPMN process has ~10 states — manageable without a full BPM engine.

  2. Inventory stays separate: As the product catalog hub referenced by billing, subscriptions, class-catalog, and shoutout, inventory must remain a standalone service. Its 36-migration schema and 11+ GraphQL controllers make it a core dependency.

  3. Class-Catalog stays separate: Unique domain complexity (15+ entities, learning credits, office hours) justifies standalone service. Remove deprecated Arlo LMS integration during upgrade.

  4. Onsite-Event could merge into class-catalog or inventory: With only 2 entities and minimal logic, onsite-event could be absorbed. However, the overhead of merging may not justify the effort given its simplicity.

BPM Strategy Decision

The shoutout-bpm service raises a strategic question about BPM engine dependency: - Current: CIB Seven 2.0 (fork of Camunda 7 CE, community support ending) - Option A: Replace with simple state machine in application code (~10 states, manageable) - Option B: Upgrade to Camunda 8 (major architecture change — Zeebe broker) - Option C: Keep CIB Seven, accept maintenance risk

Recommendation: Option A for shoutout (simple enough). Evaluate purchase-request-bpm separately (Session 4 already analyzed — similar complexity). If both BPM processes are <15 states each, eliminate BPM engine dependency entirely.

Dead Code Removal

Migration Risks

  1. In-flight shoutout orders: Celebrity may have recorded but not yet reviewed, or fan is waiting for delivery. Must implement drain period or state migration.
  2. FFmpeg dependency: Binary dependency tied to Docker image. Gen 3 could externalize to a media processing queue (align with content/media service pattern).
  3. Inventory as migration bottleneck: Multiple services depend on inventory GraphQL API. Must maintain backward compatibility during any migration phase.
  4. Learning credit data: CEU/PDU certificates reference historical course completions. Data must be preserved with referential integrity.

Last updated: 2026-01-30 — Session 6 Review by: 2026-07-30 Staleness risk: Medium — BPM engine decision and class-catalog evolution may change