Implementation
Module Structure
src/domus_api/
├── main.py App factory, lifespan, 3 root endpoints, 25 router registrations
├── config.py pydantic-settings with DOMUS_API_ prefix
├── exceptions.py PageNotFound, InvalidPath
├── models/
│ ├── page.py PageSummary, PageDetail, PageList
│ ├── project.py ProjectCreate, ProjectResponse, ProjectStatusUpdate
│ ├── case_study.py Severity, RiskLevel, IncidentCreate, CaseStudyResponse
│ └── search.py SearchResult, SearchResponse
├── routes/ 25 routers, one per content domain
│ ├── pages.py /pages, /pages/\{path}
│ ├── search.py /search?q=
│ ├── projects.py GET, POST, PATCH /\{slug}/status
│ ├── case_studies.py POST incidents, changes, RCAs
│ ├── case_studies_read.py GET case studies by type
│ └── ... 20 more domain routers
└── services/
├── filesystem.py DocumentCache — in-memory metadata store
├── parser.py AsciiDoc header extraction (title, attributes, includes)
├── scaffolder.py File generation per STD-001/005/010/011
├── search.py Regex full-text search with context windows
├── attributes.py antora.yml attribute parser
└── dependencies.py FastAPI Depends(get_cache) injection
Dependency Injection
Route handlers receive the document cache via FastAPI’s Depends mechanism. The cache is stored on app.state during the lifespan context and retrieved by get_cache():
# services/dependencies.py
def get_cache(request: Request) -> DocumentCache:
return request.app.state.cache
# routes/pages.py
@router.get("")
async def list_pages(
cache: DocumentCache = Depends(get_cache),
category: str | None = Query(None),
):
pages = cache.list_pages(category=category)
This replaced the original module-level cache = None # type: ignore pattern across 20 route files. The Depends pattern is type-safe, testable, and does not require the lifespan to know about individual route modules.
DocumentCache Architecture
The cache is a Python dict loaded once at startup. It scans pages/ and partials/ for all .adoc files, parses each file’s first 30 lines for metadata (title, attributes, includes), and stores the results keyed by relative path.
| Method | Purpose |
|---|---|
|
Full filesystem scan — called once at startup |
|
Parse and cache a single page — called after POST writes |
|
Parse and cache a single partial — called after project scaffold |
|
Lookup by relative path, returns metadata dict or None |
|
Filtered listing with pagination |
|
Filter pages where |
|
Filter pages in |
Content is loaded on demand via Path.read_text() — only when a specific page is requested. List operations return metadata only. This keeps memory usage proportional to file count (~3,500 files × ~200 bytes metadata ≈ 700 KB), not total content size.
Service Layer
| Service | Responsibility |
|---|---|
DocumentCache ( |
In-memory metadata cache. Provides all read operations. Startup load + targeted invalidation on writes. |
Parser ( |
Extracts title, attributes, and include directives from AsciiDoc headers. First 30 lines only — fast. |
Scaffolder ( |
Write engine. Creates incident reports (STD-011), change requests (STD-005), RCAs (STD-010), and projects (STD-001). Templates match the exact output of the CLI skills. |
Search ( |
Regex full-text search across all cached pages. Returns ranked results with 80-char context windows around matches. |
Attributes ( |
Parses |
Dependencies ( |
FastAPI |
Configuration
All settings via pydantic-settings with DOMUS_API_ environment variable prefix:
| Setting | Default | Override |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|