Skip to content

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.

typescript
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-mailing supports SMTP, Postmark, Resend, Plunk, and Log delivery strategies behind a common DeliveryStrategyBase.
  • platform-blob-storage supports Vercel Blob, Azure Blob Storage, AWS S3, and local filesystem out of the box, plus custom strategies via BlobStorageStrategyBase.
  • platform-documents supports pluggable renderers for DOCX, PDF, and image processing.
  • platform-authentication supports JWT, Local, Anonymous, and OAuth strategies (GitHub, Google, Microsoft, Apple).
  • platform-payments supports Stripe, LemonSqueezy, Paddle, and Mollie behind a common PaymentClientPort.
  • platform-esigning supports DocuSign, Adobe Sign, Dropbox Sign, and a built-in internal provider behind a common EsigningClientPort.
  • platform-analytics supports Sentry, Azure Application Insights, and Datadog behind a common AnalyticsClientPort.
  • platform-queue supports MemoryQueue, BullMQ (Redis), Azure Service Bus, and Vercel Queues behind a common IQueue interface.

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

mermaid
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 --> Analytics

Design Rules

  1. platform-core has no Archipel dependencies — it is the foundation that everything else builds on.
  2. Tier 1 libraries (platform-mapping, platform-resources, platform-health) depend only on platform-core and external packages.
  3. No horizontal dependencies — libraries at the same layer never depend on each other.
  4. Dependencies flow downward — higher-layer libraries depend only on lower layers and core.
  5. No circular dependencies — enforced structurally, not by convention.
  6. Ports are abstract classes — serving as both type contract and DI token.
  7. Adapters live in your application — never inside platform libraries.
  8. No global state — every module is explicitly configured at registration time.
  9. No leaking internals — only symbols exported from the package barrel are considered public API.
  10. Bounded collections — every in-memory collection has a static upper limit to prevent unbounded memory growth.
  11. Graceful lifecycle — modules implement OnModuleInit for eager setup and OnModuleDestroy with timeouts for clean teardown.
  12. 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:

LibraryError Classes
platform-mappingMappingError, MappingNotRegisteredError, TypeMappingNotRegisteredError
platform-resourcesResourceNotFoundError
platform-mailingMailDeliveryError
platform-paymentsPaymentError
platform-intelligenceTextGenerationError, ProviderLoaderError, CapabilityRegistryError
platform-cryptographyCryptoValidationError
platform-documentsDocumentError, TemplateError, RenderError, ExportError
platform-esigningEsigningError, EsigningWebhookError
platform-reportingReportingDatasetNotFoundError

Lifecycle Management

Libraries implement NestJS lifecycle hooks for deterministic startup and teardown:

PhaseHookExamples
Eager initOnModuleInitMetricsService creates instruments, McpDiscoveryService scans providers
TeardownOnModuleDestroyAnalyticsClientPort.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:

LibraryComponentLimitBehavior when exceeded
platform-coreSseHub event subjects10,000Throws error
platform-healthHealthOrchestrator indicators100Logs warning, skips registration
platform-reportingContributorRegistry200Logs warning, skips registration
platform-mcpMcpServerService transports1,000Bounded tracking
platform-esigningInternalEsigningProvider10,000Throws error
platform-intelligenceCapabilityRegistry providers50Throws CapabilityRegistryError

Released under the MIT License.