Stack¶
Technology and rationale
Status: Proposed. Nothing below is running yet. Version: 0.1 Date: 2026-08-11
Every choice here has to survive the question why not the boring option? Where the boring option wins, we take it.
Runtime¶
| Layer | Technology | Rationale |
|---|---|---|
| Compute | Quarkus 3, GraalVM native image | Sub-second start, ~64 MB resident. A supervisor's dashboard should not wait on a JVM warming up, and a regulator's infrastructure budget is not elastic. |
| Reactive core | Mutiny | Non-blocking IO on the write path. Validation fan-out over dozens of rules must not hold a thread each. |
| Decision rules | Kogito DMN | Validation and prudential computation as decision tables a supervisor can read and, eventually, edit. The alternative — rules in Java — puts the regulator back in the vendor queue. |
| Workflow | Kogito BPMN | Review and enforcement lifecycles are long-running, human-centric, and auditable. Hand-rolled state machines here become the thing nobody dares change. |
| Web tier | Quarkus REST (JAX-RS) + Quinoa | Quinoa serves the front-end from the same artefact, so there is one deployable and no CORS story. |
| Front-end | Web components + lit-html, Redux Toolkit | Web standards first. A supervisory tool has a ten-year life; a framework rewrite every three years is a liability. The design system is prototyped in Next.js for velocity, then expressed as custom elements. |
Data¶
| Concern | Technology | Rationale |
|---|---|---|
| System of record | PostgreSQL | Returns are template-shaped, so submitted values live in jsonb keyed by field id and validated against the template version. Structural data — licences, obligations, cases, audit — is fully typed and constrained. |
| Current state | Redis | Hydrated current submission documents and idempotency keys. A read of "the return as it stands" must not reassemble versions on every request. |
| Events | Kafka via transactional outbox | The outbox keeps event emission in the same transaction as the state change, so a projection can never diverge from the record that caused it. |
| Documents | S3-compatible object store | Attachments are content-addressed and checksummed. The checksum goes in the audit trail; the bytes do not go in the database. |
| Search | PostgreSQL full-text | Sufficient for firm and return lookup at supervisory scale. Revisit only if measurement demands it. |
| Analytics | Projection tables in PostgreSQL | Flattened, typed, denormalised read models. Separate schema, separate credentials, no writes. |
Why jsonb for submitted values¶
This is the load-bearing data decision, so it gets argued rather than asserted.
A configurable return template means the column set is not known at build time. The three options:
| Option | Verdict |
|---|---|
| A table per return type, generated by DDL | Rejected. Migrations become a runtime concern and thirty returns become thirty schemas that drift. |
| Entity-attribute-value rows | Rejected. Every read becomes a pivot, and the query plans get ugly fast. |
jsonb document per submission, validated against the template version |
Chosen. One shape, indexable with expression indexes on the fields that matter, and the template version is the schema. |
The cost is that jsonb will not enforce field types — so Assay validates on write and the
write is rejected if the document does not conform to its template version. The schema
guarantee moves from the database to an engine we control and can cite. Projections then flatten
the fields analysts actually query into typed columns, so reporting gets real types and real
indexes.
Platform¶
| Concern | Technology | Rationale |
|---|---|---|
| Identity | OIDC (Keycloak), per-firm realms or groups | Firms authenticate as their own tenant. Maker and approver are distinct roles, not a flag on a user. |
| Authorisation | Attribute-based, evaluated per firm and return | A firm user may only see obligations for licences they are mandated to act on. |
| Observability | OpenTelemetry, Micrometer, structured JSON logs | Trace a submission from portal to projection through one trace id. |
| Deployment | OCI containers, Kubernetes or plain hosts | Native images run anywhere. No requirement for a managed platform the regulator cannot procure. |
| Docs | MkDocs Material, Cloudflare Pages | This site. See Publish docs. |
What we are deliberately not doing¶
| Not doing | Why |
|---|---|
| Microservices per engine | Engine boundaries are module boundaries first. One deployable until measurement says otherwise; a regulator does not need a distributed systems problem on day one. |
| An event-sourced write model | Append-only versioning gives us the audit properties we need without the replay complexity. Revisit if a real requirement demands it. |
| A rules DSL of our own | DMN is a standard with tooling and a specification. Ours would have neither. |
| Excel as an integration contract | Excel import is a convenience for firms. It is never the source of truth; the template is. |
| A React production front-end | The prototype is React for speed of design. The product is web components. |
Related¶
- Architecture doctrine — the laws these choices serve
- Platform overview — the engines
- Decision log — dated records of the calls above