資源篩選 (Resource Selection) OOA
來源:fp 上游消費:domain-model OOA(
Resource/WorkCapability/ResourcePool)、capacity-module 單向限制關聯(§CAP-UR-6,尚無 OOA) 下游:resource-selection.pseudo.md(尚未建立) 性質:跨 feature 共用 OOA(Published Language 的行為面);被粗排(能力層)、細排(兩層)消費 狀態:草稿 — Step 1–3(詞彙+類別圖+SOLID)完成;patterns 裁定不新增;N1(節點宣告來源)留簽核前確認,N2/N3 於 Step 3 順帶收斂
1. 領域詞彙(Ubiquitous Language)
沿用既有(消費、不重定義)
| 詞彙 | 英文 | 出處 | 在本模組的角色 |
|---|---|---|---|
| 工序節點 | VSMNode | §DM-OA-3 | 篩選輸入 key(料號+製程+工作站;工序不參與匹配) |
| 資源 | Resource | §DM-OA-4 | 候選與輸出主體(本 OOA 將其由 flat → abstract base,見子圖 A) |
| 工作能力 | WorkCapability | §DM-OA-8 | 能力匹配層判定依據 |
| 資源主檔 | ResourcePool | §DM-OA-9 | 能力匹配層=eligible(processId, workCenterId)(共用同一機制,§RS-UR-12) |
| 單向限制關聯 | (操作員→機台、技師→模具) | §CAP-UR-6 | 耦合過濾依據(消費,非重定義,§RS-UR-7) |
本模組新建模
- §RS-OA-1
- 詞彙:資源篩選器
- 英文:
ResourceSelector - 說明:Domain Service 門面:給一個 VSMNode,回各型別已排序合格序列
- 對應 FS:整份
- §RS-OA-2
- 詞彙:篩選結果
- 英文:
ResourceSelectionResult - 說明:
{ResourceType → RankedResourceSequence}彙整 - 對應 FS:UC-C
- §RS-OA-3
- 詞彙:已排序合格序列
- 英文:
RankedResourceSequence - 說明:單一型別輸出:
[(Resource, resourceSelectionPriority)] - 對應 FS:§RS-FS-9
- §RS-OA-4
- 詞彙:指派限定來源
- 英文:
AssignmentRestrictionProvider - 說明:port:載入外部指派設定(隱藏設定檔格式,§RS-UR-10)
- 對應 FS:UC-B
- §RS-OA-5
- 詞彙:限制關聯查詢
- 英文:
ResourceCouplingQuery - 說明:port:消費單向限制關聯(§CAP-UR-6)
- 對應 FS:UC-C
- §RS-OA-6
- 詞彙:能力空集例外
- 英文:
NoCapableResourceException - 說明:僅能力路徑空集才拋
- 對應 FS:UC-D、§RS-FS-5
- 資源型別
- 英文:
ResourceType(enum) - 全域碼:—(內部)
- 說明:機台/模具/操作員/技師;輸出 bucket 標籤
- 對應 FS:§RS-FS-6
- 英文:
- 篩選 key
- 英文:
ResourceSelectionKey(VO) - 全域碼:—(內部)
- 說明:
{partNo, processId, workCenterId},由OperationCoordinate投影 - 對應 FS:§RS-UR-6
- 英文:
- 指派限定
- 英文:
AssignmentRestriction(VO) - 全域碼:—(內部)
- 說明:單一 key 的各型別指派+宣告
- 對應 FS:UC-B、§RS-FS-2
- 英文:
- 指派序
- 英文:
RankedAssignment(VO) - 全域碼:—(內部)
- 說明:某型別的指派資源 id 有序清單(順序=優先序)
- 對應 FS:§RS-FS-2
- 英文:
- 序列元素
- 英文:
RankedResource(VO) - 全域碼:—(內部)
- 說明:
(Resource, resourceSelectionPriority) - 對應 FS:§RS-FS-9
- 英文:
純內部導航的 VO 維持本地號、不掛全域碼(兩層並存,見 spec-codes.md)。
2. 關係草圖
ResourceSelector ──select(VSMNode)──> ResourceSelectionResult
ResourceSelectionResult ──{type→seq}──> RankedResourceSequence[]
RankedResourceSequence ──*──> RankedResource(Resource, resourceSelectionPriority)
決策優先(per 型別,§RS-FS-1/2):
有 AssignmentRestriction ⇒ 指派覆寫(韌性放行,BR-1)
無 ⇒ 能力 fallback
ResourceSelector ──能力路徑──> ResourcePool.eligible(製程,工作站) 〔沿用 §DM-OA-9〕
ResourceSelector ──指派路徑──> AssignmentRestrictionProvider 〔本模組新增〕
ResourceSelector ──耦合過濾──> ResourceCouplingQuery(操作員→機台) 〔消費 §CAP-UR-6〕
ResourceSelector ──能力空集──> NoCapableResourceException 〔§RS-FS-5〕
3. 類別圖
子圖 A — domain-model delta(Resource flat → 子類)
屬 domain-model OOA 結構改動:簽核前須過
consistency-audit+ 重打domain-model/spec-vN+1tag。粗排(已 coding)吃 base 視角、List<Resource>不變 → code 不動(§RS-UR-9)。
classDiagram direction TB class Resource { <<abstract>> -id: ResourceId -capability: WorkCapability +canPerform(processId, workCenterId) boolean +type() ResourceType } class Machine class Mold class Operator class Technician Resource <|-- Machine Resource <|-- Mold Resource <|-- Operator Resource <|-- Technician note for Resource "§DM-OA-4 由 flat entity → abstract base。type() 讓子類自報型別當輸出 bucket 標籤——非 if-else type-switching,行為仍在子類;ResourceType enum 只是 Result 的 map key"
子圖 B — resource-selection 本體
classDiagram direction TB class ResourceSelector { <<Domain Service>> -resourcePool: ResourcePool -assignments: AssignmentRestrictionProvider -coupling: ResourceCouplingQuery +select(node: VSMNode) ResourceSelectionResult -matchByCapability(key, type) RankedResourceSequence -resolveByAssignment(assignment) RankedResourceSequence -pruneByCoupling(machineSeq, operators) RankedResourceSequence } class ResourceSelectionResult { -sequences: Map~ResourceType, RankedResourceSequence~ +sequenceOf(type) RankedResourceSequence +declaredTypes() Set~ResourceType~ } class RankedResourceSequence { -entries: List~RankedResource~ +isEmpty() boolean +resources() List~Resource~ } class RankedResource { <<value object>> -resource: Resource -resourceSelectionPriority: int } class ResourceSelectionKey { <<value object>> -partNo: PartNo -processId: ProcessId -workCenterId: WorkCenterId +from(coordinate) ResourceSelectionKey } class ResourceType { <<enumeration>> MACHINE MOLD OPERATOR TECHNICIAN } class AssignmentRestrictionProvider { <<interface>> +findFor(key) Optional~AssignmentRestriction~ } class AssignmentRestriction { <<value object>> -key: ResourceSelectionKey -byType: Map~ResourceType, RankedAssignment~ +declaredTypes() Set~ResourceType~ +assignmentFor(type) Optional~RankedAssignment~ } class RankedAssignment { <<value object>> -resourceIds: List~ResourceId~ } class ResourceCouplingQuery { <<interface>> +allowedTargets(restrictive: Resource) Set~ResourceId~ } class NoCapableResourceException { <<exception>> } ResourceSelector ..> ResourcePool : 能力路徑 eligible() ResourceSelector ..> AssignmentRestrictionProvider : 指派路徑 ResourceSelector ..> ResourceCouplingQuery : 耦合過濾 ResourceSelector --> ResourceSelectionResult : produces ResourceSelector ..> NoCapableResourceException : 能力空集 ResourceSelectionResult "1" *-- "*" RankedResourceSequence RankedResourceSequence "1" *-- "*" RankedResource RankedResource "1" --> "1" Resource AssignmentRestriction "1" *-- "*" RankedAssignment AssignmentRestrictionProvider ..> AssignmentRestriction
設計決策(Step 1–2 已拍板 / 待確認)
已拍板(2026-06-12)
- Fork 1(b)
Resource用子類、非 enum:Machine/Mold/Operator/Technician繼承Resource。理由:限制資源功能預計從 class 等級長出差異行為,避免方法層 if-else type-switching(OCP)。代價:domain-modelResource(Published Language)由 flat → abstract base,須走 audit + 重打 DM tag;多型友善,粗排 code 不動(§RS-UR-9)。 - Fork 1b(i)四個扁平子類:直接繼承
Resource,不設PrimaryResource/GatingResource角色中介層(YAGNI;現階段四型別走相同兩層篩選、差異僅耦合資料,未達需中介層的程度。未來行為分化再 refactor)。 - Fork 2(b)指派 key =
{partNo, processId, workCenterId}:pin 綁料號×製程×工作站、可跨工單重用;非純料號。 - Fork 3(a)單一門面:
ResourceSelector.select(VSMNode),兩條路徑(能力/指派)+耦合過濾為私有步驟,不拆CapabilityMatcher/AssignmentResolver/CouplingFilter三協作者。 ResourceTypeenum 為輸出標籤、非行為開關:Resource.type()由各子類回傳自身常數,供Result的 map key;行為仍在子類多型,不違反 Fork 1(b)。
待確認(N1–N3,Step 2 提出)
- N1 — 「節點宣告」來源:UC-C「有宣告才回」「料號B 未宣告技師→不回技師」需要「該節點需要哪些限制型別」的來源。現摺進
AssignmentRestriction.declaredTypes()(外部設定檔有該型別條目=宣告),宣告與指派同一 port 進來,對齊 §RS-UR-10。待確認:是否認同,抑或「宣告」應為VSMNode自帶屬性(與 pin 分離)。 - N2 —
ResourcePool加 by-id 解析:指派路徑韌性放行保留「能力不符」的 pin(如 MX、料號A 的 MOLD1),selector 須ResourceId → Resource直接解析、繞過 eligibility。domain-modelResourcePool目前僅eligible(),需加findById(resourceId)(DM delta 第二筆)。待確認。 - N3 —
ResourceCouplingQuery為 consumed port:限制關聯屬 capacity-module§CAP-UR-6,capacity-module 尚無 OOA。本模組先定 port 介面消費;未來 capacity-module 做 OOA 時兩邊對齊(follow-up)。待確認。
4. SOLID 審查 + 設計模式
Step 3 完成(2026-06-15)。四檢點全判 + pattern 逐一裁定「不新增」。
Part A — SOLID 檢查
| 原則 | 對象 | 結論 |
|---|---|---|
| SRP | ResourceSelector 門面 | ✅ 單一職責一句可述且不含 and:「給一個 VSMNode,產出各型別已排序合格序列」。matchByCapability/resolveByAssignment/pruneByCoupling 是該演算法的三個步驟、非三個職責(不獨立複用、無獨立變更理由),Fork 3(a) 不拆三協作者站得住。⚠️ 觀察點(非現在的問題):門面握 3 collaborator + 2 決策政策。第四條路徑出現或 per-型別邏輯開始分化時, select() 會膨脹 → 屆時才拆 AssignmentResolver/CouplingFilter。現在拆是 premature。 |
| OCP | per-型別分流 + Resource.type() | ✅ ①「有 assignmentFor(type) ⇒ 指派,否則能力」是資料驅動分支(看資料在不在),非 type-switching。②type() 子類各回常數,無 instanceof/switch,新增型別不改既有方法。 |
| LSP | 四子類 Machine/Mold/Operator/Technician | ✅ 無違反(無子類弱化契約)。階層現為空殼(僅 override type()),作為「限制資源耦合行為」未來分化的 seam 刻意保留(Fork 1b,2026-06-15 再確認)。保持誠實的單一條件:耦合差異行為(Operator→Machine、Technician→Mold)落地時必須長在子類;若屆時仍只能塞進 ResourceSelector 的 if-else,即階層未賺到其位置、應回頭收成 flat(DM delta 回退)的訊號。 |
| ISP | AssignmentRestrictionProvider/ResourceCouplingQuery/ResourcePool | ✅ 兩 port 各單一方法,夠窄。N2 ResourcePool.findById() 與 eligible() 同屬「資源主檔查詢」一個內聚面,不撐肥介面。 |
| DIP | 兩 port | ✅ domain 定介面、infra 實作的依賴反轉;N3 將 §CAP-UR-6 收為 consumed port 亦對齊。 |
Part B — 設計模式(裁定:不新增)
| # | Pattern | 候選用途 | 裁定 |
|---|---|---|---|
| 1 | Strategy | 能力/指派兩路徑 | ❌ 不採。僅兩條、靠資料(指派在不在)選擇、非 runtime 抽換配置;抽成 strategy 會打碎內聚演算法,與 Fork 3(a) 衝突。 |
| 2 | Specification | 耦合過濾條件 | ❌ 現階段不採。pruneByCoupling 為單一固定謂詞(機台須落在已選操作員 allowedTargets 內),無 and/or/not 組合需求。耦合規則長出多型別組合時再回來。 |
| 3 | Repository | 兩 port + ResourcePool | ✅ 已隱含採用。兩 port 本質即 port/repository 抽象、ResourcePool 即 Resource repository;不需新增工,標註即可。 |
健康結論:本設計目前不缺 pattern,硬塞為 pattern-itis。
5. functional spec 對照
| FS 項目 | 對應 OOA |
|---|---|
| UC-A 能力匹配 fallback | ResourceSelector.matchByCapability + ResourcePool.eligible(§DM-OA-9) |
| UC-B 指派覆寫(韌性放行 BR-1) | resolveByAssignment + AssignmentRestrictionProvider(§RS-OA-4)+ RankedAssignment |
| UC-C 限制資源篩選與耦合過濾 | pruneByCoupling + ResourceCouplingQuery(§RS-OA-5)+ AssignmentRestriction.declaredTypes |
| UC-D 能力空集 → Exception | NoCapableResourceException(§RS-OA-6) |
| §RS-FS-1/2 兩路徑分流 | ResourceSelector.select 內 per-型別判 assignmentFor(type) 有無 |
| §RS-FS-3 韌性放行 | resolveByAssignment 不與能力取交集;pruneByCoupling 剔光則整批保留 |
| §RS-FS-4 各宣告型別各回序列、機台依耦合剔除 | ResourceSelectionResult(型別→序列)+ pruneByCoupling |
| §RS-FS-5 能力路徑空集 fail-fast | NoCapableResourceException(僅能力路徑、機台) |
| §RS-FS-6 有宣告才回、不反推 | AssignmentRestriction.declaredTypes / Result.declaredTypes(N1 待確認) |
| §RS-FS-7 不含產能/CO 計算 | OOA 無任何產能型別;輸出止於序列 |
§RS-FS-9 順序值 resourceSelectionPriority | RankedResource.resourceSelectionPriority: int |
簽核
- 編輯者: / 日期:
- Reviewer: / 日期:
- 草稿階段;Step 1–3 完成。進簽核前須:① 確認 N1(節點宣告來源);② 跑
consistency-audit(子圖 A 動到 domain-modelResource,屬下游影響)。