Architecture Comprehensive overview of ShipKit architecture, including system design, data flow, component structure, and technical decisions
Architecture Overview
This document provides a comprehensive overview of ShipKit's architecture, explaining the system design, data flow, component structure, and key technical decisions.
System Architecture
graph TD
Client[Client Browser] --> NextJS[Next.js App Router]
NextJS --> API[API Routes]
NextJS --> Pages[Pages/Components]
API --> Auth[Auth Service]
API --> DB[Database]
API --> Cache[Redis Cache]
API --> Storage[Blob Storage]
API --> Queue[Job Queue]
Queue --> Workers[Background Workers]
Workers --> Email[Email Service]
Workers --> Analytics[Analytics Service]
Directory Structure
shipkit/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── (app)/ # Protected routes
│ │ │ ├── dashboard/ # Dashboard pages
│ │ │ ├── settings/ # Settings pages
│ │ │ └── admin/ # Admin pages
│ │ ├── (auth)/ # Auth routes
│ │ │ ├── signin/ # Sign in page
│ │ │ └── signup/ # Sign up page
│ │ ├── (marketing)/ # Public routes
│ │ │ ├── blog/ # Blog pages
│ │ │ └── pricing/ # Pricing page
│ │ └── api/ # API routes
│ ├── components/ # React components
│ │ ├── ui/ # UI components
│ │ ├── forms/ # Form components
│ │ └── layouts/ # Layout components
│ ├── lib/ # Utility functions
│ │ ├── auth/ # Auth utilities
│ │ ├── db/ # Database utilities
│ │ └── utils/ # Helper functions
│ ├── server/ # Server-side code
│ │ ├── actions/ # Server actions
│ │ ├── api/ # API handlers
│ │ └── db/ # Database utilities
│ └── styles/ # Global styles
├── public/ # Static assets
├── prisma/ # Database schema
├── tests/ # Test files
└── scripts/ # Build scripts
Data Flow
Authentication Flow
sequenceDiagram
participant User
participant Client
participant NextAuth
participant Database
participant Email
User->>Client: Enter credentials
Client->>NextAuth: Submit credentials
NextAuth->>Database: Verify user
Database-->>NextAuth: User data
NextAuth->>Client: Session token
NextAuth->>Email: Send welcome email
Client->>User: Redirect to dashboard
API Request Flow
sequenceDiagram
participant Client
participant Middleware
participant API
participant Cache
participant Database
Client->>Middleware: API request
Middleware->>Middleware: Validate token
Middleware->>Cache: Check cache
Cache-->>Middleware: Cache hit/miss
alt Cache miss
Middleware->>API: Forward request
API->>Database: Query data
Database-->>API: Return data
API->>Cache: Update cache
API-->>Client: Send response
else Cache hit
Cache-->>Client: Send cached response
end
Component Architecture
UI Components
// src/components/ui/button.tsx
import { type ComponentProps } from 'react'
import { cva, type VariantProps } from 'class-variance-authority'
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
interface ButtonProps
extends ComponentProps<'button'>,
VariantProps<typeof buttonVariants> {
loading?: boolean
}
export const Button = ({
className,
variant,
size,
loading = false,
children,
...props
}: ButtonProps) => {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
disabled={loading}
{...props}
>
{loading ? <Spinner className="mr-2" /> : null}
{children}
</button>
)
}
Layout Components
// src/components/layouts/dashboard-layout.tsx
import { type PropsWithChildren } from 'react'
import { Sidebar } from '@/components/navigation/sidebar'
import { Header } from '@/components/navigation/header'
interface DashboardLayoutProps extends PropsWithChildren {
title: string
}
export const DashboardLayout = ({
title,
children,
}: DashboardLayoutProps) => {
return (
<div className="min-h-screen bg-background">
<Header />
<div className="flex">
<Sidebar />
<main className="flex-1 p-6">
<h1 className="text-3xl font-bold mb-6">{title}</h1>
{children}
</main>
</div>
</div>
)
}
Database Schema
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(cuid())
email String @unique
name String?
password String?
image String?
role Role @default(USER)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
accounts Account[]
sessions Session[]
posts Post[]
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model Post {
id String @id @default(cuid())
title String
content String? @db.Text
published Boolean @default(false)
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
author User @relation(fields: [authorId], references: [id])
}
enum Role {
USER
ADMIN
}
API Routes
Route Handlers
// src/app/api/posts/route.ts
import { z } from 'zod'
import { getServerSession } from 'next-auth'
import { NextResponse } from 'next/server'
import { db } from '@/server/db'
const postSchema = z.object({
title: z.string().min(1).max(255),
content: z.string().optional(),
published: z.boolean().default(false),
})
export async function POST(req: Request) {
try {
const session = await getServerSession()
if (!session?.user) {
return new NextResponse('Unauthorized', { status: 401 })
}
const json = await req.json()
const body = postSchema.parse(json)
const post = await db.post.create({
data: {
...body,
authorId: session.user.id,
},
})
return NextResponse.json(post, { status: 201 })
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(error.issues, { status: 422 })
}
return new NextResponse('Internal Error', { status: 500 })
}
}
Server Actions
// src/server/actions/posts.ts
'use server'
import { revalidatePath } from 'next/cache'
import { getServerSession } from 'next-auth'
import { db } from '@/server/db'
export async function createPost(data: {
title: string
content?: string
published?: boolean
}) {
const session = await getServerSession()
if (!session?.user) {
throw new Error('Unauthorized')
}
const post = await db.post.create({
data: {
...data,
authorId: session.user.id,
},
})
revalidatePath('/dashboard/posts')
return post
}
State Management
React Query Configuration
// src/lib/query.ts
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 minute
gcTime: 5 * 60 * 1000, // 5 minutes
retry: 1,
refetchOnWindowFocus: false,
},
},
})
Data Fetching
// src/hooks/use-posts.ts
import { useQuery } from '@tanstack/react-query'
import { getPosts } from '@/lib/api'
export function usePosts() {
return useQuery({
queryKey: ['posts'],
queryFn: getPosts,
})
}
Technical Decisions
Framework Choice
Next.js App Router : For server-side rendering, API routes, and file-based routing
React Server Components : For improved performance and reduced client-side JavaScript
TypeScript : For type safety and improved developer experience
Tailwind CSS : For utility-first styling and consistent design system
Prisma : For type-safe database access and schema management
NextAuth.js : For authentication and session management
React Query : For server state management and data fetching
Zod : For runtime type validation and schema definition
Database Choice
PostgreSQL : For relational data and ACID compliance
Redis : For caching and real-time features
Vercel Blob Storage : For file uploads and media storage
Testing Strategy
Jest : For unit and integration testing
Playwright : For end-to-end testing
MSW : For API mocking
Testing Library : For component testing
Deployment Strategy
Vercel : For production deployment and preview environments
GitHub Actions : For CI/CD pipeline
Docker : For local development and testing
Future Considerations
Scalability
Horizontal scaling with containerization
Microservices architecture
Edge computing capabilities
Performance
Edge caching
Static site generation
Image optimization
Features
Real-time collaboration
Offline support
Mobile applications
Infrastructure
Multi-region deployment
Disaster recovery
Automated backups