Skip to main content

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)​

  1. Route defines loader and renders component
  2. Component uses hooks for data and interactions
  3. Hook calls queries for server state management
  4. Query invokes server functions via tRPC
  5. Server Function applies validation and calls data access
  6. 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​

  1. Never skip layers: Each layer should only depend on the layer immediately below it
  2. No circular dependencies: Lower layers should never import from higher layers
  3. Single responsibility: Each layer should have one clear purpose
  4. 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