ARCHITECTURE
Boring technology, arranged carefully.
Nothing here is exotic. TypeScript, Fastify, PostgreSQL, Prisma, Redis. The value isn't in the choices — it's in the discipline about where code is allowed to live and what's allowed to be slow.
The stack
Every one of these is checkable, which is the point of naming them.
Language
TypeScript
Whole categories of mistake are caught while writing the code, not while a customer is using it.
Web framework
Fastify
One of the fastest available, without needing special tricks to get there.
Database
PostgreSQL
Mature and relational, with serious transaction guarantees, real data integrity, and advanced types when you need them.
Data access
Prisma
Type safety between the code and the database, plus migrations that are tracked and reversible.
Queue & background work
BullMQ on Redis
Anything that must not slow down a response: email, image processing, reports, scheduled jobs.
File storage
S3-compatible
Media scales without bounds and without touching the application server.
API documentation
OpenAPI / Swagger
Generated from the same definitions the code uses, so the docs can't drift from reality.
Every request takes the same path.
Layered by rule rather than by convention — which is why what a layer may NOT do is written down beside what it does.
Route
Input validation and authentication. No business logic.
Controller
Translates an HTTP request into an internal call and shapes the response. No business logic.
Service
All business logic lives here. Rules, calculations, decisions. Nowhere else.
Repository
The only layer permitted to talk to the database.
Database
Reached through the repository and nowhere else.
The separation means each layer is testable on its own, a change in one doesn't leak into the others, and new code always has an obvious home. That last point is what keeps the cost of maintenance from compounding in year two.
Two processes
API server
Answers user requests. Stays fast because it refuses to do heavy work — anything expensive is handed off.
Background worker
Does everything that might take seconds or minutes: sending email, processing media, generating reports, running translations, regenerating image derivatives, firing scheduled reminders.
They share the same domain logic, so a rule is never implemented twice. The split means one operator exporting thousands of orders never slows down a customer mid-checkout.
Every endpoint answers the same way.
Success or failure, the response has exactly one shape.
Every client that connects — web, mobile, a future GraphQL layer — speaks one consistent language for handling errors, and never gets surprised. The envelope has three parts:
A stable machine-readable code
It identifies the failure and does not change when the wording does, so client code can branch on it safely.
A human-readable message
In the right language, ready to show a person.
Structured detail
Which field, and what was wrong with it — not a sentence a client has to parse.
The literal field names and an example of each response live in the generated API reference, where they are produced from the same definitions the code uses and cannot drift from it.
Built to grow without a rewrite.
Stateless servers
No session data is kept on the server, so adding more servers under load is straightforward and needs no code change.
Heavy work is always background work
Anything that could take seconds leaves the response path entirely.
Measured latency budgets
Every endpoint has a millisecond target, tested with a real load tool rather than estimated.
The right order of optimisation
Database indexes first, then inefficient queries, then payload size. Caching is the last resort, always with an explicit key, lifetime, and invalidation strategy — never used to hide a real problem.
Media uploads bypass the server
Files go straight to cloud storage. Application resources are never spent shifting bytes.
Designed for years of data
Change history, financial documents, and operational logs each have their own retention policy from the start, so operational data stays controlled while commercial and legal records are never lost.
One organisation today, many tomorrow.
The data model was designed so a single-organisation installation can become a multi-organisation platform — a multi-vendor marketplace, for instance — without redesign. Organisations are a first-class concept, and data is scoped at the authorisation layer rather than by hoping every query remembers to filter.
See how a marketplace uses itWant to go deeper than a marketing page?
Book a technical walkthrough. Bring your hardest question about the data model.
- We reply within one working day
- No sales sequence, no drip campaign
- NDA before the call if you'd prefer