Skip to content

Repository files navigation

nestAcademy — College Manager

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.

Architecture

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
Loading

The enrollment saga (verified end-to-end)

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
Loading

Run it

cp .env.docker.example .env.docker
docker compose up --build        # or: npm run start:all

See 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.

Repository layout

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)

Getting started

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-present

Status

Building module by module. See .scratch/college-manager/issues/ for the ticket breakdown and dependency order.

About

simple nestjs poc w/ ms

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages