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,
});

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 pluggable blob providers via IBlobProvider.
  • 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 Creem behind a common PaymentClientPort.

You select the strategy at module registration time. Each package's documentation page lists the available options.

Mapping Service

platform-core 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

                    ┌──────────────────┐
                    │  platform-core   │  (Foundation)
                    └────────┬─────────┘

        ┌────────────────────┼────────────────────────────┐
        │                    │                             │
        ▼                    ▼                             ▼
┌───────────────┐  ┌─────────────────┐  ┌──────────────────────┐
│ platform-     │  │ platform-       │  │ platform-            │
│ logging       │  │ openapi         │  │ telemetry            │
│ (Sentry)      │  │ (Swagger)       │  │ (OpenTelemetry)      │
└───────────────┘  └────────┬────────┘  └──────────────────────┘


                   ┌─────────────────┐
                   │ platform-       │
                   │ database        │
                   │ (Prisma)        │
                   └────────┬────────┘

        ┌───────────────────┼────────────────┐
        │                   │                │
        ▼                   ▼                ▼
┌───────────────┐  ┌─────────────────┐  ┌───────────────┐
│ platform-     │  │ platform-       │  │ platform-     │
│ blob-storage  │  │ authentication  │  │ payments      │
└───────┬───────┘  └─────────────────┘  └───────────────┘


┌───────────────┐
│ platform-     │
│ mailing       │
└───────────────┘

Standalone (core only):
┌───────────────┐  ┌───────────────┐  ┌───────────────┐
│ platform-     │  │ platform-     │  │ platform-     │
│ documents     │  │ reporting     │  │ mcp           │
└───────────────┘  └───────────────┘  └───────────────┘

Design Rules

  1. platform-core has no Archipel dependencies — it is the foundation that everything else builds on.
  2. No horizontal dependencies — libraries at the same layer never depend on each other.
  3. Dependencies flow downward — higher-layer libraries depend only on lower layers and core.
  4. No circular dependencies — enforced structurally, not by convention.
  5. Ports are abstract classes — serving as both type contract and DI token.
  6. Adapters live in your application — never inside platform libraries.
  7. No global state — every module is explicitly configured at registration time.
  8. No leaking internals — only symbols exported from the package barrel are considered public API.

Released under the MIT License.