Architecture Layers
Detailed overview of the system's layered architecture including client, application, and data layers.
Architecture Layers
The system follows a modern, layered architecture pattern that provides clear separation of concerns and maintainable code structure.
Client Layer
┌─────────────────────────────────────────────────────────────┐
│ Client Layer │
├─────────────────────────────────────────────────────────────┤
│ • React Components (TSX) │
│ • State Management (React Hooks) │
│ • UI Components (shadcn/ui) │
│ • Client-side Routing (Next.js App Router) │
│ • Real-time Subscriptions (Supabase Realtime) │
└─────────────────────────────────────────────────────────────┘
Responsibilities
- User Interface Rendering: Display components and manage visual state
- Client-side State Management: Handle local component state and global state
- User Interactions: Process user events and interactions
- Real-time Data Subscriptions: Manage live data updates from the server
- Client-side Routing: Handle navigation and route changes
Technologies and Components
React 18 with Concurrent Features
- Concurrent Rendering: Improved performance with time-slicing
- Suspense: Better loading state management
- Automatic Batching: Optimized state updates
- Transition API: Smooth user experience during updates
Next.js App Router
- File-based Routing: Intuitive route organization
- Nested Layouts: Reusable layout components
- Server Components: Hybrid rendering capabilities
- Route Groups: Organized route structure
TypeScript Integration
- Strong Typing: Compile-time error detection
- IntelliSense: Enhanced development experience
- Interface Definitions: Clear component contracts
- Generic Components: Reusable typed components
Styling and UI Components
- Tailwind CSS: Utility-first styling approach
- shadcn/ui Library: Consistent component system
- Responsive Design: Mobile-first approach
- Theme System: Dark/light mode support
Client-Side Architecture Patterns
Component Organization
// Component structure example
interface ProductListProps {
products: Product[]
onProductSelect: (product: Product) => void
loading?: boolean
}
export function ProductList({ products, onProductSelect, loading }: ProductListProps) {
// Component logic and rendering
}
State Management
// Custom hooks for state management
export function useProductList() {
const [products, setProducts] = useState<Product[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
// State management logic
return { products, loading, error, refetch }
}
Application Layer
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────┤
│ • Server Components & Server Actions │
│ • API Routes (/api/*) │
│ • Middleware (Authentication, CORS) │
│ • Business Logic Services │
│ • Data Validation & Transformation │
└─────────────────────────────────────────────────────────────┘
Responsibilities
- Business Logic Processing: Core application functionality
- Data Validation: Ensure data integrity and format
- API Endpoint Handling: Process HTTP requests and responses
- Authentication & Authorization: Secure access control
- Server-side Rendering: Pre-render pages for performance
Server Components Architecture
Server Components
// Server component example
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await getProduct(params.id)
return (
<div>
<ProductDetails product={product} />
<ProductInventory productId={params.id} />
</div>
)
}
Server Actions
// Server action for form handling
export async function createProduct(formData: FormData) {
'use server'
const validatedData = await validateProductData(formData)
const result = await productService.create(validatedData)
revalidatePath('/products')
return result
}
API Route Structure
// API route example
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const category = searchParams.get('category')
const products = await productService.getByCategory(category)
return NextResponse.json({ success: true, data: products })
} catch (error) {
return NextResponse.json({ success: false, error: error.message }, { status: 500 })
}
}
Middleware Architecture
// Authentication middleware
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')
if (!token && isProtectedRoute(request.nextUrl.pathname)) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
Data Layer
┌─────────────────────────────────────────────────────────────┐
│ Data Layer │
├─────────────────────────────────────────────────────────────┤
│ • Supabase Client │
│ • PostgreSQL Database │
│ • Row Level Security (RLS) │
│ • Real-time Engine │
│ • Edge Functions │
└─────────────────────────────────────────────────────────────┘
Responsibilities
- Data Persistence: Store and retrieve application data
- Real-time Synchronization: Live data updates across clients
- User Authentication: Secure user identity management
- Database Security: Row-level security policies
- Edge Computing: Distributed function execution
Supabase Integration
Database Client
// Supabase client configuration
import { createClient } from '@supabase/supabase-js'
import type { Database } from './database.types'
export const supabase = createClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
Typed Database Queries
// Type-safe database operations
export async function getProducts(category?: string) {
let query = supabase
.from('products')
.select(`
id,
name,
price,
category:categories(name),
inventory:inventory_items(quantity)
`)
if (category) {
query = query.eq('category_id', category)
}
const { data, error } = await query
if (error) throw new Error(error.message)
return data
}
Real-time Subscriptions
// Real-time data subscription
export function useRealtimeProducts() {
const [products, setProducts] = useState<Product[]>([])
useEffect(() => {
const channel = supabase
.channel('products')
.on('postgres_changes',
{ event: '*', schema: 'public', table: 'products' },
(payload) => {
handleProductChange(payload)
}
)
.subscribe()
return () => supabase.removeChannel(channel)
}, [])
return products
}
Layer Communication
Data Flow Between Layers
// Example of cross-layer communication
// Client Layer -> Application Layer -> Data Layer
// 1. Client initiates request
const handleSubmit = async (formData: FormData) => {
// Client layer validation
const isValid = validateForm(formData)
if (!isValid) return
// Call server action (Application layer)
const result = await createProduct(formData)
if (result.success) {
// Update client state
setProducts(prev => [...prev, result.data])
}
}
// 2. Server action processes request
export async function createProduct(formData: FormData) {
'use server'
// Application layer validation and processing
const productData = await validateProductData(formData)
// Data layer interaction
const product = await supabase
.from('products')
.insert(productData)
.select()
.single()
return { success: true, data: product }
}
Error Propagation
// Error handling across layers
try {
// Data layer error
const { data, error } = await supabase.from('products').select()
if (error) throw new DatabaseError(error.message)
// Application layer processing
const processedData = await processProducts(data)
return { success: true, data: processedData }
} catch (error) {
// Application layer error handling
if (error instanceof DatabaseError) {
return { success: false, error: 'Database connection failed' }
}
return { success: false, error: 'Unknown error occurred' }
}
Best Practices
Layer Separation
- Keep business logic in the application layer
- Avoid direct database calls from client components
- Use proper abstractions between layers
- Maintain clear interface contracts
Performance Optimization
- Leverage server components for initial data loading
- Use client components only when interactivity is needed
- Implement proper caching strategies at each layer
- Optimize database queries with proper indexing
Security Considerations
- Validate data at every layer boundary
- Implement proper authentication checks
- Use Row Level Security for database access
- Sanitize all user inputs
Maintainability
- Use TypeScript for type safety across layers
- Implement consistent error handling patterns
- Document layer responsibilities clearly
- Regular code reviews and refactoring