Platform Engineering

facturom: The Invoicing Platform We Build and Run

July 23, 20264 min read

Context

facturom (facturom.ro) is our own invoicing platform, currently in development. Invoicing in Romania means two hard external dependencies: ANAF, the tax authority, whose e-invoicing API every compliant platform must integrate with, and BNR, the national bank, for exchange rates. It also means handling documents, secrets, and email at a standard we would be embarrassed to get wrong.

This case study is not about the product. It is about the runtime — because we build facturom and we run it, on our own infrastructure, and the platform engineering is the part that transfers directly to customer work. The stack is over a dozen services, every one of them running in production right now.

Constraints

  • A government API you cannot hammer. ANAF's e-invoicing endpoints are not a sandbox you iterate against freely. Development and CI need to exercise the integration end-to-end without ever touching the real thing.
  • Secrets across many services. A dozen-plus containers each needing credentials is how secrets end up scattered through env files. We wanted one place where secrets live and an audit trail for who reads them.
  • A small team. Every service we add is a service we operate. The architecture had to be exactly as micro as necessary and no more.

Approach

The shape of the platform:

gateway
├── core-api   — public API
├── admin-api  — back-office API, separate service, separate surface
├── web / site / admin-web — frontends
├── email      — outbound mail worker
├── enrich     — data enrichment worker
├── anaf       — e-invoicing integration worker
└── bnr        — exchange-rate worker
supporting: secrets manager · object storage · cache · Postgres · ANAF simulator

One gateway, split APIs. Everything enters through the gateway. The public API and the admin API are separate services with separate attack surfaces — an admin-side bug cannot be reached through the public path.

One worker per integration. Email, enrichment, ANAF, and BNR each run as their own container. The point is failure isolation: when a government endpoint has a bad afternoon, the ANAF worker backs off and retries while invoicing, email, and everything else carry on. Each worker ships a container healthcheck, so a wedged integration is visible in docker ps, not discovered in a support ticket.

Secrets in a real secrets manager. Self-hosted and open source. Services fetch what they need; credentials do not live in compose files.

State where it belongs. Postgres for records, S3-compatible object storage for generated documents and uploads, a dedicated cache for caching and queues. All boring, all deliberate.

The simulator. The piece we consider the most interesting engineering: an ANAF API simulator runs as part of the stack, so the entire e-invoicing flow — submission, polling, error paths — is testable locally and in CI against a service that behaves like the government API, without the government API. Integration code that can only be tested in production is integration code that fails in production.

Every service is built in CI and pulled from a private registry as a tagged image. Deployment is docker compose up -d with pinned tags — the same discipline we describe across our estate.

What was hard

The honest list. First, the ANAF integration itself — external APIs with strict semantics and asynchronous processing are where optimistic code goes to die, and the simulator exists because we needed to reproduce failure modes on demand. Second, keeping that many services uniform: one packaging pattern, one logging approach, one healthcheck convention, resisting the entropy where each service becomes a special case. Third, knowing when to stop splitting — every boundary in the diagram exists for an operational reason, not for architecture diagrams' sake.

Where it stands

The platform runs today, healthy, on our own estate, with the product still in development on top of it. The patterns — gateway fronting split APIs, one worker per external dependency, secrets in a vault, a simulator for the integration you cannot rehearse in production — are the same ones we bring to customer platforms.

If your platform has grown past what one compose file and a prayer can hold, talk to us.

Facing something similar?

This is the kind of work we do for clients — from network engineering to platform builds and AI-driven development. Tell us about your infrastructure and we'll tell you, honestly, whether we can help.

Let's Talk