← Back to Core Domains

4.7 Documents and Retention

KMP centralizes uploaded-file metadata and storage operations in DocumentService. Domain modules store a document reference and describe the owning entity; they do not construct filesystem/object-storage paths themselves.

Architecture

Component Responsibility
DocumentsTable / Document Tenant-local metadata, ownership, soft deletion, and audit fields
DocumentService Validation, persistence, download/inline responses, image thumbnails, PDF previews, reassignment, deletion
TenantDocumentStorageConfigResolver Resolves tenant-aware Azure container/prefix configuration; adapter selection remains global
ImageToPdfConversionService Image-to-PDF support where a domain requires it
PdfProcessingService PDF validation/processing support
RetentionPolicyService Validates policies and calculates/compares retention dates

Documents use a polymorphic owner (entity_type and entity_id) plus metadata. Callers should use stable, namespaced entity types such as Members.ProfilePhoto; do not infer an owner from a filename.

Upload flow

  1. The controller authorizes the owning entity and action.
  2. The domain service validates purpose-specific file type and size constraints.
  3. DocumentService::createDocument() stores the content through the globally configured adapter and saves metadata; the Azure adapter receives tenant-resolved container/prefix configuration.
  4. The domain record is updated to reference the new document.
  5. If a replacement succeeds, the old document is removed through deleteDocument().

If the domain save fails after upload, delete the newly created document to avoid an orphan. Existing member services demonstrate this compensating cleanup.

Serving files

Use the service’s download, inline, thumbnail, and preview responses. Every read must first authorize the owning domain record. Never expose storage paths or issue an unrestricted response based only on a document ID supplied by the client.

Legacy local paths remain in a few migration-compatible read paths. New features must use document IDs and the configured storage backend.

Retention

RetentionPolicyService supports policies anchored to gathering_end_date, upload_date, or permanent. A plugin supplies the relevant dates and persists the calculated retention date with its domain record. Deletion jobs must run inside the correct tenant context and use DocumentService so metadata and stored objects remain consistent.

Multi-tenant invariants

Verification

Test allowed and rejected MIME/extension combinations, upload rollback, authorized and denied reads, range/inline response behavior where relevant, thumbnail/preview fallbacks, retention boundaries, delete failures, and two-tenant isolation. See app/src/Services/DOCUMENT_SERVICE.md for low-level service notes maintained beside the implementation.