# **DSP-DME (Domain Model Engine) Technical Specification — Rev. 1.1.1**

**Document ID:** DSP-DME-Rev1.1.1
**Maintainer:** Fisher Tsau
**Classification:** Core Component / Validation Layer
**Status:** Stable Draft

---

## 1 . 模組概述

**Domain Model Engine (DME)** 是 DSP（Domain Service Platform）核心模組之一，屬於 **Validation & Data Construction Layer**。
它負責以 Schema 為中心驅動的模型驗證、預設值填補、計算欄位生成、生命週期 hook 管理與投影輸出。
DME 是 DSP 內部所有 Domain Model 的統一建構核心，確保資料一致性與驗證流程在多模組之間共享。

---

## 2 . 模組定位與關聯

```
┌──────────────────────────────────────────────┐
│                  DSP Core                    │
├──────────────────────────────────────────────┤
│ ValidatorV2 (Schema 驅動驗證器)               │
│ └─ SchemaEngine → 規則與預設                 │
│ DomainModelEngine (DME) ← createDME({SchemaEngine}) │
│ └─ Central Schema Registry (by-ref, lazy)    │
│ Repo Layer / Service Layer / EventBus 等     │
└──────────────────────────────────────────────┘
```

---

## 3 . 設計目標

1. **Schema 驅動** — 以 Schema 定義 type、default、rule。
2. **Functional Core** — 模型不可變，透過重建更新。
3. **Monad 封裝** — 所有 結果 以 `Ok/Err` 結構 回傳。
4. **Hook / Plugin 可插拔** — 支援多階段 extensible 流程。
5. **Projection 導向** — 支援 DTO、View、Report 輸出。
6. **SchemaEngine 整合** — 集中 Validator 建構。
7. **By-Ref Schema Registry** — 僅存 schema 參考，避免 clone。
8. **Dependency Injection 友善** — 支援注入 validator fac。
9. **Monad Composability** — 結果可用 `map`、`flatMap`、`unwrapOr` 鏈接。

---

## 4 . 模組入口： `createDME`


```js
createDME({ SchemaEngine } = {}) → DME instance
```

• SchemaEngine：提供 validatorFac(schema)。
• Monad：由全域預設或模組內建提供，無需透過工廠參數注入。

---

## 5 . DME 介面定義（含 Monad 回傳）

| 編號     | 函式                                           | 說明                              | 回傳                                      |
| ------ | -------------------------------------------- | ------------------------------- | --------------------------------------- |
| DME-01 | **doRegistry(registry, deps?={validatorFac})**               | 登記 schema (by-ref + deepFreeze) | `Monad.Ok({schemaId}) / Monad.Err(err)` |
| DME-02 | **ensureValidator({schemaId}, opts?)**         | 惰性建立 validator                  | `Monad.Ok(validator) / Monad.Err(err)`  |
|  DME-03 | ** build({schemaId})(input)**               | 正規化→驗證→建模→hook 流程               | `Monad.Ok(model) / Monad.Err(error)`    |
|  DME-04 | **patch({schemaId})({model, delta})**          | merge 後重建                       | 同上                                      |
|  DME-05 | **validateOnly({schemaId})(input)**          | 僅驗證不建模                          | 同上                                      |
| DME-06 | **project({schemaId})(model, name})**         | 依 projection 投影                 | 同上                                      |
| DME-07 | **set / setMany**                            | 重建更新                            | 同上                                      |
| DME-08 | **toJSON({schemaId})(obj, opts?)**             | 序列化                             | `Object`                                |
| DME-09 | **materialize({schemaId}(fn)**           | computed 靜態化  ,用於產生 **無 getter 的 DTO 版本**                  | `Object`                                |
| DME-10 | **useNormalizer({schemaId})(fn)**              | 註冊 normalizer                   | `void`                                  |
| DME-11 | **useComputed({schemaId})(key, fn)**           | 註冊 computed                     | `void`                                  |
| DME-12 | **useProjection({schemaId})(name, fn)**        | 註冊 projection                   | `void`                                  |
| DME-13 | **useHook / usePlugin({schemaId})(phase, fn)** | 註冊 hook                         | `void`                                  |
| DME-14 | **dispose({schemaId}) / disposeAll()**         | 釋放 registry                     | `Monad.Ok(true)`                        |


### Setter、Computed 與 Normalizer 區別

| 類型             | 定義位置                     | 行為              | 是否修改模型     |
| -------------- | ------------------------ | --------------- | ---------- |
| **Setter**     | Factory API（set/setMany） | 以新值重建模型         | ✅ 是（重建）    |
| **Computed**   | factory.computeds        | 以 getter 形式動態計算 | ❌ 否（唯讀）    |
| **Normalizer** | factory.normalizers      | 驗證前輸入清理或轉換      | ✅ 是（但於建構前） |

* Setter 不應放入 schema，而應由 factory 控制，以保持 schema 的純宣告性。



---

## 6 . Monad 結構定義

```js
const Ok = (value, extra = {}) => ({
  isOk: true,
  isErr: false,
  value,
  ...extra,
  map: (f) => Ok(f(value), extra),
  flatMap: (f) => f(value),
  unwrap: () => value,
  unwrapOr: (_) => value,
});

const Err = (error, extra = {}) => ({
  isOk: false,
  isErr: true,
  error,
  ...extra,
  map: (_) => Err(error, extra),
  flatMap: (_) => Err(error, extra),
  unwrap: () => {
    throw error;
  },
  unwrapOr: (defaultValue) => defaultValue,
});
```

**說明：**

* `Ok` 與 `Err` 皆為可鏈接的結果封裝。
* `map()` 用於純函式轉換；`flatMap()` 用於鏈式傳遞。
* `unwrap()` 直接取值（`Err` 會拋例外），`unwrapOr()` 提供安全預設值。
* 額外欄位（`extra`）可帶入 `ctx`、`traceId` 或 meta 資訊，確保上下文可追蹤。


## 7 . Hooks / Plugins 階段

| 階段             | 說明                  | 入參            |
| -------------- | ------------------- | ------------- |
| beforeValidate | 驗證前輸入預處理（補欄位、轉型）    | (raw, ctx)    |
| afterValidate  | 驗證後調整結果（修正預設值、過濾）   | (res, ctx)    |
| beforeBuild    | 模型實例化前最後加工          | (values, ctx) |
| afterBuild     | 模型生成後（可注入 metadata） | (model, ctx)  |
| beforeProject  | 投影前處理               | (model, ctx)  |
| afterProject   | 投影後處理               | (view, ctx)   |
| onError        | 捕捉錯誤與異常             | (error, ctx)  |

- 回傳為Monad (Err, Ok)
- 回傳 值 若為 `Err` 則中斷 後續流程。

---

## 8 . Registry Policy 

* ID 與 版本一致性（`type@version`）。
* 版本 須符 SemVer。
* 禁止重複 id。
* Schema 凍結。
* 選配 hash 檢查。
* DI 優先序：`callSite > registry.deps > global SchemaEngine`。
* 錯誤碼：`REGISTRY_INVALID_*`、`REGISTRY_DUPLICATE_ID`、`SCHEMA_NOT_REGISTERED`、`VALIDATOR_FACTORY_MISSING` 等。

---

## 9 . 效能策略

* `deepFreeze` 僅註冊時執行一次。
* `validator` 惰性生成，結果 緩存於 Map。
* Monad 鏈式運算為純函式，零副作用。

---

## 10 . 使用範例

```js

import createDME from '@dsp/dme';
import SchemaEngine from '@dsp/validator-v2';

const DME = createDME({ SchemaEngine });

DME.doRegistry({
  version: '1.1.0',
  id: 'product@1.1.0',
  type: 'product',
  schema: {
    title: { rules: 'type:string|required' },
    price: { rules: 'type:number|required' },
  },
});

DME.useComputed('product@1.1.0', 'priceWithTax', (m) => m.price * 1.1);

const res = DME.build({ schemaId: 'product@1.1.0', input: { title: 'AC', price: 1000 } })
  .flatMap((model) => DME.project({ schemaId: 'product@1.1.0', model, name: 'card' }))
  .map((view) => DME.toJSON('product@1.1.0')(view, { includeComputed: true }));

if (res.isOk) {
  console.log(res.value);
} else {
  console.error(res.error);
}
```

---

## 11 . 錯誤處理範例

```json
{
 "ok": false,
 "err": {
  "code": "VALIDATION_FAILED",
  "errors": { "price": ["must be >= 0"] }
 }
}
```

---

## 12 . 相容性與升級

* 完全相容 Rev 1.1.0 ，只新增 Monad 封裝層。
* 既有呼叫若仍使用 `{ok,data}` 結構可平滑過渡。

---

## 13 . 測試建議

* 驗證 `Monad.Ok/Err` 鏈式操作正確性。
* 模擬 hook 中 return Err 應 停止 流程。
* 測 快取與 效能。

---

## 14 . 文件資訊

| 欄位           | 值                        |
| ------------ | ------------------------ |
| Document ID  | DSP-DME-Rev1.1.1         |
| Maintainer   | Fisher Tsau              |
| Status       | Stable Draft             |
| Created      | 2025-10-21               |
| Last Updated | 2025-10-21               |
| Category     | Validation & Model Layer |

