Architecture Overview
How Prood applications, packages, and the Commerce API fit together in the Next.js monorepo.
Prood is organized as a layered architecture where each package has a single responsibility and applications communicate through a well-defined HTTP API.
System diagram
Drag to pan · Scroll to zoom
* Dashboard uses @prood/commerce directly only for integration config encryption and domain management helpers — all catalog/order CRUD goes through the API.
Design principles
Platform commerce engine
The CommerceAdapter interface (from @prood/types) defines the contract implemented by the built-in platform adapter. The current runtime supports this Neon Postgres engine only; Medusa/Salla-style external adapters are not selectable by environment variable today.
// @prood/platform exports
const { adapter, admin } = await createPlatformAdapter()
const products = await adapter.getProducts({ page: 1, limit: 20 })Pluggable payment providers
Payment providers implement the PaymentProvider interface. The checkout engine does not know which gateway it uses:
interface PaymentProvider {
createSession(input: CreatePaymentSessionInput): Promise<PaymentSession>
confirmSession(sessionId: string): Promise<PaymentSession>
refund(input: RefundInput): Promise<PaymentSession>
verifyWebhook(event: PaymentWebhookEvent): Promise<boolean>
}Swapping Stripe for Easypay requires changing the provider instantiation — not the checkout state machine.
API-centric data access
Client applications do not import @prood/commerce for catalog or order operations. All commerce operations go through apps/api:
| Benefit | Explanation |
|---|---|
| Single auth layer | One place to resolve tenant, scopes, and caller type |
| OpenAPI contract | Humans, @prood/api-client, MCP tools, and Agent Auth share the same schema |
| Thin storefront | BFF routes only where cookies or client-side UX require them (cart, search) |
| Agent-ready | Every operationId becomes an agent capability |
See API-centric architecture for details.
Multi-tenant by default
Every merchant is a Better Auth organization. Postgres row-level security filters all commerce rows by organization_id. Any code path that forgets withTenant() fails closed — zero rows returned, writes blocked.
Domain tiers
The platform adapter organizes commerce domains by how commonly they are needed:
| Tier | Domains | Status |
|---|---|---|
| Universal | catalog, store | Always present |
| Common | cart, checkout, orders, customers, wishlist, reviews, promotions, brands, countries | Implemented in platform |
| Specialized | returns, wholesale, auctions, rentals, giftCards | Schema/types exist; partial implementation |
Layer responsibilities
| Layer | Packages / Apps | Role |
|---|---|---|
| Types | @prood/types | Shared vocabulary — interfaces only, no runtime |
| Engine | @prood/platform | Neon Postgres commerce backend + Admin API |
| Data layer | @prood/commerce | Server-only wrapper — caching, tenant scope, providers |
| Checkout | @prood/checkout, @prood/checkout-host | State machine + Redis session host |
| Payments | @prood/payment-* | Gateway-specific provider implementations |
| Storage | @prood/storage-* | Tenant-namespaced file uploads |
| UI | @prood/ui | Shared React components |
| Client | @prood/api-client | Typed HTTP client from OpenAPI |
| API | apps/api | REST, MCP, Agent Auth, webhook ingress |
| Apps | storefront, dashboard, checkout | End-user surfaces |
Checkout separation
Payment UI is intentionally separated from the storefront:
- Storefront collects customer info and places the order via the API
- Checkout app handles payment provider interaction (Stripe Element, Multibanco references)
- Redis persists checkout sessions between redirect hops
- Webhooks reconcile async payment results back to order status
The checkout app runs as a separate Next.js surface with session state persisted in Upstash Redis, keeping payment provider credentials and UX isolated from the storefront. See Checkout flow.
Caching strategy
Catalog reads in @prood/commerce use Next.js Cache Components with per-tenant cache tags:
| Tag pattern | TTL (SWR) | Data |
|---|---|---|
products-{orgId} | 600s | Product listings |
categories-{orgId} | 600s | Category tree |
store-{orgId} | 3600s | Store info |
Cart, checkout, and account routes stay dynamic — they depend on cookies and session state.