Docs Structure Convention
Single source of truth for how every backend package's developer docs are structured. Mirror skeleton lives at
content/{en,vi}/developer/packages/_template/(excluded from the published site — copy from source). Every package — current or future — follows this exact shape.
1. Goals
| Goal | Why it matters |
|---|---|
| Uniformity | A reader who knows one package's docs knows them all. No discovery cost. |
| Compactness | Tables/diagrams over prose. A senior dev should orient in <2 min. |
| Visual-first | Mermaid (erDiagram, stateDiagram-v2, flowchart, sequenceDiagram, C4Container) + tables. Prose only when no chart fits. |
| Diátaxis-clean | One quadrant per page (reference / explanation / how-to / tutorial). No mixing. |
| Spec-as-source | API reference renders from /doc/openapi.json, not hand-written. |
| Co-located ops | Per-package operations + runbook (you-build-it-you-run-it). |
2. Folder Layout
content/{en,vi}/developer/packages/<service-name>/
├── index.md # ⓜ Identity card + service catalog
├── architecture.md # ⓜ C4 + state machines + runtime scenarios
├── domain-model.md # ⓜ ERD + entity tables
├── api-events.md # ⓜ Kafka + WS topics in/out + payloads
├── integration.md # ⓜ Sister-service + external-system contracts
├── configuration.md # ⓜ Env vars + flags + seeded data
├── operations.md # ⓜ Deploy + observability + security + runbook
├── <feature>.md ... # ⊕ Optional per-feature deep dive
├── decisions/
│ ├── index.md # ⓜ ADR catalogue
│ └── NNNN-<slug>.md # ⓜ MADR — one per decision
└── changelog.md # ⊕ Optional, only if stable contractⓜ mandatory · ⊕ optional
No
api-rest.md— REST reference is rendered live from each service's/doc/openapi.json(Scalar viewer at/doc, gateway portal explorer). Hand-maintained REST tables drift quickly. Link to the live spec fromindex.mdinstead.
3. Diátaxis Quadrant Map
| Page | Quadrant | Audience |
|---|---|---|
index.md | Reference (identity card) | Onboarding dev, integrator |
architecture.md | Explanation | Senior dev, SRE, architect |
domain-model.md | Reference | Implementer |
Live /doc/openapi.json | Reference | API consumer |
api-events.md | Reference | Event subscriber/producer |
integration.md | Explanation + reference | Sister-team dev |
<feature>.md | Reference + small explanation | Feature owner |
configuration.md | Reference | Operator, deployer |
operations.md | How-to + reference | SRE, on-call |
decisions/NNNN.md | Explanation | Anyone asking "why?" |
changelog.md | Reference | API consumer, integrator |
Rule: never bleed quadrants. If a reference page grows a "Why we did this" section >2 paragraphs, move it to an ADR and link.
4. File-Split Rules
| Rule | Threshold |
|---|---|
| Promote topic to its own file | ≥150 lines OR ≥10 endpoints/events/tables OR ≥1 mermaid diagram OR referenced from ≥1 other page |
| Promote folder to a sub-tree | ≥3 sibling pages on the same theme |
Inline in index.md | <40 lines AND <5 items |
| One ADR per file | Always — never batch decisions |
| REST + Async always separate | Different audiences, different rendering |
5. Per-Page Templates
Skeleton files live at
content/{en,vi}/developer/packages/_template/(source only). Section count and order are fixed.
index.md — 14 sections
Quick Reference · Purpose & Scope · Tech Stack · Project Structure · Architecture · Domain Snapshot · Surface Summary · Components · Services · Repositories · Entry Points · Configuration · Operations · Related Pages
architecture.md — 7 sections
System Context (C4 L1) · Container View (C4 L2) · Component View (C4 L3) · State Machines Index · Runtime Scenarios · Crosscutting Concerns · Related Pages
domain-model.md — 5 sections
Full ERD · Entities (one block per table) · Cross-entity Invariants · Soft-delete Behavior · Related Pages
api-events.md — 8 sections
Inbound Kafka · Outbound Kafka · Inbound BullMQ · Outbound BullMQ · WebSocket Emissions · Payload Schemas · Idempotency & Ordering · Related Pages
integration.md — 5 sections
Sister Services · External Systems · Critical Cross-Service Flows · Contract Stability · Related Pages
configuration.md — 5 sections
Environment Variables · Feature Flags · Seeded Data · Configuration Storage · Related Pages
operations.md — 5 sections
Deployment · Observability · Security · Runbook · Related Pages
<feature>.md — 7 sections
Overview · Entity Model · Lifecycle · Operations · REST Endpoints · Events · Related Pages
decisions/index.md
Single table: ID · Status · Date · Title · Supersedes.
decisions/NNNN-<slug>.md — MADR
Context · Decision · Consequences · Alternatives Considered · References.
6. Authoring Style Rules
| Rule | Enforcement |
|---|---|
| Visual-first | Tables/mermaid for any list ≥3 items. Prose only when neither fits. |
| Compact prose | Max 4 lines per paragraph. Bullet > paragraph when possible. |
| Frontmatter | Always title, description, outline: deep. |
| Code blocks | Only for: directory trees · env examples · JSON payloads · TS interfaces. |
| Cross-link | Internal links use /en/developer/... absolute path. |
| English-only in identifiers | Vietnamese OK only in user-facing localized strings. |
| No emojis | Unless explicitly part of UI being documented. |
7. ADR (MADR) Rules
| Rule | Detail |
|---|---|
| Numbering | Zero-padded 4-digit, sequential, never reused |
| Status values | Proposed · Accepted · Deprecated · Superseded by NNNN |
| Date | Decision date (not file-creation date) |
| Update | Append-only — supersede via new ADR; flip old status |
| Cross-service ADRs | Live in developer/decisions/, not per-package |
| Per-service ADRs | developer/packages/<svc>/decisions/ |
8. API Reference — OpenAPI Integration
| Aspect | Convention |
|---|---|
| Source of truth | Each service exposes /doc/openapi.json at runtime |
| Wiki rendering | No api-rest.md page — index.md Surface Summary table links directly to the live spec |
| Live exploration | Gateway portal at packages/gateway/portal consumes /doc/openapi.json and renders interactive UI |
| Hand-written reference | Forbidden — never duplicate field-level schema in markdown |
9. Operations vs Runbook Split
| Scope | Lives in |
|---|---|
| Per-service deployment, observability, security, alert classes | <svc>/operations.md |
| Cross-service incidents, system-wide procedures, war-room playbooks | runbook/ central tree |
<svc>/operations.md links to runbook/ for cross-service work | Always |
10. EN ↔ VI Parallel
| Rule | Detail |
|---|---|
| Mirror | Every EN file has a VI counterpart at the same relative path |
| Line parity | EN/VI line counts within ±5% |
| Translation order | EN ships first → VI follows in batch |
| Mermaid labels | Translate node labels; keep state/event identifiers in English |
Frontmatter title & description | Translated |
| Code/identifier names | Never translated |
11. Sidebar Convention
Every package's sidebar entry MUST nest its sub-pages into three Diátaxis groups + a Decisions link, in this order:
Package Name
├─ Concepts (collapsed by default)
│ ├─ Architecture
│ ├─ Domain Model
│ └─ Integration
├─ Reference (collapsed)
│ ├─ API Events
│ ├─ Configuration
│ └─ Operations
├─ Features (collapsed)
│ ├─ <feature-1>
│ ├─ <feature-2>
│ └─ ...
└─ DecisionsRules:
- Group order: Concepts → Reference → Features → Decisions (always).
- Collapsed by default — keeps initial sidebar compact (3 visible items + Decisions).
Conceptsincludes pages that explain why/how the system is shaped (architecture, domain, cross-service integration).Referenceincludes pages that are lookup-only (REST endpoints, events, env vars, ops/runbook).Featuresincludes per-feature deep-dive pages (one per non-trivial domain feature).Decisionsis a flat link todecisions/index.md; ADR list is rendered inside that page.- If a group has zero pages (e.g. a tiny service with no feature pages), omit the group rather than show an empty section.
VitePress shape (.vitepress/locales/{en,vi}.ts):
{
text: '<Service Name>',
link: '/en/developer/packages/<svc>/',
collapsed: true,
items: [
{ text: 'Concepts', collapsed: true, items: [/* ... */] },
{ text: 'Reference', collapsed: true, items: [/* ... */] },
{ text: 'Features', collapsed: true, items: [/* ... */] },
{ text: 'Decisions', link: '/en/developer/packages/<svc>/decisions/' },
],
}12. Bootstrapping a New Package
# 1. Copy skeleton
cp -r content/en/developer/packages/_template \
content/en/developer/packages/<svc>
cp -r content/vi/developer/packages/_template \
content/vi/developer/packages/<svc>
# 2. Replace placeholders
# <service-name>, <Service Name>, TODO:, <n>, <NNNNN>
# 3. Register in sidebar
# .vitepress/locales/en.ts and vi.ts| Step | Owner |
|---|---|
| Copy skeleton | Author of the new package |
| Fill mandatory 8 + decisions/index.md + at least 1 ADR | Same |
| Add per-feature pages as features are added | Per feature owner |
| Sidebar registration PR | Author |
| Code review approval | Doc convention checker (CI lint optional) |
13. Anti-patterns (do not do)
| Anti-pattern | Why bad |
|---|---|
| Hand-writing full OpenAPI reference in markdown | Drifts from code on every PR — link to live /doc/openapi.json instead |
Adding api-rest.md to a package | Forbidden — REST surface lives in OpenAPI spec only |
| Mixing reference + explanation in same page | Diátaxis violation; unscannable |
| Multi-decision ADR files | Breaks stable URLs and supersession |
| Operational docs in a separate SRE wiki | Splits ownership, decays fast |
| C4 Code-level diagrams | Source code is the source — diagram rots |
| Long prose paragraphs (>4 lines) | Visual-first principle |
| Vietnamese identifiers in code samples | Breaks English-only rule |
Skipping the _template/ skeleton | Causes structural drift |
| Flat sidebar with 10+ items at one level | Hard to scan; group into Concepts/Reference/Features per §11 |
14. Related
_template/skeleton —content/{en,vi}/developer/packages/_template/- Diátaxis framework — https://diataxis.fr/
- arc42 template — https://arc42.org/overview
- C4 model — https://c4model.com/
- MADR (Markdown ADR) — https://adr.github.io/madr/