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/:
payofy-api(apps/api): Handles synchronous REST requests and webhook intake.payofy-worker(apps/worker): Runs asynchronous background processing through BullMQ.payofy-admin(apps/admin): Ops console for internal operations staff.payofy-portal(apps/portal): Partner portal and hosted merchant dashboard.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.