FAST SCRIPTS Docs
/
GitHub

Core concepts

Architecture

A modular monolith with boundaries that are enforced by tests rather than convention, deployed as a loopback-only container topology. Service extraction is deliberately deferred until it is justified.

#Container topology

The browser reaches an unprivileged Nginx edge, which serves the React client and proxies the API on the same origin. The backend has no published host port. There is no adapter, endpoint, or credential for any prescription network.

Browser
  │  HTTP on 127.0.0.1:8081  (the only published application origin)
  ▼
Nginx edge  ──  React client + same-origin API proxy
  │  internal network only
  ▼
Spring Boot backend  (Java 21, modular monolith)
  │  JDBC + same-database relay
  ▼
PostgreSQL 17  —  committed truth + local relay record

   ╌╌ no adapter or connection ╌╌▶  prescription network / pharmacy

Both the frontend and data Docker networks are declared internal: true, so the backend has no route off the host. That topology — not a lint rule — is what carries the no-egress property.

#Module structure

Every capability module is split into four rings, and the separation is a test rather than a convention:

Capability module  (prescribing · review · idempotency · events · audit · terminology)

  api             controllers, DTOs
   └─▶ application    orchestration, ports
        └─▶ domain        framework-free rules
  infrastructure ─▶ application     JDBC, schedulers, adapters

#Enforced rules

ArchUnit fails the build if:

  • the domain gains a Spring, JPA, or Servlet dependency;
  • the API layer reaches persistence;
  • the application layer imports infrastructure;
  • top-level modules form a dependency cycle;
  • production code depends on any of six enumerated HTTP-client packages — java.net.http, javax.net.ssl, Spring's RestTemplate and WebClient, OkHttp, and Apache HttpClient 5.
Honest caveat

The network rule is a denylist, not an exhaustive guarantee — a raw java.net.Socket or HttpURLConnection call would not trip it. The actual no-egress property comes from the container topology described above. Broadening the rule toward deny-by-default is tracked hardening work. Separately, the domain is stricter than its rule requires: every domain source file today imports nothing outside java.* and the project namespace.

#Ports and seams

Extension points are declared as ports in the application or domain layer, with implementations supplied by infrastructure. New capabilities are expected to reuse these rather than invent parallel structures.

PortRoleCurrent implementation
MedicationHydratorResolve medication identity from a terminology sourceDeterministic synthetic fixture
QuantityUnitResolverResolve versioned quantity unitsDatabase-backed, exactly-one-active enforcement
DraftPersistenceCommit the clinical aggregateSingle-transaction JDBC implementation
IdGeneratorMint prefixed opaque public identifiersCryptographically seeded generator
PrescriptionTransportPortFuture network transport seamNot implemented

#The integrity spine

Four mechanisms carry the correctness guarantees, and they are the reason the platform can later be trusted with something more than fixtures.

#Single-transaction clinical commit

The prescription aggregate, medication and diagnosis snapshots, direction lines, validation run, status history, review-grant verifier, audit chain entries, outbox event, and encrypted idempotency record all commit together — or none of them do. A commit guard re-locks the entire authority graph inside the transaction, so an authorization revocation committed mid-flight deterministically wins.

#Hash-chained evidence

Each tenant has an append-only audit stream whose entries are chained by hash, with a locked sequence head and a repeatable-read verifier that re-derives the chain. A parallel stream records security observations. Database triggers reject updates and deletes on evidence rows, so tampering requires breaking the chain visibly.

#Idempotency with exact replay

Keys are scoped by tenant, caller, and operation, then blinded. A serializer-independent semantic fingerprint means logically identical requests match even when their JSON differs. The stored success response is encrypted with AES-256-GCM and bound by associated data, so replay metadata cannot be altered without detection. Retention is bounded, and expired keys become permanent scrubbed tombstones that can never create a second aggregate.

#Database-level lockdown

Persistence spans four migrations across 32 tables. Composite tenant foreign keys cover every tenant-owned edge — 43 established by the tenant-graph migration, extended by exact aggregate bindings later — so a cross-tenant assembly is rejected by PostgreSQL, not merely by application code. The integrity-lockdown migration additionally permits only the two implemented lifecycle state pairs, rejects update and delete on clinical and evidence rows, constrains the replay contract, and forbids outbox deletion.

Expanding this safely

Those constraints are deliberately restrictive, and new capability is expected to migrate them forward rather than weaken them in place. The status check, the state-pair check, and the transition trigger must evolve in one coordinated change — altering one without the others produces rows that insert but can never transition.

#Technology baseline

LayerBaseline
BackendJava 21, Spring Boot 3.5, Spring JDBC, Gradle 8.14
DatabaseDigest-pinned PostgreSQL 17, in Compose and every integration suite
BrowserReact 19, TypeScript 5.8, Vite 7, Zod, TanStack Query
EdgeUnprivileged Nginx, same-origin proxy, restrictive browser headers
ContractsOpenAPI 3.1, JSON Schema Draft 2020-12
VerificationJUnit, ArchUnit, Testcontainers, Vitest, Testing Library, Playwright, Redocly, AJV, Gitleaks

FAST SCRIPTS is a private development repository. No license to use, copy, modify, or distribute is granted.