Architecture
Archipel is a modular monolith built on NestJS. Each platform library is a self-contained NestJS module that can be composed into any application independently. The libraries enforce strict boundaries between business logic and infrastructure through well-defined contracts.
Key Patterns
Ports & Adapters
The central pattern is Ports & Adapters (Hexagonal Architecture). Platform libraries define abstract ports — contracts you implement — and your application provides concrete adapters — implementations backed by your specific data source, API, or logic.
Abstract classes serve as both the type contract and the NestJS DI token. This eliminates the need for separate Symbol() or string tokens while preserving full type safety.
For implementation details, code examples, and the complete port reference table, see the Implementing Ports guide.
Dynamic Module Registration
Every library exposes a static register() method that returns a DynamicModule. This is where your application provides port implementations and configuration. No module uses global state or hidden defaults — everything is explicit at registration time.
AuthModule.register({
authSubject: PrismaAuthSubjectAdapter,
mfaSubject: PrismaMfaSubjectAdapter,
sessionPersistence: PrismaSessionAdapter,
verificationSubject: PrismaVerificationAdapter,
});Application Bootstrap
platform-bootstrap composes app-level startup concerns as ordered PlatformBootstrapStep functions. The consuming app still creates the Nest application with NestFactory and the selected HTTP adapter, then applies reusable use* helpers for API defaults, middleware, security, sessions, and OpenAPI.
For the reusable rules and examples, see the Bootstrap Pattern and Application Bootstrap guide.
Strategy Pattern
Several libraries support pluggable strategies for different provider implementations:
platform-mailingsupports SMTP, Postmark, Resend, Plunk, and Log delivery strategies behind a commonDeliveryStrategyBase.platform-blob-storagesupports Vercel Blob, Azure Blob Storage, AWS S3, and local filesystem out of the box, plus custom strategies viaBlobStorageStrategyBase.platform-documentssupports pluggable renderers for DOCX, PDF, and image processing.platform-authenticationsupports JWT, Local, Anonymous, and OAuth strategies (GitHub, Google, Microsoft, Apple).platform-paymentssupports Stripe, LemonSqueezy, Paddle, and Mollie behind a commonPaymentClientPort.platform-esigningsupports DocuSign, Adobe Sign, Dropbox Sign, and a built-in internal provider behind a commonEsigningClientPort.platform-analyticssupports Sentry, Azure Application Insights, and Datadog behind a commonAnalyticsClientPort.platform-queuesupports MemoryQueue, BullMQ (Redis), Azure Service Bus, and Vercel Queues behind a commonIQueueinterface.
You select the strategy at module registration time. Each package's documentation page lists the available options.
Mapping Service
platform-mapping provides a centralized MappingService for transforming domain entities into response DTOs. Mapping logic lives in dedicated MappingProfileBase subclasses — not in services or controllers. Mapping keys are branded constants for type-safe, collision-free lookups.
Dependency Graph
flowchart TB
Core[platform-core\nFoundation]
PlatformBootstrap[platform-bootstrap\nApplication Bootstrap]
Configuration[platform-configuration]
Mapping[platform-mapping]
Resources[platform-resources]
Health[platform-health]
Caching[platform-caching\nRedis]
Cryptography[platform-cryptography]
Logging[platform-logging\nSentry]
Telemetry[platform-telemetry\nOTel]
OpenApi[platform-openapi\nSwagger]
Database[platform-database\nPrisma]
BlobStorage[platform-blob-storage]
Queue[platform-queue]
Authentication[platform-authentication]
Payments[platform-payments]
Mailing[platform-mailing]
Documents[platform-documents\nStandalone]
Reporting[platform-reporting\nStandalone]
Mcp[platform-mcp\nStandalone]
Esigning[platform-esigning\nStandalone]
Intelligence[platform-intelligence\nStandalone]
Analytics[platform-analytics\nStandalone]
PlatformBootstrap --> Configuration
PlatformBootstrap --> Core
PlatformBootstrap --> Database
PlatformBootstrap --> OpenApi
PlatformBootstrap --> Resources
Core --> Configuration
Core --> Mapping
Core --> Resources
Core --> Health
Core --> Caching
Core --> Cryptography
Core --> Logging
Core --> Telemetry
Core --> OpenApi
Core --> Database
Core --> BlobStorage
Core --> Queue
Database --> Authentication
Database --> Payments
Database --> Mailing
BlobStorage --> Mailing
Core --> Documents
Core --> Reporting
Core --> Mcp
Core --> Esigning
Core --> Intelligence
Core --> AnalyticsDesign Rules
platform-corehas no Archipel dependencies — it is the foundation that everything else builds on.- Tier 1 libraries (
platform-mapping,platform-resources,platform-health) depend only onplatform-coreand external packages. - No horizontal dependencies — libraries at the same layer never depend on each other.
- Dependencies flow downward — higher-layer libraries depend only on lower layers and core.
- No circular dependencies — enforced structurally, not by convention.
- Ports are abstract classes — serving as both type contract and DI token.
- Adapters live in your application — never inside platform libraries.
- No global state — every module is explicitly configured at registration time.
- No leaking internals — only symbols exported from the package barrel are considered public API.
- Bounded collections — every in-memory collection has a static upper limit to prevent unbounded memory growth.
- Graceful lifecycle — modules implement
OnModuleInitfor eager setup andOnModuleDestroywith timeouts for clean teardown. - Domain errors — each library defines typed error classes that map to HTTP status codes via a global exception filter.
Error Handling
Each platform library defines domain-specific error classes (extending Error or a library base error). These are thrown in services, caught by a global exception filter, and mapped to HTTP Problem-Details responses. Notable error hierarchies:
| Library | Error Classes |
|---|---|
platform-mapping | MappingError, MappingNotRegisteredError, TypeMappingNotRegisteredError |
platform-resources | ResourceNotFoundError |
platform-mailing | MailDeliveryError |
platform-payments | PaymentError |
platform-intelligence | TextGenerationError, ProviderLoaderError, CapabilityRegistryError |
platform-cryptography | CryptoValidationError |
platform-documents | DocumentError, TemplateError, RenderError, ExportError |
platform-esigning | EsigningError, EsigningWebhookError |
platform-reporting | ReportingDatasetNotFoundError |
Lifecycle Management
Libraries implement NestJS lifecycle hooks for deterministic startup and teardown:
| Phase | Hook | Examples |
|---|---|---|
| Eager init | OnModuleInit | MetricsService creates instruments, McpDiscoveryService scans providers |
| Teardown | OnModuleDestroy | AnalyticsClientPort.flush(), OtelSdkHolder.shutdown(), McpServerService closes transports |
Teardown operations use timeout guards (typically 5 seconds) to prevent processes from hanging when external services are unreachable.
Resource Bounds
All in-memory collections enforce static upper limits:
| Library | Component | Limit | Behavior when exceeded |
|---|---|---|---|
platform-core | SseHub event subjects | 10,000 | Throws error |
platform-health | HealthOrchestrator indicators | 100 | Logs warning, skips registration |
platform-reporting | ContributorRegistry | 200 | Logs warning, skips registration |
platform-mcp | McpServerService transports | 1,000 | Bounded tracking |
platform-esigning | InternalEsigningProvider | 10,000 | Throws error |
platform-intelligence | CapabilityRegistry providers | 50 | Throws CapabilityRegistryError |