Skip to content

Orchestrator backend

Audience: backend
Status: specced
Owns: backend
Depends on: Backend stack ADR, System architecture, Payment architecture, Coding standards, Orchestrator API, Data model, Domain — User

Orchestrator owns shared User credentials, JWT issuance, notification delivery, identity-provider adapters, payment processing (not pricing), and storage signed URLs. It does not own CAP Application, Trade/Unit, fee schedules, or centre-scoped roles beyond what JWT claims need.

Module Responsibility
Auth Register, OTP verify, password login, Google exchange, password reset, change-password, delete-account (user.deleted); issue JWT; honour intents[]
Notifications Channel ports (email, in-app, sms); inbox REST; GET/PUT preferences; consume notification.requested (skip disabled channel unless auth-critical template)
Identity gRPC VerifyIdentity (NIN first); provider-agnostic adapters
Payment gRPC InitiatePayment; provider adapters; webhook + verify REST; outbox payment.completed / payment.failednever computes price
Storage Asset metadata; signed upload / resolve / delete REST
Address Countries / states / LGAs reference data

Layout follows Coding standards (routescontrollersservicesrepositories + events/ + grpc/).

flowchart LR
Routes[Express routes] --> AuthCtrl[Auth]
Routes --> NotifyCtrl[Notify]
Routes --> PayCtrl[Payment webhook]
Routes --> StoreCtrl[Storage]
AuthCtrl --> AuthSvc[Auth service]
AuthSvc --> Users[(Postgres)]
AuthSvc --> Redis[(Redis OTP)]
AuthSvc --> Outbox[(outbox_events)]
AuthSvc --> NotifySvc[Notify service]
NotifyCtrl --> NotifySvc
NotifySvc --> EmailPort[Email port]
Grpc[gRPC server] --> IdSvc[Identity]
Grpc --> PaySvc[Payment initiate]

Full Prisma outline: Orchestrator data model. Design SoT: ecosystem-root orchestrator-schema.prisma.

Redis remains for OTP TTL, attempt counters, and short-lived rate limits.

  • Auth depends on Notifications for OTP and reset mail.
  • Prefer outbox → RabbitMQ when delivery must survive a crash; in-process EventEmitter only for same-process side effects.
  • Notifications must not depend on Auth.
  • Primary cross-service provisioning event: user.created — consumers idempotent on userId.
  • Account deletion: user.deleted after deactivate — CAP redacts PII; login/refresh fail for DEACTIVATED users.
  • gRPC only for initiation from CAP/LMS (InitiatePayment) — not a public “pay” HTTP surface for application fees.
  • Provider adapter interface (Paystack first): createCheckout, verifyWebhookSignature, parseWebhookEvent.
  • Webhook HTTP route lives only in Orchestrator.
  • On success: outbox payment.completed (optional provider); on failure: payment.failed.
  • Idempotency: unique provider reference; replayed webhooks for already-success rows are no-ops.
  • Orchestrator must not call CAP/LMS to learn price.

Minimum claims for CAP to authorize without calling Orchestrator again: sub (user id), email, expiry; optional platforms / roles as they stabilize. Publish JWKS for local verification. Exact schema is fixed in OpenAPI and shared verify middleware in @yourorg/common.

Aligned with architecture §26 (after pkg + proto):

  1. Express kit + Prisma migrate (hand-add status CHECKs) + Redis OTP + outbox poller
  2. Notifications email channel + inbox REST
  3. Auth flows + intents[]user.created
  4. Payment (gRPC InitiatePayment + Paystack + webhook + events)
  5. Identity gRPC (NIN)
  6. Storage signed URLs
  7. Address reference endpoints
  8. Deploy behind /v1/ol
  • REST: validated openapi/orchestrator.yaml (synced from ecosystem root) — Scalar
  • gRPC: @yourorg/protoContracts — gRPC
  • CAP owns POST /applications/:id/pay and POST /identity-verification

Orchestrator includes the same console framework as CAP (src/console/dist/console/cli.js) with an empty deploy list today. activate.sh runs --deploy after migrate; it no-ops until commands are registered. Pattern and VPS usage: Console commands.