# Architecture: Ports & Adapters

# Architecture: Ports & Adapters

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [README.md](README.md)
- [src/ports/config-repository-port.ts](src/ports/config-repository-port.ts)
- [src/ports/database-port.ts](src/ports/database-port.ts)
- [src/ports/health-check-port.ts](src/ports/health-check-port.ts)
- [src/ports/service-registry-port.ts](src/ports/service-registry-port.ts)

</details>



The `@primebrick/sdk` is built on the **Hexagonal Architecture** (also known as Ports & Adapters) pattern. The primary goal of this design is to make the SDK entirely **database-agnostic** and decoupled from specific Data Access Layer (DAL) implementations [README.md:3-7]().

By defining abstract "Ports" (interfaces), the SDK core logic remains pure and testable, while the consuming microservice is responsible for providing "Adapters" (concrete implementations) that bridge the SDK to specific infrastructure like PostgreSQL, MSSQL, or MariaDB [src/ports/database-port.ts:4-6]().

## Dependency Inversion Principle

The SDK applies the Dependency Inversion Principle (DIP) to ensure that high-level modules (like the Migration Runner or Config Loader) do not depend on low-level modules (like a specific database driver). Instead, both depend on abstractions [src/ports/config-repository-port.ts:4-7]().

### Data Flow: SDK to Infrastructure
The following diagram illustrates how the SDK interacts with external systems through ports.

**Entity Mapping: SDK Ports to Infrastructure**
```mermaid
graph LR
    subgraph "SDK Core (Natural Language Space)"
        CL["ConfigLoader"]
        MR["Migration Runner"]
        SR["ServiceRegistrar"]
        HC["HealthCheck"]
    end

    subgraph "Port Interfaces (Code Entity Space)"
        CRP["ConfigRepositoryPort"]
        DP["DatabasePort"]
        SRP["ServiceRegistryPort"]
        HCP["HealthCheckPort"]
    end

    subgraph "Consumer Adapters (Implementation Space)"
        PG_CRP["PostgresConfigAdapter"]
        PG_DP["PostgresQueryAdapter"]
        PG_SRP["PostgresRegistryAdapter"]
        PG_HCP["PostgresPingAdapter"]
    end

    CL --> CRP
    MR --> DP
    SR --> SRP
    HC --> HCP

    CRP -.-> PG_CRP
    DP -.-> PG_DP
    SRP -.-> PG_SRP
    HCP -.-> PG_HCP
```
**Sources:** [src/ports/config-repository-port.ts:8-14](), [src/ports/database-port.ts:11-17](), [src/ports/health-check-port.ts:8-11](), [src/ports/service-registry-port.ts:10-19]()

---

## The Four Port Interfaces

The SDK defines four primary ports that must be implemented by the consuming application to enable full functionality.

### 1. ConfigRepositoryPort
Used by the `ConfigLoader` to fetch application settings from a persistent store [src/ports/config-repository-port.ts:4-7]().

| Method | Description |
| :--- | :--- |
| `findAll()` | Returns all configuration rows as an array of `{ key, value }` pairs. Value is `null` if the key exists without a value [src/ports/config-repository-port.ts:13-13](). |

**Sources:** [src/ports/config-repository-port.ts:8-14]()

### 2. DatabasePort
A low-level interface for executing parameterized SQL. This is the backbone of the SDK's migration runner (`applyPatches`) [src/ports/database-port.ts:4-6]().

| Method | Description |
| :--- | :--- |
| `query<T>(text, params?)` | Executes a SQL statement. Returns an object containing a `rows` array. Used for transaction control (`BEGIN`, `COMMIT`) and patch execution [src/ports/database-port.ts:13-16](). |

**Sources:** [src/ports/database-port.ts:11-17]()

### 3. ServiceRegistryPort
Used by the `ServiceRegistrar` to manage the lifecycle and heartbeat of the microservice within a centralized registry table [src/ports/service-registry-port.ts:4-9]().

| Method | Description |
| :--- | :--- |
| `findByCode(code)` | Retrieves a service record by its unique code [src/ports/service-registry-port.ts:12-12](). |
| `insert(row)` | Adds a new service entry to the registry [src/ports/service-registry-port.ts:15-15](). |
| `updateByCode(code, row)` | Updates an existing service record (e.g., updating the heartbeat timestamp) [src/ports/service-registry-port.ts:18-18](). |

**Sources:** [src/ports/service-registry-port.ts:10-19]()

### 4. HealthCheckPort
A specialized port for connectivity pings. It allows the SDK's `HealthCheck` utility to verify database availability without knowing the specific SQL dialect [src/ports/health-check-port.ts:4-7]().

| Method | Description |
| :--- | :--- |
| `ping()` | Returns a boolean indicating if the database is reachable (e.g., by running `SELECT 1`) [src/ports/health-check-port.ts:9-10](). |

**Sources:** [src/ports/health-check-port.ts:8-11]()

---

## Implementing Adapters

Consumers write adapter implementations by wrapping their chosen database driver or Data Access Layer.

### Implementation Pattern
The consumer provides these implementations at startup, injecting them into the SDK's functional modules.

**Code Entity Relationship: Adapter Implementation**
```mermaid
classDiagram
    class DatabasePort {
        <<interface>>
        +query(text, params)
    }
    class PostgresAdapter {
        -pool: pg.Pool
        +query(text, params)
    }
    class MigrationRunner {
        +applyPatches(dbPort: DatabasePort)
    }

    DatabasePort <|.. PostgresAdapter : Implements
    MigrationRunner --> DatabasePort : Depends on
```
**Sources:** [src/ports/database-port.ts:1-17](), [README.md:7-7]()

### Key Requirements for Adapters:
1. **Error Handling**: Adapters should catch driver-specific errors and allow them to bubble up or wrap them in a way the SDK can log.
2. **Type Safety**: The `DatabasePort` uses generics (`<T = unknown>`) to allow consumers to maintain type safety when fetching rows [src/ports/database-port.ts:16-16]().
3. **Statelessness**: Ports are generally designed to be stateless, acting as a pass-through to the underlying connection pool or client [src/ports/database-port.ts:8-10]().

**Sources:** [src/ports/database-port.ts:1-17](), [src/ports/config-repository-port.ts:1-14](), [src/ports/service-registry-port.ts:1-19](), [src/ports/health-check-port.ts:1-11]()

---
