Skip to main content

0001: Monorepo and service split

Context​

Payofy must provide payment capabilities for multiple internal products (Budofy, Voiceyfy) and external partners. We need shared typing, centralized contracts, reliable double-entry accounting, and rapid local iteration.

Decision​

We chose a single monorepo (payofy) managed with pnpm workspaces and Turborepo.

Each distinct deployable project has its own dedicated directory under apps/:

  1. payofy-api (apps/api): Handles synchronous REST requests and webhook intake.
  2. payofy-worker (apps/worker): Runs asynchronous background processing through BullMQ.
  3. payofy-admin (apps/admin): Ops console for internal operations staff.
  4. payofy-portal (apps/portal): Partner portal and hosted merchant dashboard.
  5. payofy-docs (apps/docs): Public developer documentation.

Shared domain logic lives in @payofy/core under packages/core/. Shared packages publish under the @payofy/* namespace for SDK consumption, while internal libraries (contracts, core, db, kurv, testing) remain private within the workspace.

Consequences​

  • Modules share domain models directly through TypeScript service boundaries.
  • API and worker services run independently with dedicated package configurations.
  • Changes to API contracts automatically cascade to TypeScript types across consumer SDKs.