Structure
Learn the repository layout, the conventions the harness enforces, and where a new module, block or integration belongs.
The repository has twenty top-level directories and no monorepo packages. Everything ships from one project, so a storefront page imports a domain rule directly rather than across a package boundary.
This page names what each directory holds, the conventions the harness enforces across them, and where a new piece of code belongs.
app/
(web)/ storefront routes — pages only, no components
admin/ admin routes — pages only, no components
ai/ v1/ route handlers + services/ for the admin AI generators
api/ public API, webhooks, MCP, oRPC, Inngest, health
components/
common/ shared primitives (button, dialog, calendar, …)
web/ storefront components, including blocks/renderers/
admin/ admin components, one folder per section
data-table/ the TanStack Table wrapper used by every admin list
shared/ components used by both surfaces
server/
admin/<module>/ actions.ts · queries.ts · crud.ts · schema.ts · payloads.ts
web/ storefront read paths and the page resolver
ai/ the gated agent tool kit and registry
customers/ find-or-reconcile for the signed-in customer
messaging/ conversations and alert delivery
shared/ settings read model, facets, scarcity, policy windows
router.ts the oRPC root router
lib/ domain logic — cache.ts, blocks/, permissions/, crud/, orders/, …
services/ infrastructure — db.ts, stripe.ts, redis.ts, s3.ts, resend.ts, integrations/
functions/ Inngest functions: crons and domain-event handlers
contracts/ shared primitive and operation schemas (id, email, pagination, sorting)
config/ site, theme, pricing, fonts and activity-registry configuration
emails/ React Email templates
events/ types.ts (the domain event union) and emitter.ts
hooks/ client hooks
utils/ pure helpers — money, decimal, try-catch, search-params
types/ ambient and shared type declarations
prisma/
schema.prisma the schema — the only source of truth for it
migrations/ the history
seed.ts the seed, run by bun run db:seed
scripts/
check-*.ts the harness: 25 discipline checks and ratchets
test/ integration setup and factories
docs/ architecture and migration notes
assets/ icon sources, compiled by bun run icons
public/ static files served as-is
patches/ bun patch files (currently zsa@0.6.0)Key conventions
The layout above is not a suggestion. Most of it is checked by a script, so a file in the wrong place fails the build rather than becoming a precedent.
Route folders contain routes only
app/ holds Next.js conventions and nothing else:
page.tsx
layout.tsx
loading.tsx
error.tsx
route handlersRoute-specific UI lives in components/admin/<section>/ or components/web/<section>/, and there are no _components/ folders anywhere in app/.
scripts/check-discipline.ts rejects inline list rendering in a route page.tsx for the same reason: a route file that renders a list is a route file that has started to hold components.
Every mutation is a permission-wrapped server action
Actions live in server/<area>/<module>/actions.ts and declare their permission through modulePermission/withAnyPermission from lib/permissions/server.
Persistence goes through the module's CRUD class, which extends BaseCRUD and handles soft delete, activity logging and cache invalidation as one unit — not as three things a caller has to remember separately.
Reads are separate from writes
Queries live beside actions in queries.ts, wrapped in unstable_cache and tagged from CACHE_TAGS.
The tag is what connects the two halves: the write invalidates the same tag the read was cached under.
One module, one folder
A module's actions, queries, CRUD, zod schemas and Prisma payloads sit together, so everything about — say — coupons is in server/admin/coupons/.
Boundaries are enforced automatically
Three scripts fail the build on imports that cross the wrong way:
scripts/check-client-server-boundary.ts
scripts/check-theme-boundaries.ts
scripts/check-client-bundle-server-only.tsSee The harness.
Where to put a new thing
A storefront section is a block kind: a registry entry in lib/blocks/registry.ts plus a renderer in components/web/blocks/renderers/.
An admin entity is a pair of folders, server/admin/<module>/ and components/admin/<module>/ — see Modules.
A reaction to an event is a handler in functions/ — see Events.
A third-party integration goes in services/integrations/, behind the existing interface.
A shared rule or format goes in lib/ if it is a domain rule and utils/ if it is a pure helper — one canonical home, imported everywhere.
The question to ask when adding a file is not "where would this fit?" It is:
Which folder already owns this responsibility, and what is the nearest existing file doing the same job?
Next Steps
Stack
Learn which versions of Next.js, React, Prisma, Tailwind, Better Auth, Stripe and Inngest Litestore runs on, and the architecture decisions behind them.
Style
Learn the naming, import and file-layout conventions the codebase follows, and the two scripts — check-no-as-any.ts and check-discipline.ts — that fail on a new violation.