PrimeBrickPrimeBrick
  • Docs
  • Contact
  • MIT License
  • Documentation
  • MCP Server
  • API Catalog
  • Services
  • Libraries
PrimeBrickPrimeBrick

© 2026 PrimeBrick. MIT License. v3.11.2

github
Backend
Frontend
Microservices
    Developer Guide
      Microservices OverviewArchitectureConventionsAdding a Service
      Services
powered by Zudoku
Developer Guide

Architecture

Primebrick v3 microservices follow a gateway-proxied architecture with NATS™ for async messaging and per-service database isolation.

Backend proxy

The Backend (BE) acts as the API gateway for all microservices. The Frontend (FE) never calls microservices directly.

  • HTTP proxy: The BE proxies requests via /ws/:serviceCode/*. A request to GET /ws/emailsender/api/v1/entities/provider/list is forwarded to the EmailSender microservice at GET /api/v1/entities/provider/list.

    TerminalCode
    # From the FE (or any JWT-authenticated client) through the BE proxy: curl -H "Authorization: Bearer <jwt>" \ http://localhost:3001/ws/emailsender/api/v1/entities/provider/list # → { "rows": [...], "total": 0, "page": 1, "page_size": 20 } # Direct to the microservice (bypassing the BE, for debugging): curl http://localhost:3003/api/v1/entities/provider/list
  • OpenAPI aggregation: The BE fetches each microservice's GET /api/v1/openapi.json and merges the specs into a unified API catalog. This powers the Zudoku API explorer on the docs site.

  • MCP Server: The BE's MCP Server uses the aggregated OpenAPI specs to generate generic CRUD tools. The standardized entity CRUD path pattern (/api/v1/entities/:entity/...) allows the MCP Server to dispatch tools without per-entity path configuration.

NATS™ message bus

Microservices connect to NATS™ via the SDK's NatsClient. NATS™ is used for:

  1. Service registration — microservices publish register/heartbeat/ unregister events; the BE subscribes and persists to public.service_registry
  2. Async request/reply — the BE publishes requests (e.g. emailsender.send) and microservices reply on a per-request response subject (e.g. emailsender.response.{requestId})

NATS™ authentication uses the SDK's GATEWAY-RESOLVED mode — the BE forwards JWT/auth headers via NATS™ message headers, and the microservice verifies them with verifyNatsMessage().

SDK lifecycle

Every microservice uses @primebrick/sdk for its lifecycle:

  1. Environment validation — requireEnv() validates the bootstrap env vars at startup (DATABASE_URL, DATABASE_SCHEMA — the only env-sourced settings, validated closed-set by assertDbEnv(); all runtime config lives in the service's config_entries table)
  2. Config loading — ConfigLoader reads the config_entries table for service-specific settings (NATS™ URL, HTTP port, service code)
  3. Auth config — initAuthConfig() + loadAuthConfig() set up GATEWAY-RESOLVED auth mode
  4. NATS™ connection — NatsClient.getConnection(url)
  5. Service registration — ServiceRegistrar.register() publishes the register event, then startHeartbeat() begins periodic health checks
  6. HTTP server — createHttpServer() starts the HTTP listener with health check and route handler. The routeHandler MUST be factory-produced (makeRpcRouter / makeOpenApiHandler / composeRouteHandlers) — createMicroservice rejects arbitrary handlers at startup, so the auth → RBAC → validation → RFC 7807 chain cannot be bypassed
  7. Graceful shutdown — GracefulShutdown coordinator runs cleanup on SIGTERM/SIGINT: stop heartbeat → unregister → close NATS™ → close DB → close HTTP

Database isolation

Each microservice has its own PostgreSQL® schema (e.g. emailsender). Microservices never read or write to another microservice's schema. The only shared table is public.service_registry, which lives in the public schema and is used for service registration.

  • DAL: @primebrick/dal-pg with entity decorators (@Entity, @Column, @Key, @Unique, @AuditableField, @DeletableField)
  • Migrations: each microservice has its own db-meta/ directory (patches/ + signed MANIFEST.json), applied by the dal-pg runMigrations engine at service boot (dbPatches option)
  • Schema override: the @Entity decorator accepts an optional second argument for the schema name (e.g. @Entity("service_registry", "public"))

Single DAL API: services use getDal() only — the pg pool is internal to the Dal object and must never be touched by consumers.

Audit vs events: every entity write produces (a) the persistent {table}_audit row via dal-pg AuditPort and (b) an entity-changed event signal for whoever subscribes — two separate mechanisms; the BE's collaboration/SSE layer is just one consumer of the event.

Translations are per-module: each microservice owns <schema>.translations (e.g. emailsender.translations) for its module keys; app.* keys are global and live in public.translations (BE-owned), system.* in system.translations. The BE's translations gateway manages CRUD across module tables.

Next steps

  • Conventions — API path conventions, data model rules
  • EmailSender — Email sending microservice
Last modified on October 28, 2026
Microservices OverviewConventions
On this page
  • Backend proxy
  • NATS™ message bus
  • SDK lifecycle
  • Database isolation
  • Next steps