LOCAL_DEV_SETUP — billing-service
1. Prerequisites
- Node.js 22 LTS
- pnpm 9+
- Docker Desktop (or
colima) - Google Cloud SDK (
gcloud) for Pub/Sub emulator andgcsfake - A Postgres 16 image is pulled by Testcontainers; no host install needed
2. Bootstrap
git clone git@github.com:ghasi/melmastoon.git
cd melmastoon/services/billing-service
pnpm install
cp .env.example .env.local
pnpm db:up # docker compose up postgres + pubsub-emulator + gcs-fake
pnpm db:migrate:dev # central schema + 3 demo tenants
pnpm dev # billing-api on :8080
Two-tab dev (recommended):
- tab 1:
pnpm dev - tab 2:
pnpm dev:drainer(the outbox drainer)
3. .env.local (defaults)
NODE_ENV=development
PORT=8080
LOG_LEVEL=debug
DB_HOST=localhost
DB_PORT=5432
DB_USER=billing
DB_PASSWORD=billing
DB_DATABASE=melmastoon
DB_DEFAULT_SCHEMA=billing_central
PUBSUB_EMULATOR_HOST=localhost:8085
PUBSUB_PROJECT=local-melmastoon
IAM_JWKS_URL=http://localhost:8081/.well-known/jwks.json
IAM_STEPUP_AUDIENCE=billing-service
IAM_DEV_BYPASS=true # accepts a dev token; disabled in CI/staging/prod
AI_ORCHESTRATOR_URL=http://localhost:8090
AI_DEV_FAKE=true # returns deterministic fake responses
FILE_STORAGE_ENDPOINT=http://localhost:4443 # fake-gcs-server
FILE_STORAGE_BUCKET_PATTERN=billing-invoices-{tenantId}
PAYMENT_GATEWAY_URL=http://localhost:8082
PAYMENT_GATEWAY_DEV_FAKE=true # in-memory fake; emits processor-ish events
NOTIFICATION_SERVICE_URL=http://localhost:8083
NOTIFICATION_DEV_FAKE=true
4. Demo tenants
pnpm db:migrate:dev provisions:
| Tenant ID | Currency | Locale | Cash drawer | Sharia |
|---|---|---|---|---|
t_dev_kabul | AFN | ps-AF | yes | no |
t_dev_jeddah | SAR | ar-SA | yes | yes |
t_dev_dushanbe | TJS | tg-TJ | no | no |
Each tenant has 2 properties, 5 rooms each, and one in-house reservation already checked-in to exercise the folio path immediately.
5. Make targets
pnpm dev # API
pnpm dev:drainer # outbox drainer
pnpm test # unit + application
pnpm test:integration # spins Testcontainers, full stack
pnpm test:contract # OpenAPI + event schema validation
pnpm test:e2e # Playwright (against local stack)
pnpm lint # eslint + prettier check
pnpm typecheck # tsc --noEmit
pnpm check # lint + typecheck + unit + application
pnpm db:migrate:dev # apply central + tenant migrations
pnpm db:migrate:tenant <tid> # apply per-tenant migrations for one tenant
pnpm seed:fixtures # load fixtures (rate plans, tax rules)
pnpm openapi:emit # regenerate openapi.yaml from controllers
pnpm fixtures:tax-matrix # regenerate tax fixture from tenant overrides
6. Running against the desktop locally
The Electron app (apps/backoffice-desktop) points to http://localhost:8080 when BACKOFFICE_API_BASE is unset. Bring it up with:
cd ../../apps/backoffice-desktop
pnpm dev
A dev token button on the login screen issues a tenant-scoped token signed by the local iam-fake.
7. Working with Pub/Sub locally
Topics are auto-created on first publish. To inspect:
gcloud pubsub topics list --project local-melmastoon
gcloud pubsub subscriptions pull billing.folio.local-debug --auto-ack --limit 10
A pre-wired local-debug subscription on every topic logs to stdout for easy event watching.
8. Hot debugging
- VS Code launch config
Attach to billing-apiattaches at port 9229. - Domain breakpoints are stable across runs because the domain layer has no dynamic DI.
pnpm test:domain --watchis the tightest loop; runs in ~80 ms.
9. Resetting state
pnpm db:nuke # drops all schemas
pnpm db:migrate:dev
pnpm seed:fixtures
10. Cross-references
- Architecture and dependencies: SERVICE_OVERVIEW §11
- Migrator and tenant provisioning: MIGRATION_PLAN
- Test layers: TESTING_STRATEGY