Skip to content

Backend Services

The backend is a microservices monorepo in the blitz repo, built on Bun + Elysia.

Tech Stack

LayerTechnology
RuntimeBun (not Node.js)
FrameworkElysia (not Express/Fastify)
ORMPrisma with PostgreSQL
LanguageTypeScript
TestingBun built-in test runner
Dependency InjectionInversifyJS

Repository Structure

blitz/src/
├── apps/          # Deployable microservices
├── packages/      # Domain business logic (dual-package pattern)
├── libs/          # Shared libraries (@blitz/lib-*)
└── playground/    # Experimental projects

Dual-Package Pattern

Each domain is split into two packages:

  • *a — API layer: controllers, services, schemas, event handlers
  • *d — Data layer: Prisma schema, migrations, generated client
  • *wf (some domains) — Temporal workflow definitions

Example: casha + cashd = complete cash domain.

Never import generated Prisma clients directly — use factory functions:

typescript
// Correct
import { createDefaultCashClient } from "@blitz/cashd";
// Wrong
import { PrismaClient } from "@blitz/cashd/generated";

Domain Packages

DomainPackagesDescription
Cashcasha, cashdCash management
Close Hubclosehuba, closehub-eclosedPeriod close management
Invoiceinvoicea, invoicedInvoicing
Journaljournala, journaldJournal entries
Reconciliationreconciliationa, reconciliationdReconciliation
Core Systemcoresystema, coresystemdCore financial operations
Finance Basefinbasea, finbasedShared finance domain
Mgmtmgmta, mgmtdMulti-tenancy management
Systemsystema, systemdUsers, auth, settings, agents, workflows

Deployed Microservices (6 active)

ServicePortRole
api10001API gateway — single entry point for all clients
ws10002WebSocket server for real-time features
agents10013AI agent execution
excelrw10014Excel read/write
emailprocessorAsync email processing worker
wfwTemporal workflow worker (minimum 1 GB RAM — crashes at 512 MB)

wfw runs system, closehub, cash, coresystem, and invoice Temporal workers in parallel.

Additional Services (built, not yet deployed)

23 services exist in the monorepo; 17 are built and in GHCR but not actively running in staging or production. See Gaps & Roadmap.

API Gateway Pattern

The api service (port 10001) aggregates all domain controllers:

typescript
const endpoint = new Elysia({ prefix: "/api" })
  .use(tenantInfoResolverPlugin)
  .use(systemControllers)
  .use(authenticator)
  .guard({}, (app) => app.use(cashControllers).use(journalControllers) /* ... */);

Controller → Service → Prisma Flow

typescript
// Controller
const endpoint = new Elysia().group("/payments", (app) =>
  app.post("/filter", async ({ logger, cashPrisma, body }) =>
    await PaymentService.filterInstances(logger, cashPrisma, body), {
      body: queryFilterSchema
    })
);

// Service — static methods, Prisma passed as parameter
export class PaymentService {
  public static async filterInstances(logger, prisma, filter) {
    const { orderBy, where } = QueryBuilder.createQuery(logger, filter, ...);
    return await prisma.payment.findMany({ ...orderBy, ...where });
  }
}

Database

PostgreSQL with multi-schema design. Two databases:

  • finance DB: cash, closehub/eclose, journal, invoice, finbase, reconciliation, sys
  • mgmt DB: tm (tenant management)

Each *d package has its own Prisma schema targeting specific schemas:

prisma
generator client {
  provider   = "prisma-client"
  output     = "../generated"
  runtime    = "bun"        // required
  engineType = "client"
}
datasource db {
  provider = "postgresql"
  schemas  = ["cash", "finbase", "sys"]
}

Multi-Tenancy

Via @blitz/lib-tenancy — header-based (x-blitz-tenant-id) or domain-based. Toggle: TENANCY_ENABLED=true|false.

Available Elysia Context Services

Controllers receive these from DI:

  • logger
  • rootContainer
  • bucketManagementService (RustFS)
  • queueService (RabbitMQ)
  • eventEmitterService
  • {domain}Prisma (e.g. cashPrisma, journalPrisma)

Workflows (Temporal)

The wfw service runs Temporal workers. Workflow definitions live in *wf packages. Temporal UI is available on the Railway internal network.