資源篩選 (Resource Selection) OOA

來源:fp 上游消費:domain-model OOAResource/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+1 tag。粗排(已 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 用子類、非 enumMachine/Mold/Operator/Technician 繼承 Resource。理由:限制資源功能預計從 class 等級長出差異行為,避免方法層 if-else type-switching(OCP)。代價:domain-model Resource(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 三協作者。
  • ResourceType enum 為輸出標籤、非行為開關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-model ResourcePool 目前僅 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 檢查

原則對象結論
SRPResourceSelector 門面✅ 單一職責一句可述且不含 and:「給一個 VSMNode,產出各型別已排序合格序列」。matchByCapability/resolveByAssignment/pruneByCoupling 是該演算法的三個步驟、非三個職責(不獨立複用、無獨立變更理由),Fork 3(a) 不拆三協作者站得住。
⚠️ 觀察點(非現在的問題):門面握 3 collaborator + 2 決策政策。第四條路徑出現per-型別邏輯開始分化時,select() 會膨脹 → 屆時才拆 AssignmentResolver/CouplingFilter。現在拆是 premature。
OCPper-型別分流 + 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 回退)的訊號。
ISPAssignmentRestrictionProvider/ResourceCouplingQuery/ResourcePool✅ 兩 port 各單一方法,夠窄。N2 ResourcePool.findById()eligible() 同屬「資源主檔查詢」一個內聚面,不撐肥介面。
DIP兩 port✅ domain 定介面、infra 實作的依賴反轉;N3 將 §CAP-UR-6 收為 consumed port 亦對齊。

Part B — 設計模式(裁定:不新增)

#Pattern候選用途裁定
1Strategy能力/指派兩路徑不採。僅兩條、靠資料(指派在不在)選擇、非 runtime 抽換配置;抽成 strategy 會打碎內聚演算法,與 Fork 3(a) 衝突。
2Specification耦合過濾條件現階段不採pruneByCoupling單一固定謂詞(機台須落在已選操作員 allowedTargets 內),無 and/or/not 組合需求。耦合規則長出多型別組合時再回來。
3Repository兩 port + ResourcePool已隱含採用。兩 port 本質即 port/repository 抽象、ResourcePool 即 Resource repository;不需新增工,標註即可。

健康結論:本設計目前不缺 pattern,硬塞為 pattern-itis。


5. functional spec 對照

FS 項目對應 OOA
UC-A 能力匹配 fallbackResourceSelector.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 能力空集 → ExceptionNoCapableResourceException§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-fastNoCapableResourceException(僅能力路徑、機台)
§RS-FS-6 有宣告才回、不反推AssignmentRestriction.declaredTypes / Result.declaredTypes(N1 待確認)
§RS-FS-7 不含產能/CO 計算OOA 無任何產能型別;輸出止於序列
§RS-FS-9 順序值 resourceSelectionPriorityRankedResource.resourceSelectionPriority: int

簽核

  • 編輯者: / 日期:
  • Reviewer: / 日期:
  • 草稿階段;Step 1–3 完成。進簽核前須:① 確認 N1(節點宣告來源);② 跑 consistency-audit(子圖 A 動到 domain-model Resource,屬下游影響)。