Skip to main content
All insights

2026-07-11By Yalçın Yörük, Founder & Principal

The reference architecture we run in production — and deliver to clients

The seven-layer architecture behind our tender-intelligence platform — channels, experience, domain services, integration, data & AI, platform, and a cross-cutting evidence layer — and the TypeScript end-to-end stack that implements it: NestJS, Next.js, Postgres with pgvector, queue-based pipelines, and a gateway-mediated LLM layer. Why we deliver client platforms by adapting this architecture rather than designing from a blank page, with the real production details — including the parts that only exist because something broke.

When we pitch AI platform delivery, the core claim is that we don't design client platforms from a blank page — we adapt a reference architecture that already runs in production. Fair question: whose production? The honest answer, and the same disclosure we make on our extraction case studies: our own. The platform behind this website — the tender-intelligence product that monitors 80+ procurement markets — is the reference architecture, running live against adversarial real-world sources every day. We're a single-principal boutique; our proof is the system we operate, not a logo wall. This post walks through that architecture as it actually exists, warts included.

Seven layers

We describe the platform in seven layers. None of the layer names are novel — that's rather the point. What matters is that every layer has pre-made, production-tested decisions attached.

Channels. Everything a user touches: the public Next.js web portal, a native SwiftUI iOS app, a REST API, and a React admin dashboard. The discipline here is that channels stay thin — the iOS app and the web portal call the same versioned /v1 API as everyone else, so a new channel (a partner API, an AI-agent interface) is an addition, not a rework.

Experience. The edge in front of the domain core: authentication (JWT with coalesced token refresh, Sign in with Apple with account linking), sessions, per-user rate limiting (Redis-backed, global 100 req/min), and feature flags that let capabilities ship dark and flip on without a deploy.

Domain services. The business logic, as NestJS modules with explicit boundaries: tenders, matching, search, proposals, credits, billing, notifications, site onboarding. Each module owns its data access and exposes a contract; the matching module can be reworked without the billing module noticing. This is where most of the code lives and where most client customisation happens.

Integration. How work moves: BullMQ queues on Redis for everything asynchronous — scraping jobs, embedding generation, proposal generation, document extraction, notifications. Long-running processes are modelled as durable jobs with retries and dead-lettering, consumed by horizontally scalable workers, not as HTTP calls that pray. A scheduler creates crawl jobs; workers and an AI extraction agent consume them; results flow back through one canonical upsert path with deterministic deduplication.

Data & AI. PostgreSQL with the pgvector extension — one database engine for relational data and 1024-dimension embeddings, which removes an entire class of sync-between-stores problems. Thirty-six models split across three Postgres schemas: app (user-facing), crawl (ingestion engine) and analytics (telemetry) — logical separation without the operational cost of separate databases. Search is hybrid: BM25 lexical plus dense-vector retrieval, fused with reciprocal rank fusion, because each catches what the other misses.

The AI centrepiece is the extraction pipeline: an eight-stage ladder that tries the cheapest, most deterministic method first and escalates only when it must. Official APIs and structured feeds first (OCDS for UK sources, the TED v3 API that alone yields ~4,000 EU notices a day), then learned selectors, then extraction rules, then a catalog of 27 hand-built site-specific extractors, and only then an LLM reasoning over the page — with a structured-parse fallback behind even that. The LLM is the escalation path, not the default, which is why the marginal cost per extracted record stays sane. We've written up individual rungs before — the extraction ladder behind a bot-protected portal and the R Shiny app at CERN.

Platform. The boring layer that decides whether weekends are quiet: containerised services on Fly.io (London), managed Postgres on Supabase, Redis, object storage, deploys from declarative config files in the repo, secrets in the platform's secret store, nightly backups with rotation. Deliberately managed-service-heavy — a boutique team's attention should go into the domain, not into babysitting Kubernetes.

Cross-cutting: evidence and observability. The layer clients underrate until an auditor or an incident arrives. The analytics schema is an append-only record of what the system did: audit logs, metric events, per-site health scores and incidents, and — critically for AI systems — a log of every LLM call with cost rolled up per session and per day. AI features are consent-gated per user, and the row-level-security middleware scopes queries to the requesting user at the database layer. When we say "compliance as architecture" on the services pages, this layer is what we mean: the evidence exists because the system emits it, not because someone remembered to write it down.

One language, end to end

The stack is TypeScript end to end: NestJS on Node for the API and domain services, Next.js and React for the web, one type system from the database schema (via Prisma) to the browser. The payoff for a small senior team is brutal in the best way — no context-switching tax, no serialization mismatches negotiated between a Python team and a frontend team, and every engineer can trace a feature across every tier. The stack is also deliberately hireable: clients can staff NestJS/Next.js/Postgres without hunting for unicorns, which matters because they own the platform at handover.

Two honest exceptions. The extraction agent is plain Node.js — no TypeScript, no build step — because a service that's redeployed many times a day while chasing hostile websites benefits more from zero build friction than from types. And Python enters only where its ML ecosystem is irreplaceable. Both are deliberate trade-offs, not inconsistencies; an architecture that can't tolerate a justified exception is a doctrine.

LLM access goes through a gateway layer rather than being sprinkled through the codebase: provider selection is configuration, every call is logged with token counts and cost attribution, and each AI decision point has a governed fallback — extraction falls back down the ladder, semantic search degrades to keyword search, and a consent flag or kill switch turns a feature off without turning the product off. Providers change pricing and models deprecate; the gateway is what makes that an afternoon's work instead of a migration.

Why "adapt" beats "design from scratch"

Every layer above encodes decisions that were paid for in production. The two most instructive parts of this platform are the ones that only exist because something broke.

Our scraping agent once wedged in production for fifteen hours while its health check returned 200 the whole time — one runaway browser renderer, memory exhausted, zero jobs completing, dashboard green. The fix is now architecture: health means work-liveness (is work actually completing?), not HTTP-liveness; a watchdog that terminates the process when progress stalls so the platform restarts it; a reaper that force-recycles browser contexts that outlive their budget. Similarly, Google once deindexed this site's 9,000 pages because a minority of junk auto-generated titles poisoned domain-wide quality — the post-mortem is public — and data-quality gates now sit at ingestion and at the serving edge.

A from-scratch design contains none of this, because none of it is foreseeable at the whiteboard. That's the real argument for a reference architecture: not that it saves weeks of architecture debate (it does), but that it front-loads the failure-mode knowledge you otherwise buy retail, in production, on your own timeline.

When we deliver a client platform, this is what "adapt" means: same layers, same stack defaults, same pre-made decisions on retrieval, queueing, evidence and fallbacks — configured to your domain, your data and your constraints, deployed into infrastructure you own, with no licence fee and no lock-in to us. The first production use case on a new foundation typically lands in 8–14 weeks, and every engagement starts with a fixed-price discovery sprint so the scope is written down before the build starts. If you're weighing a platform build — or weighing a rebuild of a prototype that never made it to production — the walkthrough above is the honest preview of what you'd get: AI platform design & implementation.

Methodology: drawn from the week ending 2026-07-11 tender corpus. Tender data sourced from public procurement portals worldwide; see our methodology for the extraction pipeline.