ignitionstack.pro v1.0 is out! Read the announcement →
Skip to Content

Application Layering

This page translates the conventions we follow inside src/app into a set of layers so new contributors can orient themselves before shipping features. It is based on the current code that lives in ../src:

src/ ├── app/ │ ├── [locale]/ # Server Components per locale │ ├── actions/ # Server Actions (mutations) │ ├── server/ # Server-side queries (reads) │ ├── lib/ # Shared domain & infra utilities │ ├── api/ # Route handlers / webhooks │ ├── components/, hooks/ # Presentation helpers │ └── types/ # Cross-layer types ├── fonts/, providers/, store/, types/ └── i18n/, middleware.ts

Layer Stack

LayerPurposeConcrete folders
PresentationRender HTML/React output and capture user intent. Server Components go under src/app/[locale]/…; client components live in src/app/components/ and src/app/hooks/.[locale], components, hooks
ApplicationOrchestrate use-cases. Mutations belong to Server Actions (src/app/actions/*), reads belong to Server Queries (src/app/server/*). Both return structured results via ActionResult.actions, server, unauthorized
DomainEncapsulate business rules and analytics events. Repository implementations, mappers, services, and validations live under src/app/lib/**.lib/repositories, lib/mappers, lib/services, lib/validations, lib/analytics-events.ts
InfrastructureSupabase clients, caching, logging, rate limiting, Stripe/Resend integration, and Supabase migrations.lib/supabase, lib/cache.ts, lib/logger.ts, lib/rate-limit.ts, supabase/migrations/*

Guiding rule: presentation never talks to Supabase directly. Every data read or mutation flows through the Application layer and eventually through a repository.

Request & Data Flow

  1. Routing & Renderingsrc/app/layout.tsx and every route under src/app/[locale]/… render via React Server Components. Client-only features (e.g., src/app/components/chat/chat-input.tsx) mark "use client" and call server actions through form action props or useServerAction hooks.
  2. Server Actions (src/app/actions/*) – Each mutation file is a "use server" module that parses inputs (Zod schemas in src/app/lib/validations), enforces auth (requireAuth from src/app/lib/auth.ts), delegates to repositories, and returns an ActionResult<T> from src/app/lib/action-result.ts. Admin-only actions wrap their handler with withAdminAuth from src/app/lib/admin-action-wrapper.ts to avoid repeating boilerplate.
  3. Server Queries (src/app/server/*) – Read-only helpers (e.g., server/projects/get-project-by-slug.ts) encapsulate Supabase fetches behind caching utilities such as createCachedQueryWithParams in src/app/lib/cache.ts. Presentation layers consume these helpers instead of hitting Supabase directly.
  4. Repositories (src/app/lib/repositories/*) – Concrete data access (e.g., ProjectRepository, ConversationRepository) inherits from base-repository.ts and maps Database['public']['Tables'] rows into domain objects through src/app/lib/mappers/*. Admin repositories (like admin-repository.ts) call createAdminClient while regular repositories rely on createClient or createServerClient to respect RLS.
  5. Supabase Clients & Utilitiessrc/app/lib/supabase/{server,client,admin}.ts centralize client creation, attach the correct session, and plug into helpers like query-timeout.ts and error-handler.ts. Never instantiate createBrowserClient or createClient outside this folder.
  6. Caching & Invalidation – All shared cache tags are defined in src/app/lib/cache.ts. Every mutation must import the relevant revalidate* helper (e.g., revalidateProjects) after persisting data so Server Components see fresh content.
  7. Observability & Analyticssrc/app/lib/logger.ts and logger-edge.ts create pino-based loggers. Analytics events are centralized in src/app/lib/analytics-events.ts so server actions log consistent Mixpanel/GA payloads. Feature-specific analytics hooks live in src/app/hooks/*.

Application Layer Anatomy

Domain & Data Access Layer

ComponentKey filesNotes
Repositorieslib/repositories/*.tsImplement CRUD using Supabase clients. ProjectRepository, MessageRepository, AIRepository, etc. share logging patterns and return typed objects instead of raw rows.
Mapperslib/mappers/*Translate snake_case database rows (DbProject, DbConversation) to camelCase domain objects consumed by the presentation layer.
Serviceslib/services/*Cross-cutting utilities (e.g., payment orchestration, email sending) that coordinate multiple repositories or providers.
Validationslib/validations/*Zod schemas for forms, server actions (sendMessageSchema in lib/validations/ai.ts, contactValidation in lib/contact-validation.ts).
Analyticslib/analytics-events.ts, lib/analytics.ts, lib/mixpanel*.tsProvide strongly typed event names so instrumentation stays centralized.

Infrastructure Concerns

Workflow Reminders

  1. Add Zod schema before touching a repository. Schemas live in lib/validations and are reused by server actions and components.
  2. Return ActionResult everywhere. It makes error handling predictable for UI callers.
  3. Wrap admin mutations. Use withAdminAuth or withAdminAuthSimple to avoid duplicating session checks.
  4. Always revalidate caches. Mutations must import the relevant revalidate* helper from lib/cache.ts.
  5. Keep architecture docs updated. If you introduce a new cross-cutting service or data store, add a section here and link to more detailed ADRs.