Skip to Content
ArchitectureProject Structure

Project Structure

The codebase is organized by domains (feature modules) under src/domains/. Each domain owns its data, services, and UI for one concern. Root directories (src/actions/, src/lib/, src/app/) wire domains together.

Domain map

src/domains/ ├── cms/ # Payload CMS: data + schema layer │ ├── payload/ │ │ ├── collections/ # users, customers, media, pages, posts, categories, │ │ │ # tenants, leads, products, records, project-schemas, │ │ │ # import-mappings, observations │ │ ├── blocks/ # 15 page-builder blocks (see Features → Content) │ │ ├── access/ # authenticated, anyone, authenticatedOrPublished │ │ ├── fields/ # section-tab-fields, slug-field, … │ │ ├── hooks/ # slug formatting, revalidation, Stripe sync, … │ │ └── plugins/ # better-auth, mcp, multi-tenant, search, seo, │ │ # stripe, s3, live-preview, import-export │ ├── components/ # admin + block React components │ ├── composer/ # ComposerService + page templates │ └── lib/ # tenant helpers, CMS client utils ├── auth/ # Better Auth client, session, constants ├── monetization/ # Stripe client, paywall, fulfillment, webhooks ├── leads/ # Newsletter subscription, welcome email trigger ├── emails/ # React Email templates ├── seo/ # Meta tags, structured data, robots, sitemap ├── present/ │ └── orama/ # Per-tenant in-memory search index over records ├── import/ # Import service: bulk JSON via plugin-import-export ├── generation/ # AI generation: runner factory, pipeline executor └── monitor/ # Observations engine + /operate dashboard

Root directories

PathPurpose
src/actions/All 'use server' actions. Calls domain services, injects deps.
src/lib/Root-owned clients: getPayload(), Stripe, email, auth, procedures, Replicate.
src/app/(frontend)/Public routes. [tenantDomain]/ for multi-tenant resolution.
src/app/(payload)/Payload admin panel + API routes.
src/cli/shipmore CLI source (built with tsup).
src/components/Shared UI components (shadcn-derived primitives).
src/env.tsTyped env validation (@t3-oss/env-nextjs).
src/payload.config.tsPayload entrypoint. Imports from domains/cms/payload/ only.
content/Documentation source (MDX + _meta.js); served by Nextra at /docs.

Module boundaries

FromMay importMust NOT import
domains/*/Other domain utils (pure), src/lib/ typesgetPayload(), stripe directly
actions/Any domain service, src/lib/ clientsNothing — it’s the wiring layer
app/ routesactions/, domain services, src/lib/Cross-domain side effects

Domains expose pure services/utils only. Root wires them.

Collections (12)

CollectionPurpose
usersPayload admin users (email/password auth, RBAC, API keys for CLI)
customersCustomer-facing accounts (Better Auth, Stripe fields)
mediaFile uploads, auto-sized, S3 optional
pagesPage builder with 15 blocks, drafts, live preview
postsBlog posts (Lexical, categories, authors, drafts)
categoriesHierarchical, tenant-scoped
tenantsTenant config: domain, branding, legal, analytics
leadsNewsletter subscribers, UTM tracking, status
productsStripe-synced products (4 monetization types + paywall config)
recordsUniversal data store. One row per item. data blob validated against the tenant’s DataSchema.
project-schemasHolds the DataSchema for each tenant.
import-mappingsRaw source row → records.data projection rules.
observationsAppend-only facts emitted by monitoring tasks (data quality, evals, GSC, Stripe).

Plugins enabled

better-auth, search (posts only), multi-tenant, mcp (pages + tenants), seo, stripe, s3-storage, live-preview, import-export.