# Domain Entity System

# Domain Entity System

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

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

- [src/domain/entities/column-pg-io.ts](src/domain/entities/column-pg-io.ts)
- [src/domain/entities/entity-decorators.ts](src/domain/entities/entity-decorators.ts)
- [src/domain/entities/entity-meta.ts](src/domain/entities/entity-meta.ts)
- [src/domain/entities/iauditable_entity.ts](src/domain/entities/iauditable_entity.ts)
- [src/domain/entities/ideletable_entity.ts](src/domain/entities/ideletable_entity.ts)
- [src/domain/entities/iexposable_entity.ts](src/domain/entities/iexposable_entity.ts)
- [src/domain/entities/registry.ts](src/domain/entities/registry.ts)
- [src/index.ts](src/index.ts)

</details>



The Domain Entity System is a decorator-based metadata engine that bridges TypeScript classes and PostgreSQL tables. It utilizes TypeScript's legacy decorator reflection and `reflect-metadata` to define schema properties, constraints, and audit behaviors directly on domain models.

## Core Decorators

The system uses decorators to define how class properties map to database columns. By convention, every enumerable property on a class prototype is treated as a column unless explicitly excluded.

| Decorator | Target | Purpose |
| :--- | :--- | :--- |
| `@Entity(tableName?)` | Class | Maps the class to a PostgreSQL table. Defaults to class name if `tableName` is omitted [src/domain/entities/entity-decorators.ts:161-167](). |
| `@Key(opts?)` | Property | Marks a property as the Primary Key. Supports identity or manual generation [src/domain/entities/entity-decorators.ts:223-231](). |
| `@Column(opts)` | Property | Overrides default mapping (SQL name, type, nullability, length, precision) [src/domain/entities/entity-decorators.ts:192-212](). |
| `@Unique()` | Property | Signals a unique constraint for DDL generation [src/domain/entities/entity-decorators.ts:237-241](). |
| `@IsNotColumn()` | Property | Excludes the property from database snapshots and DAL persistence [src/domain/entities/entity-decorators.ts:246-250](). |
| `@AuditableField(type)` | Property | Identifies metadata fields like `CREATED_AT`, `UPDATED_BY`, or `VERSION` [src/domain/entities/entity-decorators.ts:261-267](). |
| `@DeletableField(type)` | Property | Identifies soft-delete fields like `DELETED_AT` [src/domain/entities/entity-decorators.ts:273-279](). |

### Special Field Types
The system defines enums for specialized tracking fields:
*   **AuditableFieldType**: `CREATED_AT`, `CREATED_BY`, `UPDATED_AT`, `UPDATED_BY`, `VERSION` [src/domain/entities/entity-decorators.ts:26-32]().
*   **DeletableFieldType**: `DELETED_AT`, `DELETED_BY` [src/domain/entities/entity-decorators.ts:34-37]().

Sources: [src/domain/entities/entity-decorators.ts:26-279](), [src/domain/entities/entity-meta.ts:4-31]()

## Metadata Discovery and Mapping

The system uses a `WeakMap` named `META` to store `ClassEntityMeta` against class constructors [src/domain/entities/entity-decorators.ts:102-111]().

### Natural Language to Code Entity Space: Decorator Flow
This diagram shows how a developer's intent (Natural Language) is translated into the `ENTITY_REGISTRY` and metadata structures.

```mermaid
graph TD
    subgraph "Natural Language Intent"
        A["'I want a Customer table with a Primary Key'"]
    end

    subgraph "Code Entity Space"
        B["@Entity('customers')"]
        C["@Key() id: number"]
        D["CustomerEntity class"]
        E["ENTITY_REGISTRY"]
        F["META WeakMap"]
    end

    A --> B
    A --> C
    B --> D
    C --> D
    D -- "Imported into" --> E
    D -- "Stores metadata in" --> F
```
Sources: [src/domain/entities/entity-decorators.ts:102-111](), [src/domain/entities/registry.ts:14-20]()

### Implicit Column Discovery
TypeScript class fields declared with `!` (e.g., `name!: string`) do not exist at runtime unless initialized. To handle this, the system uses `syncImplicitEntityColumns` which:
1.  Instantiates a temporary object to discover enumerable keys [src/domain/entities/entity-decorators.ts:124-130]().
2.  Uses `reflect-metadata` (`design:type`) to infer the PostgreSQL type [src/domain/entities/entity-decorators.ts:146-149]().
3.  Determines nullability based on the TypeScript type [src/domain/entities/entity-decorators.ts:150-153]().

Sources: [src/domain/entities/entity-decorators.ts:124-155]()

## Entity Registry

The `ENTITY_REGISTRY` in `src/domain/entities/registry.ts` is the central source of truth for all domain entities. Database patch tooling and the migration system scan this registry to generate DDL or compare the current database state against the code [src/domain/entities/registry.ts:1-4]().

**Current Registered Entities:**
*   `CustomerEntity` [src/domain/entities/registry.ts:15]()
*   `UserProfileEntity` [src/domain/entities/registry.ts:16]()
*   `RoleMappingEntity` [src/domain/entities/registry.ts:17]()
*   `OrganizationEntity` [src/domain/entities/registry.ts:18]()
*   `ServiceRegistryEntity` [src/domain/entities/registry.ts:19]()

Sources: [src/domain/entities/registry.ts:14-20]()

## Type Mapping (TS ↔ PostgreSQL)

The `column-pg-io.js` utility handles the conversion of data between the application and the database driver.

### Date Handling
*   **Standard**: TS `Date` maps to `timestamptz` [src/domain/entities/column-pg-io.ts:4]().
*   **Explicit Date**: If `@Column({ pgType: 'date' })` is used, the system converts `Date` objects to `YYYY-MM-DD` strings for the driver [src/domain/entities/column-pg-io.ts:47-49]().
*   **Hydration**: After loading data from JSON, `hydrateEntityDateFieldsFromJson` converts ISO strings back into JS `Date` objects based on metadata [src/domain/entities/column-pg-io.ts:76-92]().

### Mapping Logic Diagram
This diagram maps the code-level transformation functions to the data flow between TypeScript and PostgreSQL.

```mermaid
graph LR
    subgraph "TypeScript Space"
        JS_VAL["JS Value (Date/String/Number)"]
        ENTITY_INST["Entity Instance"]
    end

    subgraph "Conversion Logic"
        JS_TO_PG["jsValueToPgParam()"]
        PG_TO_JS["pgValueToJsValue()"]
        HYDRATE["hydrateEntityDateFieldsFromJson()"]
    end

    subgraph "PostgreSQL Space"
        PG_PARAM["node-pg Parameter"]
        WIRE_VAL["Wire Value"]
    end

    JS_VAL -- "Pre-Query" --> JS_TO_PG
    JS_TO_PG --> PG_PARAM
    WIRE_VAL -- "Post-Query" --> PG_TO_JS
    PG_TO_JS --> ENTITY_INST
    ENTITY_INST -- "From JSON/API" --> HYDRATE
    HYDRATE --> JS_VAL
```
Sources: [src/domain/entities/column-pg-io.ts:43-69](), [src/domain/entities/column-pg-io.ts:76-92]()

## Common Interfaces

Entities often implement standard interfaces to ensure compatibility with generic DAL features like soft-delete and audit tracking.

*   **`IExposableEntity`**: Requires a `uuid` field for public API exposure [src/domain/entities/iexposable_entity.ts:1-7]().
*   **`IDeletableEntity`**: Includes `deleted_at` and `deleted_by` for soft-delete support [src/domain/entities/ideletable_entity.ts:1-4]().
*   **`IAuditableEntity`**: Extends `IDeletableEntity` and adds `created_at`, `created_by`, `updated_at`, `updated_by`, and `version` (for optimistic concurrency) [src/domain/entities/iauditable_entity.ts:3-9]().

Sources: [src/domain/entities/iexposable_entity.ts:1-7](), [src/domain/entities/ideletable_entity.ts:1-4](), [src/domain/entities/iauditable_entity.ts:1-9]()

---
