Design Document

Overview

The Universal Starter is architected as a modern, scalable foundation that enables startups to rapidly build and deploy full-stack applications. The design follows microservices principles with clear separation of concerns, leveraging cutting-edge technologies for optimal performance and developer experience. The system is built around a core philosophy of “batteries included but removable” - providing comprehensive functionality out of the box while maintaining flexibility for customization.

The architecture supports both web and native mobile applications through Expo’s universal platform, backed by a high-performance Elysia API running on Bun. Background processing is handled through BullMQ with Redis, ensuring reliable job execution and system scalability. The entire stack is designed for cloud-native deployment with Docker containers and infrastructure-as-code principles.

Architecture

System Architecture Overview

graph TB
    subgraph "Client Layer"
        A[Web App - Expo]
        B[iOS App - Expo]
        C[Android App - Expo]
        D[Admin Dashboard]
    end

    subgraph "API Gateway & Load Balancer"
        E[Load Balancer]
        F[Rate Limiting]
        G[SSL Termination]
    end

    subgraph "Application Layer"
        H[Elysia API Server]
        I[Authentication Service]
        J[Payment Service]
        K[File Service]
        L[Notification Service]
    end

    subgraph "Background Processing"
        M[BullMQ Workers]
        N[Job Scheduler]
        O[Email Worker]
        P[Billing Worker]
    end

    subgraph "Data Layer"
        Q[(PostgreSQL)]
        R[(Redis)]
        S[S3 Storage]
        T[CDN]
    end

    subgraph "External Services"
        U[Stripe API]
        V[Email Provider]
        W[OAuth Providers]
        X[Monitoring Services]
    end

    A --> E
    B --> E
    C --> E
    D --> E
    E --> H
    H --> I
    H --> J
    H --> K
    H --> L
    H --> M
    M --> Q
    M --> R
    H --> Q
    H --> R
    K --> S
    K --> T
    J --> U
    O --> V
    I --> W
    H --> X

Technology Stack Architecture

graph LR
    subgraph "Frontend Stack"
        A1[React Native]
        A2[Expo Router]
        A3[NativeWind]
        A4[Zustand/Legend State]
    end

    subgraph "Backend Stack"
        B1[Elysia Framework]
        B2[Bun Runtime]
        B3[TypeScript]
        B4[Prisma ORM]
    end

    subgraph "Infrastructure Stack"
        C1[Docker]
        C2[PostgreSQL]
        C3[Redis]
        C4[BullMQ]
    end

    subgraph "External Services"
        D1[Stripe Payments]
        D2[AWS S3]
        D3[Resend/Plunk Email]
        D4[Better Auth]
    end

    A1 --> B1
    B1 --> C2
    B1 --> C3
    B4 --> C2
    C4 --> C3
    B1 --> D1
    B1 --> D2
    B1 --> D3
    B1 --> D4

Components and Interfaces

Core Application Structure

apps/
├── api/                    # Elysia API server
│   ├── src/
│   │   ├── routes/        # API route handlers
│   │   ├── services/      # Business logic services
│   │   ├── middleware/    # Custom middleware
│   │   ├── workers/       # Background job workers
│   │   └── utils/         # Utility functions
│   └── package.json
├── frontend/              # Expo universal app
│   ├── app/              # App router pages
│   ├── components/       # Reusable UI components
│   ├── hooks/           # Custom React hooks
│   ├── services/        # API client services
│   └── package.json
└── admin/               # Admin dashboard (optional)
    ├── src/
    └── package.json

packages/
├── db/                  # Database package
│   ├── prisma/         # Prisma schema and migrations
│   └── src/            # Database utilities
├── shared/             # Shared types and utilities
├── logger/             # Logging utilities
├── queue-kit/          # BullMQ job definitions
└── redis/              # Redis utilities

API Design Patterns

RESTful API Structure

// Standard CRUD operations
GET    /api/v1/users           # List users
POST   /api/v1/users           # Create user
GET    /api/v1/users/:id       # Get user
PUT    /api/v1/users/:id       # Update user
DELETE /api/v1/users/:id       # Delete user
 
// Nested resources
GET    /api/v1/users/:id/subscriptions
POST   /api/v1/users/:id/subscriptions
 
// Actions on resources
POST   /api/v1/subscriptions/:id/cancel
POST   /api/v1/invoices/:id/retry

Response Format Standards

interface APIResponse<T> {
  success: boolean;
  data?: T;
  error?: {
    code: string;
    message: string;
    details?: any;
  };
  meta?: {
    pagination?: {
      page: number;
      limit: number;
      total: number;
    };
  };
}

Database Design Patterns

Core Entity Models

// User management
model User {
  id            String    @id @default(cuid())
  email         String    @unique
  name          String
  avatar        String?
  role          UserRole  @default(USER)
 
  // Relationships
  organizations UserOrganization[]
  subscriptions Subscription[]
  files         File[]
 
  // Timestamps
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
}
 
// Multi-tenancy
model Organization {
  id          String    @id @default(cuid())
  name        String
  slug        String    @unique
  settings    Json?
 
  // Relationships
  members     UserOrganization[]
  subscription Subscription?
 
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt
}
 
// File management
model File {
  id          String    @id @default(cuid())
  filename    String
  originalName String
  mimeType    String
  size        Int
  url         String
  userId      String
 
  user        User      @relation(fields: [userId], references: [id])
  createdAt   DateTime  @default(now())
}

Frontend Component Architecture

Component Hierarchy

// Layout components
<AppLayout>
  <Navigation />
  <MainContent>
    <PageHeader />
    <PageContent />
  </MainContent>
  <Footer />
</AppLayout>
 
// Feature components
<SubscriptionManager>
  <PlanSelector />
  <PaymentForm />
  <BillingHistory />
</SubscriptionManager>
 
// UI primitives
<Button variant="primary" size="lg" />
<Input type="email" validation={emailSchema} />
<Modal isOpen={isOpen} onClose={onClose} />

State Management Pattern

// Global state with Zustand/Legend State
interface AppState {
  user: User | null;
  organization: Organization | null;
  subscription: Subscription | null;
 
  // Actions
  setUser: (user: User) => void;
  setOrganization: (org: Organization) => void;
  updateSubscription: (sub: Subscription) => void;
}
 
// Feature-specific state
interface BillingState {
  invoices: Invoice[];
  paymentMethods: PaymentMethod[];
  loading: boolean;
 
  fetchInvoices: () => Promise<void>;
  addPaymentMethod: (method: PaymentMethod) => Promise<void>;
}

Data Models

User and Authentication Models

interface User {
  id: string;
  email: string;
  name: string;
  avatar?: string;
  role: "USER" | "ADMIN" | "SUPER_ADMIN";
  emailVerified: boolean;
  createdAt: Date;
  updatedAt: Date;
}
 
interface Session {
  id: string;
  userId: string;
  token: string;
  expiresAt: Date;
  ipAddress?: string;
  userAgent?: string;
}

Organization and Multi-tenancy Models

interface Organization {
  id: string;
  name: string;
  slug: string;
  settings: OrganizationSettings;
  createdAt: Date;
  updatedAt: Date;
}
 
interface UserOrganization {
  userId: string;
  organizationId: string;
  role: "OWNER" | "ADMIN" | "MEMBER";
  permissions: Permission[];
  joinedAt: Date;
}

File and Content Models

interface File {
  id: string;
  filename: string;
  originalName: string;
  mimeType: string;
  size: number;
  url: string;
  userId: string;
  organizationId?: string;
  createdAt: Date;
}
 
interface ContentPage {
  id: string;
  title: string;
  slug: string;
  content: string;
  status: "DRAFT" | "PUBLISHED";
  authorId: string;
  createdAt: Date;
  updatedAt: Date;
}

Error Handling

API Error Standards

enum ErrorCodes {
  VALIDATION_ERROR = "VALIDATION_ERROR",
  AUTHENTICATION_ERROR = "AUTHENTICATION_ERROR",
  AUTHORIZATION_ERROR = "AUTHORIZATION_ERROR",
  NOT_FOUND = "NOT_FOUND",
  RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED",
  INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR",
}
 
interface APIError {
  code: ErrorCodes;
  message: string;
  details?: Record<string, any>;
  timestamp: string;
  requestId: string;
}

Frontend Error Handling

// Global error boundary
<ErrorBoundary fallback={<ErrorFallback />}>
  <App />
</ErrorBoundary>
 
// API error handling
const handleAPIError = (error: APIError) => {
  switch (error.code) {
    case ErrorCodes.AUTHENTICATION_ERROR:
      // Redirect to login
      break;
    case ErrorCodes.VALIDATION_ERROR:
      // Show form errors
      break;
    default:
      // Show generic error toast
  }
};

Testing Strategy

Testing Pyramid

// Unit Tests (70%)
- Service layer logic
- Utility functions
- Component logic
- Database operations
 
// Integration Tests (20%)
- API endpoint testing
- Database integration
- External service mocking
- Worker job processing
 
// End-to-End Tests (10%)
- Critical user flows
- Payment processing
- Authentication flows
- Cross-platform compatibility

Test Organization

tests/
├── unit/
│   ├── services/
│   ├── utils/
│   └── components/
├── integration/
│   ├── api/
│   ├── database/
│   └── workers/
└── e2e/
    ├── auth/
    ├── payments/
    └── core-flows/

Security Architecture

Authentication and Authorization

// JWT-based authentication with refresh tokens
interface AuthTokens {
  accessToken: string; // Short-lived (15 minutes)
  refreshToken: string; // Long-lived (30 days)
}
 
// Role-based access control
interface Permission {
  resource: string;
  action: "CREATE" | "READ" | "UPDATE" | "DELETE";
  conditions?: Record<string, any>;
}

Security Middleware Stack

// API security layers
app.use(helmet()); // Security headers
app.use(cors(corsConfig)); // CORS configuration
app.use(rateLimit()); // Rate limiting
app.use(authenticate()); // JWT verification
app.use(authorize()); // Permission checking
app.use(validateInput()); // Input sanitization

Performance Optimization

Caching Strategy

// Multi-layer caching
interface CacheStrategy {
  // Application cache (Redis)
  redis: {
    userSessions: "15m";
    subscriptionData: "5m";
    organizationSettings: "1h";
  };
 
  // Database query optimization
  database: {
    connectionPooling: true;
    queryOptimization: true;
    indexStrategy: "comprehensive";
  };
 
  // CDN and static assets
  cdn: {
    staticAssets: "1y";
    userUploads: "30d";
    apiResponses: "5m";
  };
}

Background Job Optimization

// Job queue configuration
const queueConfig = {
  defaultJobOptions: {
    removeOnComplete: 100,
    removeOnFail: 50,
    attempts: 3,
    backoff: "exponential",
  },
 
  // Priority queues
  queues: {
    critical: { priority: 10 },
    normal: { priority: 5 },
    low: { priority: 1 },
  },
};

Deployment Architecture

Container Strategy

# Multi-stage Docker build
FROM oven/bun:1 as base
WORKDIR /app
 
# Dependencies stage
FROM base as deps
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
 
# Build stage
FROM base as build
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN bun run build
 
# Production stage
FROM base as production
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
EXPOSE 3000
CMD ["bun", "run", "start"]

Infrastructure as Code

# Docker Compose for development
version: "3.8"
services:
  api:
    build: ./apps/api
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/app
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
 
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - postgres_data:/var/lib/postgresql/data
 
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data

Monitoring and Observability

Logging Strategy

// Structured logging with Pino
const logger = pino({
  level: process.env.LOG_LEVEL || "info",
  formatters: {
    level: (label) => ({ level: label }),
  },
  timestamp: pino.stdTimeFunctions.isoTime,
});
 
// Request logging middleware
app.use(
  pinoHttp({
    logger,
    customLogLevel: (req, res) => {
      if (res.statusCode >= 400) return "error";
      if (res.statusCode >= 300) return "warn";
      return "info";
    },
  })
);

Metrics and Alerting

// Key metrics to track
interface SystemMetrics {
  // Performance metrics
  responseTime: number;
  throughput: number;
  errorRate: number;
 
  // Business metrics
  activeUsers: number;
  subscriptionRevenue: number;
  conversionRate: number;
 
  // Infrastructure metrics
  cpuUsage: number;
  memoryUsage: number;
  diskUsage: number;
  queueDepth: number;
}

Scalability Considerations

Horizontal Scaling Strategy

// Load balancing configuration
const loadBalancerConfig = {
  algorithm: "round-robin",
  healthCheck: {
    path: "/health",
    interval: "30s",
    timeout: "5s",
  },
 
  // Auto-scaling rules
  autoScaling: {
    minInstances: 2,
    maxInstances: 10,
    targetCPU: 70,
    targetMemory: 80,
  },
};

Database Scaling

// Database optimization strategy
const dbScalingStrategy = {
  // Read replicas for query distribution
  readReplicas: {
    count: 2,
    regions: ["us-east-1", "us-west-2"],
  },
 
  // Connection pooling
  connectionPool: {
    min: 5,
    max: 20,
    acquireTimeoutMillis: 30000,
  },
 
  // Query optimization
  indexStrategy: "comprehensive",
  queryAnalysis: "enabled",
};