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

  1. Scalability

    • Horizontal scaling with containerization
    • Microservices architecture
    • Edge computing capabilities
  2. Performance

    • Edge caching
    • Static site generation
    • Image optimization
  3. Features

    • Real-time collaboration
    • Offline support
    • Mobile applications
  4. Infrastructure

    • Multi-region deployment
    • Disaster recovery
    • Automated backups