Skip to content
Last updated: Sep 25, 2026

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 ​

Domains nest under packages/finance/, packages/mgmt/, or packages/system/, each as <domain>/<domain-a|d|wf> (not a flat packages/<name>a/<name>d layout):

DomainPackagesDescription
Cashcasha, cashdCash management
Close Hubclosehuba, closehubd, closehubwfPeriod close management (eclose is the DB schema name, not a package)
Invoiceinvoicea, invoicedInvoicing
Journaljournala, journaldJournal entries
Reconciliationreconciliationa, reconciliationdReconciliation
Core Systemcoresystema, coresystemdCore financial operations
Finance Basefinbasea, finbasedShared finance domain
Finance AgentsfinanceagentsAI agents scoped to finance domains
Mgmtmgmta, mgmtdMulti-tenancy management
Systemsystema, systemd, systemwf, systemwfpUsers, auth, settings, workflows, proxied activities (SAP, etc.)
System AgentssystemagentsAI agents scoped to system domain

Deployed Microservices (12) ​

Count matches backendServices[] in fin-infra/railway/backend/infrastructure.ts.

ServicePortRole
blitz-api10001API gateway — single entry point for all clients
blitz-ws10002WebSocket server for real-time features
blitz-apimgmt9999Management/control-plane API for tenant onboarding (private-only)
blitz-agents10013AI agent execution
blitz-excelrw10014Excel read/write — .NET, not Bun
blitz-emailprocessor—Async email processing worker
blitz-wfw—Temporal workflow worker (minimum 1 GB RAM — crashes at 512 MB)
blitz-wfwpdf—Temporal PDF workflow worker
blitz-recon10015Reconciliation service
blitz-remotecontrol10016Remote control
blitz-classicml—Python ML Temporal worker
blitz-bapiproxy10017SAP/BAPI proxy — .NET, also a Temporal worker on taskqueue-system-sap

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

excelrw and bapiproxy are both .NET 10 (ASP.NET Core), not Bun — see Platform Architecture for the two-file (ELF apphost / PE IL .dll) container detail if debugging one of them.

Additional Services (built, not deployed) ​

blitz/src/apps/ has 22 directories; 12 have a CI build-push workflow and are the services deployed to Railway (above). The remainder (desktop, email, events, fs, jsontransformer, mcp, pdfwriter, queue, rulesengine, scriptrunner, templaterenderer) exist in the monorepo but are not built or deployed. 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 — local dev only; RabbitMQ was removed from Railway 2026-07-10 and no deployed service depends on it)
  • 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.

Finaisse Internal — Confidential. Access-restricted; not for external distribution.