# Configuration Management

# Configuration Management

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

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

- [src/config/__tests__/config-loader.test.ts](src/config/__tests__/config-loader.test.ts)
- [src/config/config-loader.ts](src/config/config-loader.ts)
- [src/config/iconfig-entity.ts](src/config/iconfig-entity.ts)
- [src/ports/config-repository-port.ts](src/ports/config-repository-port.ts)

</details>



The `Config` module provides a standardized, dictionary-style configuration system designed to be shared across all microservices. It implements a **Load-Cache-Get** pattern that ensures high performance by avoiding database hits on the hot path while maintaining a DB-agnostic architecture through the use of ports.

## Data Shape: IConfigEntity

Configuration data is modeled as a simple key-value pair system. Each configuration entry is represented by the `IConfigEntity` interface, which mirrors a standard configuration table structure where every row represents a single setting.

| Property | Type | Description |
| :--- | :--- | :--- |
| `key` | `string` | The unique identifier for the setting (e.g., `"brevo_api_key"`). |
| `value` | `string \| null` | The raw text value. `null` indicates the key exists but is not yet set. |
| `label_key` | `string` (optional) | i18n key for UI display titles. |
| `description_key` | `string` (optional) | i18n key for UI display descriptions. |

Sources: [src/config/iconfig-entity.ts:10-19]()

## The ConfigRepositoryPort Contract

To maintain the SDK's database-agnostic philosophy, the `ConfigLoader` does not interact with a database directly. Instead, it depends on the `ConfigRepositoryPort`. Consuming microservices must provide an adapter that implements this interface using their specific Data Access Layer (DAL) or raw SQL.

```mermaid
classDiagram
    class ConfigRepositoryPort {
        <<interface>>
        +findAll() Promise~Array~
    }
    class ConfigLoader {
        -repo: ConfigRepositoryPort
        -cache: Map~string, string | null~
        +load() Promise
        +get(key) string
    }
    ConfigLoader --> ConfigRepositoryPort : uses
```

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

## ConfigLoader Lifecycle

The `ConfigLoader` manages an in-memory cache to ensure that once configuration is loaded, access is instantaneous.

### 1. Load & Cache
The `load()` method must be called once at application startup [src/config/config-loader.ts:24-31](). It fetches all rows via the `ConfigRepositoryPort.findAll()` method and populates an internal `Map` [src/config/config-loader.ts:25-29]().

### 2. Access (Get)
Once loaded, values are retrieved from the `Map`. If `get()` or `require()` is called before `load()`, the loader throws an error to prevent inconsistent application states [src/config/config-loader.ts:38-40]().

### 3. Invalidation
The `invalidate()` method clears the internal cache [src/config/config-loader.ts:86-88](). This forces the next call to `load()` to perform a fresh fetch from the underlying data source [src/config/config-loader.ts:13-13]().

### Data Flow Diagram

```mermaid
sequenceDiagram
    participant App as "Microservice Entry"
    participant CL as "ConfigLoader"
    participant Port as "ConfigRepositoryPort (Adapter)"
    participant DB as "Database (Config Table)"

    App->>CL: load()
    CL->>Port: findAll()
    Port->>DB: SELECT key, value FROM config
    DB-->>Port: rows[]
    Port-->>CL: Array<{key, value}>
    CL->>CL: Populate Map (Cache)
    
    Note over App, CL: Application Hot Path
    App->>CL: get("port")
    CL-->>App: "8080" (from Cache)

    Note over App, CL: Cache Invalidation
    App->>CL: invalidate()
    CL->>CL: cache = null
    App->>CL: load()
    CL->>Port: findAll() (Refreshes Cache)
```

Sources: [src/config/config-loader.ts:15-89](), [src/config/__tests__/config-loader.test.ts:22-81]()

## Typed Accessors and Error Handling

Since the database stores all values as `TEXT` [src/config/iconfig-entity.ts:13-13](), the `ConfigLoader` provides utilities for type conversion and mandatory field validation.

### Accessor Methods

| Method | Return Type | Behavior on Missing/Null |
| :--- | :--- | :--- |
| `get(key)` | `string \| null` | Returns `null`. [src/config/config-loader.ts:37-42]() |
| `require(key)` | `string` | **Throws Error**. [src/config/config-loader.ts:47-53]() |
| `getTyped(key, conv)` | `T \| null` | Returns `null`. [src/config/config-loader.ts:59-63]() |
| `requireTyped(key, conv)` | `T` | **Throws Error**. [src/config/config-loader.ts:68-71]() |

### Type Conversion
The `getTyped` and `requireTyped` methods accept a converter function (e.g., `Number`, `Boolean`, or a custom parser) to transform the raw string into the desired runtime type [src/config/config-loader.ts:59-71]().

### Error Handling
The loader enforces strict presence for "required" keys. If `require()` or `requireTyped()` encounters a `null` or empty string, it throws an error: `Missing required config key: <key>` [src/config/config-loader.ts:49-51]().

Sources: [src/config/config-loader.ts:47-71](), [src/config/__tests__/config-loader.test.ts:42-59]()

---
