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/retryResponse 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 compatibilityTest 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 sanitizationPerformance 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:/dataMonitoring 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",
};