An event-driven microservices backend for college administration, built to demonstrate the full NestJS + BullMQ stack (microservices, polyglot persistence, JWT security, third-party integration patterns, rigorous testing).
No front-end. The system is exercised via HTTP and integration tests.
Eight NestJS services + a mock payment provider, communicating through Redis. An API Gateway is the single HTTP entry point. The system is pure event-driven — no direct service-to-service HTTP (except billing → mock payment provider), polyglot (Postgres for transactional services, Mongo for notifications), and database-per-service.
Two transports live on the same Redis, chosen by message kind:
- Events (fire-and-forget, fan-out) → Redis Pub/Sub. Every subscriber process gets its own copy.
- Commands (request/reply, single handler, retried) → BullMQ queues. Dead-lettered jobs persist for replay.
flowchart TB
Client["Client (curl / Postman)"]
subgraph HTTP["HTTP entry points"]
GW["api-gateway<br/>REST + JWT + RBAC guards<br/>:3000"]
MPP["mock-payment-provider<br/>charge / refund / mode<br/>:3001"]
end
subgraph Redis["Redis (shared transport)"]
PubSub{{"Pub/Sub channels<br/>evt-* (fan-out events)"}}
Queues[["BullMQ queues<br/>cmd-* (retried commands)"]]
end
subgraph Postgres["Postgres (one schema per service)"]
direction LR
AuthDB[(auth)]
StuDB[(students)]
CrsDB[(courses)]
EnrDB[(enrollments)]
BilDB[(billing)]
GrdDB[(grades)]
end
Mongo[(Mongo<br/>notifications)]
subgraph Services["Microservices"]
Auth["auth-service<br/>RS256 JWT · argon2id"]
Stu["students-service"]
Crs["courses-service"]
Enr["enrollments-service<br/>(saga orchestrator)"]
Bil["billing-service<br/>retry · idempotency"]
Not["notifications-service<br/>templates · opt-out"]
Grd["grades-service<br/>immutable once published"]
end
Client -->|HTTP REST| GW
GW -->|commands| Queues
GW <-.->|reply| Queues
Queues --> Auth & Stu & Crs & Enr & Grd & Bil
PubSub --> Bil
PubSub --> Enr
PubSub --> Grd
PubSub --> Not
Auth --- AuthDB
Stu --- StuDB
Crs --- CrsDB
Enr --- EnrDB
Bil --- BilDB
Grd --- GrdDB
Not --- Mongo
Bil -->|HTTP charge/refund| MPP
Auth -->|publish events| PubSub
Stu -->|publish events| PubSub
Crs -->|publish events| PubSub
Enr -->|publish events| PubSub
Bil -->|publish events| PubSub
enrollments-service orchestrates the cross-service transaction. Each
transition is an event on Redis Pub/Sub; commands ride BullMQ queues with retry.
sequenceDiagram
autonumber
participant C as Client
participant GW as api-gateway
participant Enr as enrollments-svc
participant Bil as billing-svc
participant MPP as mock-payment-provider
participant Grd as grades-svc
participant Not as notifications-svc
C->>GW: POST /enrollments (JWT)
GW->>Enr: cmd EnrollStudent
Enr-->>GW: 202 + correlationId
Enr->>Enr: validate student (cmd) + capacity (cmd)
Enr->>Enr: state = PENDING_PAYMENT
Enr-)Bil: evt EnrollmentCreated
Bil->>Bil: issue invoice
Bil-)Not: evt InvoiceIssued
Bil->>MPP: HTTP /charge (idempotency key)
MPP-->>Bil: succeeded
Bil-)Enr: evt PaymentSucceeded
Enr->>Enr: state = ENROLLED
Enr-)Grd: evt EnrollmentConfirmed
Enr-)Not: evt EnrollmentConfirmed
Grd->>Grd: provision blank draft grade
Not->>Not: record "enrollment confirmed"
C->>GW: GET /enrollments/{id}
GW->>Enr: cmd GetEnrollment
Enr-->>GW: state = ENROLLED
GW-->>C: 200 + state + history
cp .env.docker.example .env.docker
docker compose up --build # or: npm run start:allSee RUNNING.md for the full curl walkthrough. The only
host-published ports are api-gateway:3000 and mock-payment-provider:3001;
internal services talk over the docker network.
See docs/superpowers/specs/2026-08-02-college-manager-design.md for the full
design, and .scratch/college-manager/ for the spec and per-ticket issues.
packages/
contracts/ # message envelope, command/event types, MessageBus port
test-utils/ # FakeMessageBus, SQLite bootstrapper, data factories
bus-bullmq/ # BullMQ-backed MessageBus (production transport)
services/
health-service/ # skeleton service proving the pattern (Ping → Pong)
npm install
# build the shared packages (contracts first — others depend on its dist/)
npm run build -w @nestacademy/contracts
npm run build -w @nestacademy/test-utils
npm run build -w @nestacademy/bus-bullmq
# run everything
npm run typecheck --workspaces --if-present
npm test --workspaces --if-presentBuilding module by module. See .scratch/college-manager/issues/ for the
ticket breakdown and dependency order.