Backend Services
The backend is a microservices monorepo in the blitz repo, built on Bun + Elysia.
Tech Stack
| Layer | Technology |
|---|---|
| Runtime | Bun (not Node.js) |
| Framework | Elysia (not Express/Fastify) |
| ORM | Prisma with PostgreSQL |
| Language | TypeScript |
| Testing | Bun built-in test runner |
| Dependency Injection | InversifyJS |
Repository Structure
blitz/src/
├── apps/ # Deployable microservices
├── packages/ # Domain business logic (dual-package pattern)
├── libs/ # Shared libraries (@blitz/lib-*)
└── playground/ # Experimental projectsDual-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:
// 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):
| Domain | Packages | Description |
|---|---|---|
| Cash | casha, cashd | Cash management |
| Close Hub | closehuba, closehubd, closehubwf | Period close management (eclose is the DB schema name, not a package) |
| Invoice | invoicea, invoiced | Invoicing |
| Journal | journala, journald | Journal entries |
| Reconciliation | reconciliationa, reconciliationd | Reconciliation |
| Core System | coresystema, coresystemd | Core financial operations |
| Finance Base | finbasea, finbased | Shared finance domain |
| Finance Agents | financeagents | AI agents scoped to finance domains |
| Mgmt | mgmta, mgmtd | Multi-tenancy management |
| System | systema, systemd, systemwf, systemwfp | Users, auth, settings, workflows, proxied activities (SAP, etc.) |
| System Agents | systemagents | AI agents scoped to system domain |
Deployed Microservices (12)
Count matches backendServices[] in fin-infra/railway/backend/infrastructure.ts.
| Service | Port | Role |
|---|---|---|
blitz-api | 10001 | API gateway — single entry point for all clients |
blitz-ws | 10002 | WebSocket server for real-time features |
blitz-apimgmt | 9999 | Management/control-plane API for tenant onboarding (private-only) |
blitz-agents | 10013 | AI agent execution |
blitz-excelrw | 10014 | Excel 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-recon | 10015 | Reconciliation service |
blitz-remotecontrol | 10016 | Remote control |
blitz-classicml | — | Python ML Temporal worker |
blitz-bapiproxy | 10017 | SAP/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:
const endpoint = new Elysia({ prefix: "/api" })
.use(tenantInfoResolverPlugin)
.use(systemControllers)
.use(authenticator)
.guard({}, (app) => app.use(cashControllers).use(journalControllers) /* ... */);Controller → Service → Prisma Flow
// 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:
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:
loggerrootContainerbucketManagementService(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.