Document Generation
api-v1 shared service that renders a document's data into branded, self-contained HTML and a PDF. No HTTP surface, no persistence, no lifecycle state (ADR-0132).
Bounded Context Boundaries
apps/api-v1/src/modules/document-generation is a Generic Subdomain. It was graduated from the documents skill (ADR-0132). It runs inside api-v1 and is not a deployable of its own.
It owns:
- document types: a schema, a view model and a template each;
- BuildPlan’s brand standards (
brand.json, the embedded Liberation Sans fonts and the logo); - the render pipeline: validate → view → HTML → PDF.
It never owns, stores, attaches or sends a document. The calling module keeps the record and the file.
Surface
One inbound port, ForRenderingDocuments.render({ caller, documentType, data }), which returns { pdf, html, documentNumber, title }. Other modules import only types from index.ts. App composition hands each caller the port, and the caller wraps it in its own ACL adapter. Failures are UNKNOWN_DOCUMENT_TYPE, DOCUMENT_VALIDATION_FAILED (problems by JSON path), DOCUMENT_RENDER_FAILED (stage assets, html or pdf) and DOCUMENT_RENDER_TIMEOUT.
Vendor code sits behind outbound ports: Handlebars (ForRenderingHtml), Playwright Chromium (ForPrintingPdfs) and the file system (ForLoadingDocumentAssets).
Document types
| Id | Shapes | Caller |
|---|---|---|
invoice | draft (open figures print “To be determined”, DRAFT watermark), issued (every figure stated) | Construction Management: Print invoice on a Product Order |
The skill’s change order and purchase order are not graduated (ADR-0132).
Dependency edges
Hex §6:
document-generation → sharedconstruction-management → document-generation, throughDocumentGenerationInvoiceAclAdapteronly
The module emits no events.
Documentation Links
- Vocabulary:
docs/api-v1/document-generation/GLOSSARY.md - How to add a document type:
docs/api-v1/document-generation/README.md