# How Modules and Components Are Organized in Openship: Monorepo Architecture Deep Dive

> Explore the Openship monorepo architecture. Discover how modules and components are organized within self-contained packages under packages/ and the factory pattern used for adapter wiring.

- Repository: [oblien/openship](https://github.com/oblien/openship)
- Tags: internals
- Published: 2026-07-29

---

**Openship organizes its modules as a TypeScript monorepo with self-contained packages under `packages/`, using a factory pattern in `@repo/adapters` to wire runtime, routing, SSL, and system adapters based on the deployment target.**

The oblien/openship repository implements a modular architecture designed to support cloud, self-hosted, and desktop deployments from a single codebase. Understanding how modules or components are organized in openship reveals a clean separation of concerns through domain-driven packages and a dynamic adapter system. The architecture leverages a Platform factory pattern that selects concrete implementations at initialization time, allowing the core business logic to remain agnostic of infrastructure details.

## Monorepo Layout and Package Structure

The repository follows a monorepo pattern with two primary directories: `packages/` for shared libraries and `apps/` for deployable applications. Each package represents a logical layer of the platform and exposes its public API via a [`package.json`](https://github.com/oblien/openship/blob/main/package.json) file, enabling tree-shaking at build time so only required code reaches the final bundle.

The top-level organization includes:

- **`packages/adapters`** – The abstraction layer containing runtime, infrastructure, and system adapters.
- **`packages/core`** – Generic utilities and helper functions used across the system.
- **`packages/ui`** – React component library for the web dashboard.
- **`packages/onboarding`** – CLI onboarding flow and validation logic.
- **`packages/db`** – Drizzle ORM schema definitions for all persisted entities.
- **`packages/db-email`** – SMTP email client wrapper.
- **`apps/web`** – The Next.js web dashboard application (configured via [`apps/web/tsconfig.json`](https://github.com/oblien/openship/blob/main/apps/web/tsconfig.json)).

## The Three Core Architectural Layers

The codebase revolves around three fundamental concepts that define how components interact.

### 1. Adapters (`@repo/adapters`)

The adapters package provides the abstraction layer that glues the API to underlying runtime environments. Located in `packages/adapters/`, this is the most critical module for understanding how openship handles different deployment targets. The relationship between layers is documented in [`packages/adapters/docs/ARCHITECTURE.md`](https://github.com/oblien/openship/blob/main/packages/adapters/docs/ARCHITECTURE.md), which illustrates how targets, layers, and wiring interact.

Key files include:

- [`packages/adapters/src/platform.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/platform.ts) – The Platform factory and singleton implementation.
- [`packages/adapters/src/runtime/docker.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/docker.ts) – Docker-based runtime implementation using `dockerode`.
- [`packages/adapters/src/runtime/bare.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/bare.ts) – Bare-metal runtime for SSH or local execution.
- [`packages/adapters/src/infra/nginx.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/infra/nginx.ts) – Nginx/Traefik routing and SSL provider.
- [`packages/adapters/src/infra/cloud.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/infra/cloud.ts) – Cloud provider for Oblien SaaS integration.
- [`packages/adapters/src/infra/noop.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/infra/noop.ts) – No-op stub for desktop targets.
- [`packages/adapters/src/system/setup.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/system/setup.ts) – System checks and installer logic.

### 2. Core (`@repo/core`)

The core package contains shared utilities that have no external dependencies on infrastructure. These helpers support the rest of the system with functions like TOML parsing and slug formatting.

Important files:

- [`packages/core/src/utils-format-slug.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/utils-format-slug.ts) – String formatting utilities.
- [`packages/core/src/toml-helpers.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/toml-helpers.ts) – TOML configuration parsing.

### 3. Domain-Specific Packages

These modules handle specific business concerns:

- **`packages/ui`** – Reusable React components such as [`packages/ui/src/components/button.tsx`](https://github.com/oblien/openship/blob/main/packages/ui/src/components/button.tsx) and [`packages/ui/src/components/card.tsx`](https://github.com/oblien/openship/blob/main/packages/ui/src/components/card.tsx).
- **`packages/onboarding`** – CLI initialization logic in [`packages/onboarding/src/index.ts`](https://github.com/oblien/openship/blob/main/packages/onboarding/src/index.ts) and API client setup in [`packages/onboarding/src/api-client.ts`](https://github.com/oblien/openship/blob/main/packages/onboarding/src/api-client.ts).
- **`packages/db`** – Database schema definitions including [`packages/db/src/schema/project.ts`](https://github.com/oblien/openship/blob/main/packages/db/src/schema/project.ts) and [`packages/db/src/schema/service.ts`](https://github.com/oblien/openship/blob/main/packages/db/src/schema/service.ts).
- **`packages/db-email`** – Email delivery client in [`packages/db-email/src/client.ts`](https://github.com/oblien/openship/blob/main/packages/db-email/src/client.ts).

## The Platform Factory Pattern

The heart of openship's module organization is the **Platform factory** pattern implemented in [`packages/adapters/src/platform.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/platform.ts). This factory creates a singleton Platform object that wires together concrete implementations based on the deployment configuration.

### Initialization Flow

At server startup, the CLI or API calls `initPlatform(config)`, which reads the deployment target (`cloud`, `selfhosted`, or `desktop`) and constructs a Platform object with four key properties:

```typescript
{
  runtime: RuntimeAdapter,    // Docker or Bare metal
  routing: RoutingProvider,   // Nginx, Cloud, or Noop
  ssl: SslProvider,          // Certificate management
  system: SystemManager | null,  // Host setup and validation
  executor: CommandExecutor | null // Local or SSH command execution
}

```

The Platform is cached via `getPlatform()` and injected throughout the service layer, ensuring consistent access to infrastructure capabilities.

### Adapter Selection Logic

The factory selects implementations based on the `target` and `runtime` configuration values:

- **Runtime adapters**: `DockerRuntime` ([`packages/adapters/src/runtime/docker.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/docker.ts)) handles container operations via Dockerode, while `BareRuntime` ([`packages/adapters/src/runtime/bare.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/bare.ts)) executes shell commands locally or over SSH.
- **Infra adapters**: `NginxProvider` ([`packages/adapters/src/infra/nginx.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/infra/nginx.ts)) manages reverse proxy configuration and ACME certificate issuance. `CloudInfraProvider` ([`packages/adapters/src/infra/cloud.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/infra/cloud.ts)) communicates with the Oblien SaaS API. `NoopInfraProvider` ([`packages/adapters/src/infra/noop.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/infra/noop.ts)) provides stubs for desktop environments where routing is unnecessary.
- **System manager**: `SystemManager` ([`packages/adapters/src/system/setup.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/system/setup.ts)) validates prerequisites like Docker, Git, and Node installations, installing missing components when needed.

## Cross-Module Communication

Modules communicate through dependency injection and shared interfaces rather than direct imports of concrete classes.

The **CommandExecutor** abstraction demonstrates this pattern. Depending on configuration, the factory injects either a `LocalExecutor` (using `child_process`) or an `SshExecutor` (via `ssh2`) into both runtime and infrastructure adapters. This ensures a single connection to the target host and consistent error handling across all operations.

```typescript
import { initPlatform } from '@repo/adapters';

// Configuration for self-hosted Docker deployment
const cfg = {
  target: 'selfhosted',
  runtime: 'docker',
  docker: { socketPath: '/var/run/docker.sock' },
  nginx: { certEmail: 'admin@example.com' },
  ssh: undefined,  // local execution
};

async function start() {
  // Initialize the singleton Platform
  const platform = await initPlatform(cfg);
  
  // Access wired components
  const { runtime, routing, ssl, system } = platform;
  
  // Execute platform operations
  await runtime.build({ name: 'my-app' }, console.log);
  await routing.registerRoute({ 
    domain: 'app.example.com', 
    targetUrl: 'http://localhost:3000' 
  });
  await ssl.provisionCert('app.example.com');
  
  if (system) await system.requireFeature('deploy');
}

```

This code resolves to concrete implementations chosen by the factory, demonstrating how the same business logic operates across Docker, bare-metal, cloud, and desktop targets without modification.

## Summary

- **Openship uses a monorepo structure** with self-contained packages under `packages/` and applications under `apps/`.
- **The `@repo/adapters` package** implements a factory pattern that wires runtime, routing, SSL, and system components based on deployment targets.
- **Platform initialization** occurs via `initPlatform()` in [`packages/adapters/src/platform.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/platform.ts), which creates a singleton with concrete adapter implementations.
- **Runtime adapters** include Docker and bare-metal variants, while infrastructure adapters handle Nginx, cloud, or no-op routing.
- **Domain packages** (`ui`, `db`, `onboarding`, `core`) provide specific business functionality without infrastructure dependencies.
- **CommandExecutor abstraction** ensures consistent local or remote execution across all modules.

## Frequently Asked Questions

### What is the purpose of the Platform factory in openship?

The Platform factory in [`packages/adapters/src/platform.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/platform.ts) serves as the central wiring mechanism that selects and configures concrete implementations of runtime, routing, SSL, and system adapters based on the deployment target. It creates a singleton Platform object cached via `getPlatform()`, allowing the same business logic to run on cloud servers, self-hosted Docker hosts, bare-metal SSH machines, or desktop environments without code changes.

### How does openship handle different runtime environments?

Openship handles different runtime environments through the adapter pattern implemented in the `@repo/adapters` package. The factory instantiates either `DockerRuntime` (from [`packages/adapters/src/runtime/docker.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/docker.ts)) for containerized deployments or `BareRuntime` (from [`packages/adapters/src/runtime/bare.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/bare.ts)) for direct shell execution. Both implement the same `RuntimeAdapter` interface, ensuring consistent build and deployment operations regardless of the underlying infrastructure.

### What distinguishes the Core package from domain-specific packages?

The `packages/core` module contains generic, infrastructure-agnostic utilities like TOML helpers ([`packages/core/src/toml-helpers.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/toml-helpers.ts)) and slug formatters ([`packages/core/src/utils-format-slug.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/utils-format-slug.ts)) used across the entire system. Domain-specific packages such as `packages/ui`, `packages/db`, and `packages/onboarding` handle specific business concerns with potential external dependencies. Core utilities never interact with Docker, SSH, or database connections directly, maintaining a clean boundary between pure logic and infrastructure code.

### Can I add custom adapters to the openship platform?

Yes, you can extend openship by creating new adapter implementations that conform to the existing interfaces defined in `packages/adapters`. To add a custom runtime, implement the `RuntimeAdapter` interface and register it in the factory at [`packages/adapters/src/platform.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/platform.ts). Similarly, custom routing providers can extend the infrastructure abstraction by following the patterns in [`packages/adapters/src/infra/nginx.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/infra/nginx.ts) or [`packages/adapters/src/infra/cloud.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/infra/cloud.ts), then wiring them into the Platform initialization logic.