Eventuall Architecture Overview

Eventuall is a live event platform built on Next.js 15 and Cloudflare's edge infrastructure. The system combines a monolithic frontend with microservice-style backend workers, connected by three real-time communication channels (video, chat, and state synchronization).

This section provides visual diagrams and high-level context. For detailed explanations of each layer, see the sub-pages below.

Deep Dives

  • Frontend Architecture — Next.js 15 App Router, Server vs Client Components, OpenNext.js on Cloudflare, and styling patterns
  • Backend Architecture — Cloudflare Workers, Hono routing, all four Durable Objects, queue processing, and webhook handling
  • Compositor and Recording — The recording pipeline: CloudcasterWorkflow, Coordinator lock management, Mux streaming, LiveKit web egress, composite page layouts, and participant clip generation
  • Database Architecture — Two-database design with D1 and Drizzle ORM, migration workflows, and schema conventions
  • Real-Time Architecture — LiveKit video, Twilio chat, PartyKit presence, recording pipeline, and connection lifecycle
  • Infrastructure and Deployment — Terraform provisioning, environment types, Cloudflare tunnels, secrets management, and Docker containers

System Architecture Diagram

graph TB
    subgraph "Client Layer"
        Browser["Browser
Next.js 15 App"] Mobile["Mobile Clients
(Future)"] end subgraph "Application Layer - Next.js" AppRouter["App Router
File-based Routing"] ServerComponents["Server Components
Data Fetching"] ClientComponents["Client Components
Interactivity"] ServerActions["Server Actions
Form Handling"] tRPCServer["tRPC Server
Type-safe API"] end subgraph "Authentication" NextAuth["NextAuth
Multiple Providers"] GoogleAuth["Google OAuth"] OTPAuth["OTP/SMS Auth"] end subgraph "Data Layer" DrizzleORM["Drizzle ORM
Type-safe Queries"] D1Database["Cloudflare D1
SQLite Database"] R2Storage["Cloudflare R2
Object Storage"] KVStore["Cloudflare KV
Metadata Store"] end subgraph "Real-time Services" LiveKit["LiveKit
Video/Audio"] Twilio["Twilio
Messaging"] PartyKit["PartyKit
WebSockets"] end subgraph "Worker Services" CFWorkers["Cloudflare Workers
Edge Computing"] DurableObjects["Durable Objects
Stateful Logic"] Queues["Cloudflare Queues
Event Processing"] end Browser --> AppRouter Mobile --> tRPCServer AppRouter --> ServerComponents AppRouter --> ClientComponents ServerComponents --> tRPCServer ClientComponents --> tRPCServer ClientComponents --> ServerActions tRPCServer --> NextAuth NextAuth --> GoogleAuth NextAuth --> OTPAuth tRPCServer --> DrizzleORM ServerActions --> DrizzleORM DrizzleORM --> D1Database ServerComponents --> R2Storage ServerActions --> KVStore ClientComponents --> LiveKit ClientComponents --> Twilio ClientComponents --> PartyKit tRPCServer --> CFWorkers CFWorkers --> DurableObjects DurableObjects --> Queues

Request Flow Diagrams

Server Component Data Flow

sequenceDiagram
    participant Browser
    participant NextJS as Next.js Server
    participant tRPC as tRPC Router
    participant DB as D1 Database

    Browser->>NextJS: Request Page
    NextJS->>NextJS: Render Server Component
    NextJS->>tRPC: api.event.getMyEvents()
    tRPC->>DB: Query Events
    DB-->>tRPC: Event Data
    tRPC-->>NextJS: Typed Response
    NextJS->>NextJS: Render HTML with Data
    NextJS-->>Browser: HTML Response
    Note over Browser: Hydration occurs

Client Component Interaction Flow

sequenceDiagram
    participant User
    participant ClientComp as Client Component
    participant tRPCClient as tRPC Client
    participant NextAPI as Next.js API Route
    participant tRPCServer as tRPC Server
    participant DB as Database

    User->>ClientComp: Click "Create Event"
    ClientComp->>tRPCClient: mutation.mutate()
    tRPCClient->>NextAPI: POST /api/trpc
    NextAPI->>tRPCServer: Execute Procedure
    tRPCServer->>tRPCServer: Validate Input (Zod)
    tRPCServer->>DB: Insert Event
    DB-->>tRPCServer: Success
    tRPCServer-->>NextAPI: Typed Response
    NextAPI-->>tRPCClient: JSON Response
    tRPCClient->>ClientComp: Update UI
    ClientComp->>User: Show Success

Server Action Flow

sequenceDiagram
    participant Form as Form Component
    participant ServerAction as Server Action
    participant SafeAction as next-safe-action
    participant Auth as NextAuth
    participant DB as Database

    Form->>ServerAction: Submit Form Data
    ServerAction->>SafeAction: Validate Schema
    SafeAction->>Auth: Check Session
    Auth-->>SafeAction: User Context
    SafeAction->>ServerAction: Execute Action
    ServerAction->>DB: Database Operation
    DB-->>ServerAction: Result
    ServerAction-->>Form: Return Result
    Form->>Form: Update UI State

Key Architectural Decisions

Decision Choice Why
Frontend framework Next.js 15 with App Router Server Components reduce client JS, streaming improves TTFB
API layer tRPC End-to-end type safety without code generation
Edge runtime Cloudflare Workers Global distribution, integrated D1/R2/KV services
Database Cloudflare D1 (SQLite) Co-located with Workers, no connection pooling needed
ORM Drizzle Type-safe SQL, lightweight, D1-compatible
Real-time video LiveKit Open-source WebRTC, scalable, self-hostable
Real-time chat Twilio Conversations Managed service, reliable message delivery
Real-time state PartyKit (Durable Objects) WebSocket with persistent state, Yjs CRDT support
Monorepo pnpm workspaces Shared code between webapp and workers, atomic changes
Infrastructure Terraform Per-environment isolation, reproducible setup

Database Schema Overview

erDiagram
    USERS ||--o{ USER_PROVIDERS : has
    USERS ||--o{ SESSIONS : has
    USERS ||--o{ ACCOUNT_OWNERSHIP : owns
    ACCOUNTS ||--o{ ACCOUNT_OWNERSHIP : owned_by
    ACCOUNTS ||--o{ EVENTS : hosts
    EVENTS ||--o{ ROOMS : contains
    EVENTS ||--o{ TICKETS : offers
    ROOMS ||--o{ ROOM_ACCESS : grants
    TICKETS ||--o{ ROOM_ACCESS : provides
    USERS ||--o{ ROOM_ACCESS : has
    EVENTS ||--o{ EVENT_PARTICIPANTS : has
    ROOMS ||--o{ VET_CONVERSATIONS : manages

    USERS {
        string id PK
        string email UK
        string name
        string displayName
        timestamp createdAt
    }

    ACCOUNTS {
        string id PK
        string name
        string logo
        timestamp createdAt
    }

    EVENTS {
        string id PK
        string accountId FK
        string name
        string status
        timestamp startsAt
        timestamp endsAt
    }

    ROOMS {
        string id PK
        string eventId FK
        string name
        string livekitRoomName
        boolean isMainRoom
    }

    TICKETS {
        string id PK
        string eventId FK
        string name
        boolean isUpgrade
        string skuId
    }

Deployment Architecture

graph LR
    subgraph "Development"
        LocalDev["Local Development
pnpm dev"] LocalDB["Local D1 Database"] LocalTunnel["Cloudflare Tunnel"] end subgraph "Git Worktrees" Worktree1["Feature Branch 1"] Worktree2["Feature Branch 2"] IsolatedInfra1["Isolated Infrastructure"] IsolatedInfra2["Isolated Infrastructure"] end subgraph "Preview Environment" PreviewApp["Preview Deployment"] PreviewDB["Preview D1 Database"] PreviewWorkers["Preview Workers"] end subgraph "Production" ProdApp["Production App
eventuall.live"] ProdDB["Production D1"] ProdWorkers["Production Workers"] ProdCDN["Cloudflare CDN"] end LocalDev --> LocalDB LocalDev --> LocalTunnel Worktree1 --> IsolatedInfra1 Worktree2 --> IsolatedInfra2 PreviewApp --> PreviewDB PreviewApp --> PreviewWorkers ProdApp --> ProdDB ProdApp --> ProdWorkers ProdApp --> ProdCDN