Developer guide
Architecture
How the admin, storefront, shared packages, and external services fit together.
Prolifto is a multi-tenant ecommerce platform built as a Turborepo. The admin dashboard is the system of record; the storefront is a separate application that consumes the admin API.
High-level view
┌──────────────────────────────┐
│ PostgreSQL 17 │
│ organizations, catalog, │
│ orders, invoices, auth │
└──────────────▲───────────────┘
│ Drizzle
┌──────────────────────────┴─────────────────────────┐
│ Admin app │
│ Next.js 16 · Better Auth · oRPC · Inngest │
│ /api/orpc · /api/{orgSlug}/... · /api/openapi.json│
└───────▲──────────────────────────────▲─────────────┘
│ │
session / │ │ ApiKey header
oRPC │ │
┌───────────────┴──────────┐ ┌──────────┴─────────────┐
│ Admin dashboard │ │ Storefront │
│ Products · Orders · │ │ Catalog · Cart · │
│ Inventory · Settings │ │ Checkout · Account │
└──────────────────────────┘ └────────────────────────┘
│ │
└──────────┬───────────────────┘
│
┌─────────────────────┼──────────────────────┐
│ │ │
┌────▼────┐ ┌─────▼─────┐ ┌──────▼──────┐
│ Mailpit │ │ Inngest │ │ Storage │
│ SMTP │ │ jobs │ │ Cloudinary/ │
└─────────┘ └───────────┘ │ S3 │
└─────────────┘Applications
Admin (apps/admin)
- Owns the database schema usage, authentication, oRPC API, and background jobs.
- Serves the REST/OpenAPI surface under
/api/{organizationSlug}/.... - Exposes
/api/openapi.jsonand an in-app Scalar reference. - Runs Inngest functions for email, SMS, WhatsApp, PDFs, imports, search indexing, and subscription renewals.
- Uses module slices:
modules/<name>/server,modules/<name>/ui, and sharedcomponents.
Storefront (apps/storefront)
- Customer-facing catalog, cart, checkout, account, and order tracking.
- Uses
@prolifto/sdkwith an API key scoped to one organization. - Has its own Better Auth tables and supports email/password, email OTP, and Google sign-in.
- Resolves the organization from the request host for multi-tenant domains.
- Does not read catalog tables directly for storefront operations.
Docs (apps/docs)
- Fumadocs + MDX. Independent of the workspace packages.
- Builds from committed content and a committed OpenAPI snapshot.
- API pages are generated from the admin's OpenAPI spec.
Desktop (apps/desktop)
- Electron wrapper that packages the built admin app.
- Not a separate implementation; it ships the admin dashboard.
Shared packages
| Package | Role |
|---|---|
@prolifto/db | Drizzle schema, client, queries, migrations |
@prolifto/domain | Types, enums, Zod schemas, business utilities |
@prolifto/auth | Admin and storefront Better Auth configuration |
@prolifto/env | Shared env schema and validation helpers |
@prolifto/infrastructure | Email, SMS, WhatsApp, storage, AI adapters |
@prolifto/payments | Stripe and COD providers, Polar webhook verification |
@prolifto/pdf | Invoice and receipt PDF templates |
@prolifto/sdk | Typed HTTP client for the admin API |
@prolifto/search | PostgreSQL and Upstash search providers |
@prolifto/telemetry | Pino logger |
@prolifto/ui | Shared components and styles |
Multi-tenancy
An organization is a store. Every tenant-owned table carries an
organizationId, and queries must scope by it.
- Admin requests get the organization from the URL slug and the session's active organization.
- API-key requests resolve the organization from the key's
referenceIdand verify it matches the slug in the path. - Storefront requests resolve the organization from the request host (custom domain or configured root domain).
- Storefront customer accounts are scoped per organization, with scoped email identities so the same person can exist in multiple stores.
Request flow
Admin browser
→ Next.js route /api/{orgSlug}/...
→ oRPC OpenAPI handler
→ createORPCContext (session, org slug, db, search)
→ procedure (public / protected / org / apiKey)
→ permission middleware
→ service or query
→ Drizzle → PostgreSQLStorefront requests follow the same path through the SDK:
Storefront server
→ @prolifto/sdk → /api/{orgSlug}/...
→ ApiKey header
→ oRPC procedure
→ same service/query layerPayments
- Cash on delivery is the default for the Algerian market.
- Stripe handles card payments when
STRIPE_ENABLED="true". - Polar handles platform subscription billing, not storefront checkout.
Payment confirmation sets the order's paid state, locks the invoice, creates a receipt, and records a transaction. See Payments.
Background work
Inngest runs asynchronous tasks so requests stay fast:
- Order confirmation email, SMS, and WhatsApp messages.
- Low-stock and out-of-stock notifications.
- Invoice and receipt PDF generation.
- Product CSV imports and search indexing.
- Product description generation.
- Subscription renewals and domain revalidation.
See Background jobs.
Data and file storage
- PostgreSQL stores business data.
- Cloudinary or S3-compatible storage stores product media, billboards, and generated PDFs.
- Mailpit captures email locally; production uses SMTP.
- Upstash is optional; search falls back to PostgreSQL.
Design boundaries
- Shared business rules belong in
@prolifto/domain. - Database access belongs in
@prolifto/dband admin server modules. - The storefront must not import admin server code.
- Public API contracts must remain stable for the SDK and external consumers.
- Destructive operations should stay behind explicit permissions.