PdfGeneration
Info
Architectural truth for
PdfGenerationin theDocumentsfeature. This leaf module handles the asynchronous and manual generation, storage, and retrieval of PDF files.
Overview
PDF Generation is a critical export feature of MILTON. A completed document (consisting of sections and generated blocks) must be converted into a shareable PDF. This process happens automatically at the end of the DocumentProcessingSaga or can be triggered manually by a user.
Because rendering an entire document to HTML and converting it via an external service (Gotenberg) can be slow, PDF generation is handled as a background message.
Business Logic Intent
The core intent is to cleanly separate the heavy lifting of PDF rendering from the API’s HTTP request-response cycle.
- Rendering: The
IDocumentRenderServicetakes the document entity and renders it into a complete HTML string (with embedded assets/styling). - Conversion: The HTML is sent to the Gotenberg container via
IPdfStorageService(or similar infrastructure) which returns a PDF file. - Storage: The PDF binary is uploaded to an S3-compatible blob store.
- Linking: The S3 object key (
PdfStorageKey) is saved to theDocumentrecord inmilton-db.
Endpoints & Flow
GeneratePdfEndpoint: Manual trigger. Enqueues aGeneratePdfCommandto Wolverine and returns a200 OKto the client immediately.DownloadPdfEndpoint: Synchronous fetch. Given aDocumentId, it retrieves thePdfStorageKeyfrom the database, fetches the binary from S3, and streams it back to the client as anapplication/pdfdownload.
Wolverine Handlers
GeneratePdfOnCompletionHandler: ConsumesGeneratePdfCommand(emitted manually or by the document saga). Orchestrates the render → convert → store process and then updates theDocumententity. Finally, it publishesPdfGeneratedEventso SignalR can notify the client that the PDF is ready.
Sequence: PDF Generation Pipeline
sequenceDiagram participant Client participant API as API / Saga participant Bus as Wolverine participant Handler as GeneratePdfHandler participant DB as milton-db participant Gotenberg as Gotenberg (PDF) participant S3 as S3 Storage alt Manual Trigger Client->>API: POST /documents/{id}/pdf else Saga Trigger API->>API: DocumentGenerationCompleted end API->>Bus: Publish(GeneratePdfCommand) Bus->>Handler: Consume(GeneratePdfCommand) Handler->>DB: Fetch Document & Blocks Handler->>Handler: Render to HTML Handler->>Gotenberg: Convert HTML to PDF Gotenberg-->>Handler: PDF binary stream Handler->>S3: Upload PDF stream S3-->>Handler: Return StorageKey Handler->>DB: Update Document.PdfStorageKey Handler->>Bus: Publish(PdfGeneratedEvent) Bus-->>Client: (SignalR) PDF Ready Notification
Domain Models
- Commands/Events:
GeneratePdfCommand,PdfGeneratedEvent,PdfGenerationFailedEvent - Request Models:
DownloadPdfRequest,GeneratePdfRequest