PDF Module β Architecture Reference
Last Updated: 2026-03-24
Module: apps/backend/src/pdf/
Pattern: Hexagonal Architecture (Ports & Adapters)
Module Structureβ
pdf/
βββ pdf.module.ts # NestJS module definition
βββ application/
β βββ dtos/
β β βββ pdf-controller.dto.ts # PDF generation DTOs
β β βββ template.dto.ts # Template management DTOs
β βββ ports/
β β βββ pdf-generator.port.ts # PDF generation contract
β β βββ template-renderer.port.ts # Template rendering contract
β βββ services/
β β βββ template-resolver.service.ts # Template resolution chain
β βββ transformers/
β β βββ sale-data.transformer.ts # (21 transformers, one per document type)
β β βββ ...
β βββ use-cases/
β βββ base-generate-pdf.use-case.ts # Abstract base for all PDF generation
β βββ generate-sale-pdf.use-case.ts # (21 concrete use cases)
β βββ upload-template.use-case.ts # Template CRUD use cases
β βββ update-template.use-case.ts
β βββ delete-template.use-case.ts
β βββ get-templates.use-case.ts
β βββ test-template.use-case.ts
β βββ preview-pdf.use-case.ts
β βββ generate-custom-pdf.use-case.ts
β βββ *-location-template-config.use-case.ts # Location config CRUD
βββ domain/
β βββ exceptions/
β β βββ template.exceptions.ts # TemplateNotFoundError, etc.
β βββ ports/
β β βββ template-repository.port.ts # Repository contract + types
β β βββ location-template-config-repository.port.ts
β β βββ template-audit.port.ts # Audit logging contract
β βββ services/
β β βββ template-validator.service.ts # Pure domain validation (no deps)
β βββ value-objects/
β βββ pdf-options.vo.ts # Page size, margins, orientation
βββ infrastructure/
βββ adapters/
β βββ puppeteer-pdf-generator.adapter.ts # Headless Chrome PDF gen (retry logic)
β βββ handlebars-template-renderer.adapter.ts # Handlebars compilation
β βββ file-system-pdf-storage.adapter.ts # File system storage
β βββ template-audit.adapter.ts # Kysely-based audit logging
β βββ template-cache.adapter.ts # LRU cache for compiled templates
βββ controllers/
β βββ pdf.controller.ts # POST /pdf/generate, /pdf/preview
β βββ template.controller.ts # CRUD /pdf/templates/*
β βββ location-template-config.controller.ts # CRUD /pdf/location-template-configs/*
βββ queues/
β βββ preview-generation.processor.ts # BullMQ async preview generation
βββ repositories/
β βββ template.repository.ts # Kysely impl of TemplateRepositoryPort
β βββ location-template-config.repository.ts
βββ templates/
βββ *.hbs # 18 Handlebars file-based templates (legacy)
Dependency Flowβ
Controllers β Use Cases β Application Services β Domain Ports
β
Infrastructure Adapters βββββββββββββββββββββββββββββββ
All cross-layer dependencies point inward toward the domain:
- Domain: Zero framework dependencies. Ports (interfaces), exceptions, value objects, pure validators.
- Application: Orchestration. Use cases depend on domain ports via injection. Contains DTOs, transformers, and the template resolver service.
- Infrastructure: Concrete implementations. Adapters implement domain ports. Controllers handle HTTP. Repositories use Kysely.
Key Design Decisionsβ
Template Resolution Chainβ
TemplateResolverService resolves templates in priority order:
- Explicit template ID β caller passes a specific template UUID
- Location-specific β
location_template_configfor the location + document type - Business-level β default template for the business + document type
- System-level β global system template for the document type
- Fallback β file-based Handlebars template from
infrastructure/templates/
Base Use Case Patternβ
All 21 PDF generation use cases extend BaseGeneratePdfUseCase<TEntity>:
- Subclasses implement:
getDocumentType(),getFallbackTemplateName(),getEntityBusinessId(),generateFilename() - Base class handles: template resolution, caching, PDF generation, error handling
- Each use case has a paired
DataTransformerthat maps the entity to template data
Port Bindings (pdf.module.ts)β
| Injection Token | Concrete Implementation |
|---|---|
"PdfGeneratorPort" | PuppeteerPdfGeneratorAdapter |
"TemplateRendererPort" | HandlebarsTemplateRendererAdapter |
"PdfStoragePort" | FileSystemPdfStorageAdapter |
TEMPLATE_REPOSITORY_PORT | TemplateRepository |
LOCATION_TEMPLATE_CONFIG_REPOSITORY_PORT | LocationTemplateConfigRepository |
TEMPLATE_AUDIT_PORT | TemplateAuditAdapter |
Puppeteer Retry Logicβ
PuppeteerPdfGeneratorAdapter uses executeWithRetry() (max 3 attempts) with exponential backoff for both generatePdf and generatePdfFromUrl. Retryable errors: protocol errors, target closed, navigation timeouts.
Template Cacheβ
TemplateCacheAdapter uses an LRU cache (100 entries) for compiled Handlebars templates. Cache key = template UUID. Invalidated on template update/delete.
API Endpointsβ
PDF Generation (PdfController β /pdf)β
| Method | Path | Description |
|---|---|---|
| POST | /pdf/generate | Generate PDF from stored template + data |
| POST | /pdf/generate-from-html | Generate PDF from raw HTML |
| POST | /pdf/preview | Preview template as rendered HTML |
| GET | /pdf/template-files | List file-based templates (legacy) |
| POST | /pdf/templates/clear-cache | Clear file template cache |
| POST | /pdf/templates/:templateId/reload | Reload specific template |
| GET | /pdf/health | Service health check |
Template Management (TemplateController β /pdf/templates)β
| Method | Path | Description |
|---|---|---|
| POST | /pdf/templates/upload | Upload new template (rate limited: 10/min) |
| GET | /pdf/templates | List templates with pagination + search |
| GET | /pdf/templates/available | All templates (business + system) |
| GET | /pdf/templates/health | Health check with cache stats (public) |
| GET | /pdf/templates/cache/stats | Cache statistics (public) |
| GET | /pdf/templates/variables/:documentType | Template variables for document type |
| GET | /pdf/templates/:id | Get template by ID |
| PATCH | /pdf/templates/:id | Update template |
| DELETE | /pdf/templates/:id | Soft-delete template |
| POST | /pdf/templates/:id/test | Test template with sample data β PDF |
| POST | /pdf/templates/test | Test template (alt endpoint) |
| POST | /pdf/templates/preview | Preview template rendering as HTML |
| GET | /pdf/templates/:id/audit | Audit history for template |
| POST | /pdf/templates/cache/clear | Clear all template caches |
Location Template Config (LocationTemplateConfigController β /pdf/location-template-configs)β
| Method | Path | Description |
|---|---|---|
| POST | /pdf/location-template-configs | Create config (rate limited: 20/min) |
| GET | /pdf/location-template-configs | Get configs by location (query: locationId) |
| GET | /pdf/location-template-configs/by-document-type | Get by location + doc type |
| GET | /pdf/location-template-configs/:id | Get config by ID |
| PATCH | /pdf/location-template-configs/:id | Update config (rate limited: 30/min) |
| DELETE | /pdf/location-template-configs/:id | Delete config (rate limited: 20/min) |
Supported Document Typesβ
All types defined in DocumentType enum (packages/global/enums/document-type.enums.ts):
sale, purchase, purchase_order, quote, cash_register_session, cash_register_z_report, accounts_receivable_receipt, accounts_payable_payment, service_booking, inventory_adjustment, transfer_request, transfer_dispatch_note, transfer_goods_receipt, inventory_transfer, contractor_assignment, goods_received_note, material_consumption, production_run, credit_note, debit_note, cancellation
Bruno API Collectionsβ
API collections for testing are at: api-client/flowpos/collections/pdf/
pdf/
βββ opencollection.yml
βββ Generate PDF from Template.yml
βββ Preview HTML (No PDF Generation).yml
βββ Generate PDF from HTML Content.yml
βββ Check health.yml
βββ ...
βββ templates/
β βββ folder.yml
β βββ templates.yml (list)
β βββ template - upload.yml
β βββ template - get by id.yml
β βββ template - update.yml
β βββ template - delete.yml
β βββ template - test.yml
β βββ template - test (alt).yml
β βββ template - preview.yml
β βββ template - available.yml
β βββ template - variables.yml
β βββ template - audit.yml
β βββ health.yml
β βββ cache stats.yml
β βββ Clear cache.yml
βββ location-template-configs/
βββ folder.yml
βββ create config.yml
βββ get by location.yml
βββ get by location and type.yml
βββ get by id.yml
βββ update config.yml
βββ delete config.yml