Architecture Guide
This document explains the layered architecture pattern used in this TanStack Start application.
Architecture Overview
The application follows a strict layered architecture with the following flow:
Routes → Components → Hooks → Queries → Server Functions → Data Access
Each layer builds upon the previous one, creating clear separation of concerns.
Layer Definitions
1. Routes Layer (src/routes/)
Purpose: Handles URL routing, route parameters, and page-level data loading.
Responsibilities:
- Define route structure and parameters
- Handle route loaders for data prefetching
- Compose page components
- Manage route-level state and navigation
Example:
// src/routes/index.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: HomePage,
});
function HomePage() {
return <div>Welcome to AllMyChats</div>;
}
2. Components Layer (src/components/)
Purpose: Reusable UI components that handle presentation and user interactions.
Responsibilities:
- Render UI elements
- Handle user interactions
- Manage local component state
- Delegate data operations to hooks
3. Hooks Layer (src/hooks/)
Purpose: Custom React hooks that manage component state and provide reusable logic.
Responsibilities:
- Wrap TanStack Query operations
- Handle complex state logic
- Provide reusable stateful behavior
- Bridge components to server functions
4. Queries Layer (src/queries/)
Purpose: TanStack Query definitions that define how data is fetched and cached.
Responsibilities:
- Define query keys for caching
- Configure query options
- Connect to server functions
- Handle query invalidation patterns
5. Server Functions Layer (src/server/functions/)
Purpose: Server functions that handle HTTP endpoints, validation, and authentication.
Responsibilities:
- Define API endpoints (via tRPC)
- Handle input validation with Zod
- Apply middleware (authentication, authorization)
- Coordinate business logic
6. Data Access Layer (src/data-access/)
Purpose: Direct database operations using Drizzle ORM.
Responsibilities:
- Execute database queries
- Handle database transactions
- Perform CRUD operations
- Apply basic data transformations
Dependency Flow
Data Flow (Top to Bottom)
- Route defines loader and renders component
- Component uses hooks for data and interactions
- Hook calls queries for server state management
- Query invokes server functions via tRPC
- Server Function applies validation and calls data access
- Data Access executes database operations
Best Practices
When to Use Each Layer
- Routes: Only for routing logic and page composition
- Components: For all UI rendering and local interactions
- Hooks: For reusable state logic and server state management
- Queries: For all TanStack Query configurations
- Server Functions: For API endpoints, validation, and middleware
- Data Access: For all direct database operations
Layer Communication Rules
- Never skip layers: Each layer should only depend on the layer immediately below it
- No circular dependencies: Lower layers should never import from higher layers
- Single responsibility: Each layer should have one clear purpose
- Error handling: Each layer should handle errors appropriate to its level
Folder Structure
src/
├── routes/ # File-based routing
├── components/ # Reusable UI components
│ └── ui/ # Base UI components (shadcn)
├── hooks/ # Custom React hooks
├── queries/ # TanStack Query definitions
├── server/ # Server-side code
│ ├── actions/ # Server actions
│ └── routers/ # tRPC routers
├── fn/ # Middleware and utilities
├── data-access/ # Database operations
├── lib/ # Utility libraries
└── db/ # Database schema and migrations
Related Documentation
- Overview - Developer documentation overview
- Design Decisions - Technical design decisions
- LLM-Agnostic Standards - LLM provider patterns