Architecture
Our architecture prioritizes reusability, modularity, and a world-class developer experience. It’s designed for a small, async, part‑time team to add value safely and predictably. Every feature is composed from small, typed building blocks and mirrors a clear URL-to-directory structure.
Core Principles
Our design decisions are guided by a few key principles:
- Component-Driven Modularity – Inspired by the philosophy of shadcn/ui, we provide a codebase of reusable, customizable components at their most minimal, essential state. Every piece of functionality is an "atomic" building block designed for composition.
- Superior Developer Experience – We believe that an intuitive and predictable structure is key to productivity. Our architecture is designed to be easily understood, navigated, and extended.
- Feature-Based & Composable – The system is built as a collection of features, enabling a micro-services and micro-frontends approach. Independent components and services can be developed, deployed, and composed to build complex applications.
- Serverless-First – Deploy on Vercel; Neon (Postgres) for serverless DB.
- Type-Safety by Default – Prisma + Zod + TypeScript across the stack.
- Async-First – Small PRs, documented decisions, steady progress.
The Composition Hierarchy: From Radix UI to Masterpieces
Our methodology is built on a foundation of proven, composable building blocks that scale from primitive components to complete applications. We leverage the power of the shadcn ecosystem to create a systematic approach to building features.
Foundation Layer: Radix UI → shadcn/ui → shadcn Ecosystem
- Radix UI – The primitive foundation providing unstyled, accessible components
- shadcn/ui – Built on top of Radix UI, providing styled, customizable components
- shadcn Ecosystem – Community-driven components and libraries inspired by shadcn/ui patterns
Composition Hierarchy: From UI to Masterpieces
- UI – shadcn/ui components serve as our base UI layer, providing consistent styling and behavior
- Atoms – Two or more shadcn/ui components (or components from the shadcn ecosystem) combined to create atomic building blocks
- Templates – Reusable layouts and component compositions that solve common UI patterns
- Blocks – Interactive features combining Templates with client-side logic (
hooks.ts
,validation.ts
) - Micro – Encapsulated features with backend logic (
actions.ts
), Prisma access, validation, and UI - Apps – Complete applications composed of several Micro features
This layered approach ensures that every component is built on solid foundations while maintaining the flexibility to create complex, feature-rich applications. Each level adds value and functionality while remaining composable and reusable.
Project Structure: A Feature-Based, Mirror-Pattern Approach
Our project structure follows a predictable, mirror-pattern architecture that enhances code discoverability and creates an intuitive mental model for developers.
Philosophy: URL-to-Directory Mapping
Every URL route has a corresponding, mirrored directory structure for its component logic. If you can see a URL, you know exactly where to find its code.
URL: /abc
mirrors to:
src/app/abc/ (Next.js App Router files)
src/components/abc/ (All related component logic)
Directory Structure
Top-Level Structure
project-root/
src/
— Source code directoryapp/
— Next.js App Router (Routing & Layouts)abc/
— URL route: /abccomponents/
— Component Logic (Mirrors app structure)abc/
— Mirrors app/abc/atom/
— Atomic UI componentstemplate/
— Reusable layout templatesui/
— Base UI components (shadcn/ui)lib/
— Shared utilities & functionsstyles/
— Global styles and CSSDeeper Layer Structure
src/
app/
page.tsx
— Root page componentlayout.tsx
— Root layout componentabc/
— URL route: /abcpage.tsx
— ABC main page componentlayout.tsx
— ABC route-specific layoutloading.tsx
— ABC loading state componentcomponents/
abc/
— Mirrors app/abc/content.tsx
— ABC UI content componentsaction.ts
— Server actions & API callsconstant.ts
— Arrays, enums, static datavalidation.ts
— Zod schemas & validation logictype.ts
— TypeScript interfaces & typesform.tsx
— Form componentscard.tsx
— Card componentsall.tsx
— All listfeatured.tsx
— Featured listdetail.tsx
— Detail contentutil.ts
— Utility functionscolumn.tsx
— Table columnsuse-abc.ts
— Custom React hooksREADME.md
— README fileISSUE.md
— ISSUE fileStandardized File Patterns
To ensure consistency, each feature directory follows standardized naming conventions. A CLI tool helps scaffold these files automatically, enforcing the pattern across the codebase.
File | Purpose |
---|---|
content.tsx | Compose feature/page UI: headings, sections, layout orchestration. |
action.ts | Server actions & API calls: validate, scope tenant, mutate. |
constant.ts | Enums, option lists, labels, defaults for the feature. |
validation.ts | Zod schemas & refinements; parse and infer types. |
type.ts | Domain and UI types; generic helpers for forms/tables. |
form.tsx | Typed forms (RHF) with resolvers and submit handling. |
card.tsx | Card components for KPIs, summaries, quick actions. |
all.tsx | List view with table, filters, pagination. |
featured.tsx | Curated feature list showcasing selections. |
detail.tsx | Detail view with sections, relations, actions. |
util.ts | Pure utilities and mappers used in the feature. |
column.tsx | Typed Table column builders and cell renderers. |
use-abc.ts | Feature hooks: fetching, mutations, derived state. |
README.md | Feature README: purpose, APIs, decisions. |
ISSUE.md | Known issues and follow-ups for the feature. |
Generics in Practice
We leverage TypeScript Generics extensively across type.ts
, use-abc.ts
, column.tsx
, validation.ts
, and forms to keep logic reusable and type-safe without duplication. See the dedicated guide for patterns and examples: Pattern docs.
System Layers & Data Flow
This structured, component-based approach integrates seamlessly with our technical system layers.
- Presentation Layer (Next.js/React) – Renders the components defined in
form.tsx
,cards.tsx
, andcontent.tsx
. - Application & API Layer (Vercel) – Executes the logic from
actions.ts
, validates data with schemas fromvalidation.ts
, and handles authentication with Auth.js. - Data & Persistence Layer (Neon Postgres/Prisma) – Interacts with the database via Prisma schemas and types, scoping queries by
schoolId
. - Observability & Ops – Logging, metrics, backups, and runbooks sized for a small team.
- Developer Tooling – ESLint, Prettier, pnpm scripts; shadcn/ui conventions.
A Typical Interaction
- A user interacts with a component from
form.tsx
on the Next.js frontend, triggering a Server Action fromactions.ts
. - The request payload is validated by a Zod schema from
validation.ts
. - The serverless function uses the type-safe Prisma client to query Neon Postgres.
- Observability captures request ID and
schoolId
for traceability. - The result is streamed back and managed by a hook from
use-abc.ts
, efficiently updating the UI.
This reorganized document follows the same styling and structural conventions used across the documentation, ensuring a cohesive reading experience.