Architecture

Project architecture overview, file organization, and code structure patterns.

Architecture

Project Structure

Next.js App Router Architecture

The project follows Next.js 15's App Router convention with a clear separation of concerns:

app/
├── (app)/                # Protected routes group
│   ├── layout.tsx        # Authenticated user layout with sidebar
│   ├── dashboard/        # Dashboard and analytics
│   ├── inventory/        # Inventory management pages
│   ├── products/         # Product catalog management
│   ├── purchasing/       # Purchase order management
│   ├── sales/           # Sales and order management
│   ├── warehouse/       # Warehouse operations
│   ├── analytics/       # Reports and data visualization
│   └── admin/           # System administration
├── api/                 # API routes and endpoints
│   ├── auth/           # Authentication endpoints
│   ├── inventory/      # Inventory API routes
│   ├── products/       # Product management API
│   ├── orders/         # Order management API
│   └── analytics/      # Analytics and reporting API
├── auth/               # Authentication pages (login, register)
├── layout.tsx          # Root application layout
├── page.tsx           # Landing/home page
├── loading.tsx        # Global loading UI
├── error.tsx          # Global error boundary
├── not-found.tsx      # 404 page
└── globals.css        # Global styles and CSS variables

Component Architecture

components/
├── ui/                 # Base UI components (shadcn/ui)
│   ├── button.tsx
│   ├── card.tsx
│   ├── dialog.tsx
│   ├── form.tsx
│   ├── table.tsx
│   └── ...
├── layout/             # Layout components
│   ├── navbar.tsx
│   ├── sidebar.tsx
│   └── footer.tsx
├── dashboard/          # Dashboard-specific components
│   ├── stats-cards.tsx
│   ├── recent-orders.tsx
│   └── analytics-chart.tsx
├── inventory/          # Inventory management components
│   ├── inventory-table.tsx
│   ├── stock-level-badge.tsx
│   └── reorder-alert.tsx
├── products/           # Product management components
│   ├── product-form.tsx
│   ├── product-card.tsx
│   └── category-selector.tsx
├── barcode/           # Barcode scanning components
│   ├── barcode-scanner.tsx
│   └── scanner-controls.tsx
├── auth/              # Authentication components
│   ├── login-form.tsx
│   ├── register-form.tsx
│   └── auth-guard.tsx
└── shared/            # Shared utility components
    ├── data-table.tsx
    ├── loading-spinner.tsx
    ├── error-boundary.tsx
    └── confirmation-dialog.tsx

Service Layer Architecture

lib/
├── supabase/          # Database client configuration
│   ├── server.ts      # Server-side Supabase client
│   ├── client.ts      # Client-side Supabase client
│   └── middleware.ts  # Authentication middleware
├── services/          # Business logic services
│   ├── dashboard.ts   # Dashboard data aggregation
│   ├── inventory.ts   # Inventory operations
│   ├── products.ts    # Product management
│   ├── orders.ts      # Order processing
│   ├── analytics.ts   # Analytics and reporting
│   └── warehouse.ts   # Warehouse operations
├── actions/           # Server actions
│   ├── auth.ts        # Authentication actions
│   ├── inventory.ts   # Inventory actions
│   └── products.ts    # Product actions
├── hooks/             # Custom React hooks
│   ├── use-current-user.ts
│   ├── use-inventory.ts
│   └── use-products.ts
├── types/             # TypeScript type definitions
│   ├── database.ts    # Database schema types
│   ├── api.ts         # API response types
│   └── auth.ts        # Authentication types
├── validations/       # Zod validation schemas
│   ├── product.ts
│   ├── inventory.ts
│   └── auth.ts
├── utils/             # Utility functions
│   ├── cn.ts          # Class name utility
│   ├── format.ts      # Formatting utilities
│   └── date.ts        # Date utilities
└── constants/         # Application constants
    ├── routes.ts      # Route definitions
    ├── permissions.ts # Permission constants
    └── status.ts      # Status constants

Code Organization Principles

File Naming Conventions

Components

// Use PascalCase for component files
ProductCard.tsx         // ✅ Good
product-card.tsx        // ❌ Avoid
productCard.tsx         // ❌ Avoid

// Use kebab-case for pages and layouts
page.tsx                // ✅ Good (Next.js convention)
layout.tsx              // ✅ Good (Next.js convention)
inventory-list/page.tsx // ✅ Good (nested routes)

API Routes

// Use kebab-case for API route files
route.ts                // ✅ Good (Next.js convention)
products/route.ts       // ✅ Good
purchase-orders/route.ts // ✅ Good

Services and Utilities

// Use camelCase for service files
inventoryService.ts     // ✅ Good
authUtils.ts           // ✅ Good
databaseHelpers.ts     // ✅ Good

// Or kebab-case for consistency
inventory-service.ts    // ✅ Also good
auth-utils.ts          // ✅ Also good
database-helpers.ts    // ✅ Also good

Import Organization

Follow a consistent import order for better readability:

// 1. React and Next.js imports
import React, { useState, useEffect } from 'react'
import { NextRequest, NextResponse } from 'next/server'
import { redirect } from 'next/navigation'

// 2. Third-party libraries
import { createClient } from '@supabase/supabase-js'
import { z } from 'zod'
import { clsx } from 'clsx'

// 3. Internal utilities and services
import { createClient } from '@/lib/supabase/server'
import { getInventoryStats } from '@/lib/services/inventory'
import { formatCurrency } from '@/lib/utils/format'

// 4. UI Components (external first, then internal)
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader } from '@/components/ui/card'
import { InventoryTable } from '@/components/inventory/inventory-table'

// 5. Types and interfaces
import type { Database } from '@/lib/types/database'
import type { InventoryItem, Product } from '@/lib/types/inventory'

// 6. Constants and configurations
import { ROUTES } from '@/lib/constants/routes'
import { PERMISSIONS } from '@/lib/constants/permissions'

Code Style Guidelines

TypeScript Best Practices

// Use interfaces for object shapes
interface ProductFormData {
  name: string
  sku: string
  category_id: string
  cost_price: number
  selling_price: number
  description?: string
}

// Use type aliases for unions and primitives
type ProductStatus = 'active' | 'inactive' | 'discontinued'
type ProductId = string

// Use const assertions for immutable data
const PRODUCT_CATEGORIES = [
  'electronics',
  'clothing', 
  'books',
  'home'
] as const

type ProductCategory = typeof PRODUCT_CATEGORIES[number]

// Prefer async/await over promises
export async function getProducts(): Promise<Product[]> {
  try {
    const supabase = await createClient()
    const { data, error } = await supabase
      .from('products')
      .select('*')
      .eq('is_active', true)
      .order('name')
    
    if (error) throw error
    return data || []
  } catch (error) {
    console.error('Error fetching products:', error)
    return []
  }
}

Component Patterns

// Server Component (default - no 'use client')
export default async function ProductsPage() {
  const products = await getProducts()
  
  return (
    <div className="space-y-6">
      <PageHeader title="Products" />
      <ProductTable products={products} />
    </div>
  )
}

// Client Component (when needed)
'use client'

interface InteractiveComponentProps {
  initialData: any[]
}

export function InteractiveComponent({ initialData }: InteractiveComponentProps) {
  const [data, setData] = useState(initialData)
  
  // Client-side logic here
  
  return <div>{/* Interactive content */}</div>
}

Architectural Patterns

Service Layer Pattern

Centralize business logic in service modules:

// lib/services/inventory.ts
export class InventoryService {
  private supabase: SupabaseClient

  constructor(supabase: SupabaseClient) {
    this.supabase = supabase
  }

  async getInventoryStats(): Promise<InventoryStats> {
    // Complex business logic here
  }

  async updateStockLevel(productId: string, newLevel: number): Promise<void> {
    // Stock update logic with validation
  }

  async checkReorderPoints(): Promise<Product[]> {
    // Reorder point checking logic
  }
}

// Usage in API route or Server Component
const inventoryService = new InventoryService(supabase)
const stats = await inventoryService.getInventoryStats()

Repository Pattern

Abstract database operations:

// lib/repositories/product-repository.ts
export class ProductRepository {
  constructor(private supabase: SupabaseClient) {}

  async findById(id: string): Promise<Product | null> {
    const { data } = await this.supabase
      .from('products')
      .select('*')
      .eq('id', id)
      .single()
    
    return data
  }

  async findByCategory(categoryId: string): Promise<Product[]> {
    const { data } = await this.supabase
      .from('products')
      .select('*')
      .eq('category_id', categoryId)
      .eq('is_active', true)
    
    return data || []
  }

  async create(product: CreateProductInput): Promise<Product> {
    const { data } = await this.supabase
      .from('products')
      .insert(product)
      .select()
      .single()
    
    return data
  }
}

Error Handling Pattern

Consistent error handling across the application:

// lib/utils/error-handler.ts
export class AppError extends Error {
  constructor(
    message: string,
    public statusCode: number = 500,
    public code?: string
  ) {
    super(message)
    this.name = 'AppError'
  }
}

export function handleApiError(error: unknown): Response {
  if (error instanceof AppError) {
    return Response.json(
      { error: error.message, code: error.code },
      { status: error.statusCode }
    )
  }

  console.error('Unexpected error:', error)
  return Response.json(
    { error: 'Internal server error' },
    { status: 500 }
  )
}

Validation Pattern

Centralized validation with Zod:

// lib/validations/product.ts
import { z } from 'zod'

export const CreateProductSchema = z.object({
  name: z.string().min(1, 'Product name is required'),
  sku: z.string().min(1, 'SKU is required'),
  category_id: z.string().uuid('Invalid category ID'),
  cost_price: z.number().positive('Cost price must be positive'),
  selling_price: z.number().positive('Selling price must be positive'),
  description: z.string().optional(),
  barcode: z.string().optional(),
})

export const UpdateProductSchema = CreateProductSchema.partial()

export type CreateProductInput = z.infer<typeof CreateProductSchema>
export type UpdateProductInput = z.infer<typeof UpdateProductSchema>

This architecture provides a scalable, maintainable foundation for the application with clear separation of concerns and consistent patterns.