The short answer: architecture is not a diagram you show people. It is four things that can be executed — directories, guards, ports, contracts. Diagrams expire; directories do not. Documentation drifts; guards do not. A structure you can verify with one command is an architecture.
This system now looks like this: 1 base + 6 business faces + 1 hub, with the number of faces frozen and processes allowed to split.
The numbers
| Item | Value | Scope |
|---|---|---|
| Business faces | exactly 6 (names frozen) | Guard test_business_boundary_is_exactly_six |
| Hub | 1 (interface only; no business logic, no database) | Separate process |
| Base contracts | 20 (frozen; may shrink, never grow) | Line budget 6,135 / 6,200 |
| Platform services | 5 (do not occupy a business slot) | hub / gen / ops / console / iam |
| Frozen invariants | 1 | Changing the face count requires guard + registry + docs in the same commit |
| Physical refactor | 1 (no behaviour change) | Commit message: “directories as architecture, zero external change” |
Scope note: the line budget is a freeze, not a target. Its job is to stop the base from quietly growing.

Three decisions
1. Directories as architecture: a refactor with zero behaviour change
Early code was organised by type: api/, services/, scripts/. The problem was not mess — it was that you could not see the boundaries. From the directory alone, you could not tell who owns a piece, whether you may change it, or what breaks if it fails.
The refactor did one thing: made the directories match the architecture one-to-one.
app/ base (contracts, schema, gates)
business/01-…/ business faces (one directory each)
business/02-…/
platform/hub/ the hub
platform/console/ console assets
The hard constraint: zero external behaviour change. Only move files, fix imports, update service paths — no interface semantics touched. The test is unglamorous but effective: run the same contract suite before and after, and compare responses line by line.
Cost: a large-scale file move (hundreds of files) and every reference updated. Benefit: from then on, “where does this change go?” is answered by the directory, not by memory.
2. Freezing 1+6+1: faces fixed, processes flexible
The real danger is not a wrong design; it is slow drift — a “temporary service” today, a “helper module” tomorrow, and six months later nobody can say how many boundaries exist.
So we froze an invariant:
- exactly 6 business faces, names frozen
- exactly 1 hub
- platform services may not occupy a face
At the same time we left one opening: the number of processes may change. Re-edit and study live in the same face as two processes — what splits is the failure domain, not the face.
Cost: new capabilities cannot just open a new directory; they must fit an existing face. Adding a face requires changing the guard constant, the registry and the docs in one commit — change only one and CI goes red.
Benefit: anyone, including me three months later, can prove with a single test that the structure is intact.
3. One-way dependencies: services reach the base over HTTP
The rule is one sentence: service directory → HTTP → base. Service code may not import base code, and the base may not import services.
It comes with a second rule: the base owns the schema; subsystems own the semantics. Subsystems never connect to the database directly; they read and write through base contracts. That way, changing databases, adding tenant isolation or auditing happens in one place.
Cost: an extra HTTP hop; slower than a direct call.
Benefit: boundaries are testable. The guard inspects the import graph and goes red on violations — not a promise made in code review.
Five questions before drawing an architecture
- Boundary: if this capability fails, who is affected? (failure domain)
- Contract: how many interfaces are exposed? Can the list only shrink?
- Data: who owns the schema? Who may only read/write their own semantics?
- Process: can one face run multiple processes? Are you splitting failure domains or business logic?
- Guard: which test proves, in ten seconds, that the structure is intact?
If you cannot answer all five, do not draw a diagram yet — write the directories and the tests first.
Where this does not apply
- Good fit: multi-person teams, long-lived systems, anything that will be handed over to a client.
- Poor fit: throwaway scripts, or tools you will delete in three days. Do not pay the architecture tax for those.
FAQ
Why bind directories to architecture?
Because directories are the only documentation that never gets forgotten. Diagrams expire and READMEs drift, but ls is always true.
Will 1+6+1 limit future expansion?
It limits “adding faces casually”, but allows “adding processes inside a face”. Growth happens inside the faces, not by opening new directories forever.
How do you verify a “zero change” refactor?
Freeze a contract test suite first (input/output compared line by line), rerun after the refactor. Any difference means it was not a pure physical move.
Last updated: 2026-10-19 (first draft)