共用領域物件 (Domain Model) OOA

來源:fp 性質:跨 feature 共用 OOA(Published Language 的結構面);欄位級 single source of truth 落於此。 消費方 OOA:rough-cut-scheduler OOA 下游:domain-model.pseudo.md(尚未建立) 狀態:已簽核(2026-06-09,含 eligibility cascade)


1. 領域詞彙(Ubiquitous Language)

主體類 / 具識別(Entities)

  • §DM-OA-1
    • 詞彙:投產單
    • 英文:ProductionOrder
    • 說明:Aggregate Root;identity poNo。一筆生產請求,組合一棵 VSM
  • §DM-OA-2
    • 詞彙:製程樹
    • 英文:VSM
    • 說明:per-工單 instance:工單釋放時實例化的製程路徑(樹狀、多上游匯流)
  • §DM-OA-3
    • 詞彙:工序節點
    • 英文:VSMNode
    • 說明:VSM 內的節點,由 OperationCoordinate 識別;帶製程時間(DataBox)與物料需求
  • §DM-OA-4
    • 詞彙:資源
    • 英文:Resource
    • 說明:產能承載主體(鍵=資源×日);持 WorkCapability、identity ResourceId(如 LA01/LA02)。WorkCenter 退為純 WorkCenterId VO(活在能力 pair 與 OperationCoordinate),不另建實體;資源歸屬工作站本期不建模(YAGNI)

共用值物件(Value Objects)

  • §DM-OA-5
    • 詞彙:工序座標
    • 英文:OperationCoordinate
    • 說明:共用識別:{投產單 poNo, 工單 workOrderNo, 料號 partNo, 工序 opSeq, 製程 processId, 工作站 workCenterId}(identity)+ 數量 qty(payload)。VSMNodeOccupation.occupier 共用
  • §DM-OA-6
    • 詞彙:製程時間參數
    • 英文:DataBox
    • 說明:製造時間 CT、前置換模 CO、後置等待、移動至下站(VSM data box)
  • §DM-OA-7
    • 詞彙:物料需求
    • 英文:MaterialRequirement
    • 說明:領料清單項;可標 pickAt(中途領料節點,預設第一站)
  • §DM-OA-8
    • 詞彙:工作能力
    • 英文:WorkCapability
    • 說明:資源能做的製程-工作站集合,存為 Map<ProcessId, Set<WorkCenterId>>canPerform(processId, workCenterId) 本期=exact 比對,未來「鄰近工作站」relax 點集中於此(本期不實作)

時間/資源類(Temporal / Capacity)

  • §DM-OA-9
    • 詞彙:資源主檔
    • 英文:ResourcePool
    • 說明:「有哪些資源、各能做什麼」的主檔;eligible(processId, workCenterId) 回傳該製程-工作站的 eligible 資源集合。與 AvailableCapacity(何時有空)分離(SRP)
  • §DM-OA-10
    • 詞彙:可用產能
    • 英文:AvailableCapacity
    • 說明:「資源×日」可用性的持久讀模型(基準=行事曆/班別 − FREEZE);只存 FREEZE 佔用。引擎跑時另疊一份 IN_RUN overlay(屬引擎、不寫回本 model)
  • §DM-OA-11
    • 詞彙:產能格
    • 英文:CapacitySlot
    • 說明:單一(資源×日)的可用量與其佔用清單
  • §DM-OA-12
    • 詞彙:佔用
    • 英文:Occupation
    • 說明:對某 slot 的一筆佔用,帶 occupier(工序座標)+ source
  • §DM-OA-13
    • 詞彙:佔用來源
    • 英文:CapacityOccupationSource
    • 說明:FREEZE(引擎外、唯讀)|IN_RUN(本次粗排暫時佔用)

查詢/輸出投影(Query / Output Projection)

  • §DM-OA-14
    • 詞彙:粗排結果
    • 英文:RoughScheduleResult
    • 說明:一次粗排的整包輸出;含全批節點結果
  • §DM-OA-15
    • 詞彙:粗排節點結果
    • 英文:ScheduledNode
    • 說明:每節點掛 leanPlayDate / lsd / sequence;快照複製輸入節點參數,可回溯

2. 關係草圖(全景)

三個子系統,靠兩個「接縫」串起來:OperationCoordinate 接「製程樹 ↔ 產能」、VSMNode 接「製程樹 ↔ 結果」。下方 §3 各子系統一張小圖。

【子系統 A:製程樹】
  ProductionOrder ──has 1──> VSM ──composed of *──> VSMNode
  VSM ──designates 0..1──> VSMNode(takt)
  VSMNode ──tree link──> VSMNode
  VSMNode ──has──> DataBox / MaterialRequirement
  VSMNode ──identified by──> OperationCoordinate ──┐(接縫 1)
                                                   │
【子系統 B:資源主檔 + 產能佔用】                      │
  ResourcePool ──*──> Resource ──has──> WorkCapability(製程→工作站集合)
  Resource ──eligible(製程-工作站)──> VSMNode(接縫 1,經 OperationCoordinate)
  AvailableCapacity ──*──> CapacitySlot(資源×日)   │
  CapacitySlot ──keyed by──> ResourceId             │
  CapacitySlot ──*──> Occupation                    │
  Occupation ──occupier──> OperationCoordinate ◀────┘
  Occupation ──source──> {FREEZE | IN_RUN}

【子系統 C:粗排結果】
  RoughScheduleResult ──*──> ScheduledNode
  ScheduledNode ──snapshot of──> VSMNode ◀──(接縫 2,可回溯)

3. 類別圖(拆成三張子圖)

子圖 A — 製程樹(Order & Routing)

classDiagram
    direction TB

    class ProductionOrder {
        -poNo: PoNo
        -product: PartNo
        -qty: int
        -dueDate: LocalDate
        -prioritySeq: int
        +vsm() VSM
    }
    class VSM {
        -nodes: List~VSMNode~
        +takt() VSMNode
        +lastNode() VSMNode
        +allNodes() List~VSMNode~
        +childrenNodes() List~VSMNode~
        +parentNode() VSMNode
        +upstreamOf(node) List~VSMNode~
        +downstreamOf(node) List~VSMNode~
    }
    class VSMNode {
        -coordinate: OperationCoordinate
        -dataBox: DataBox
        -materials: List~MaterialRequirement~
        +upstream() List~VSMNode~
        +downstream() List~VSMNode~
    }
    class OperationCoordinate {
        <<value object>>
        -poNo: PoNo
        -workOrderNo: String
        -partNo: PartNo
        -opSeq: int
        -processId: ProcessId
        -workCenterId: WorkCenterId
        -qty: int
    }
    class DataBox {
        <<value object>>
        -cycleTime: Duration
        -changeOverTime: Duration
        -waitingTime: Duration
        -transferTime: Duration
    }
    class MaterialRequirement {
        <<value object>>
        -partNo: PartNo
        -qty: int
        -pickAt: OperationCoordinate
    }

    note for VSM "導航共用契約:upstreamOf/downstreamOf 回傳『遞移』節點集且依拓樸序;allNodes 供正/反向拓樸走訪;lastNode=樹的唯一匯流末站(sink)"

    ProductionOrder "1" *-- "1" VSM
    VSM "1" *-- "*" VSMNode
    VSM "1" --> "0..1" VSMNode : takt
    VSMNode "*" --> "*" VSMNode : downstream
    VSMNode "1" *-- "1" DataBox
    VSMNode "1" *-- "*" MaterialRequirement
    VSMNode "1" --> "1" OperationCoordinate : identified by

子圖 B — 資源主檔 + 產能佔用(Capacity,鍵=資源×日)

classDiagram
    direction TB

    class ResourcePool {
        -resources: List~Resource~
        +eligible(processId, workCenterId) List~Resource~
    }
    class Resource {
        -id: ResourceId
        -capability: WorkCapability
        +canPerform(processId, workCenterId) boolean
    }
    class WorkCapability {
        <<value object>>
        -byProcess: Map < ProcessId, Set < WorkCenterId > >
        +canPerform(processId, workCenterId) boolean
    }
    class AvailableCapacity {
        -slots: List~CapacitySlot~
        +isAvailable(resourceId, day) boolean
        +slotOf(resourceId, day) CapacitySlot
    }
    class CapacitySlot {
        -resourceId: ResourceId
        -day: LocalDate
        -capacity: Duration
        -occupations: List~Occupation~
        +remaining() Duration
        +occupy(occ) void
    }
    class Occupation {
        <<value object>>
        -occupier: OperationCoordinate
        -source: CapacityOccupationSource
        -amount: Duration
    }
    class CapacityOccupationSource {
        <<enumeration>>
        FREEZE
        IN_RUN
    }
    class OperationCoordinate {
        <<value object>>
    }
    note for WorkCapability "canPerform 本期=exact 製程-工作站;未來『鄰近工作站』relax 點集中於此(本期不實作)"
    note for OperationCoordinate "共用 VO;完整欄位見子圖 A。不含 resourceId——粗排不指派實際資源"
    note for CapacitySlot "鍵=資源×日;occupations 只存 FREEZE(外部、唯讀)"
    note for CapacityOccupationSource "IN_RUN 由引擎 per-run overlay 持有,不落地於本 model"

    ResourcePool "1" o-- "*" Resource
    Resource "1" *-- "1" WorkCapability
    AvailableCapacity "1" o-- "*" CapacitySlot
    CapacitySlot "*" --> "1" Resource : keyed by(資源×日)
    CapacitySlot "1" o-- "*" Occupation
    Occupation "1" --> "1" OperationCoordinate : occupier
    Occupation "1" --> "1" CapacityOccupationSource

子圖 C — 粗排結果(Result,快照可回溯)

classDiagram
    direction TB

    class RoughScheduleResult {
        -nodes: List~ScheduledNode~
        +bySequence() List~ScheduledNode~
    }
    class ScheduledNode {
        -coordinate: OperationCoordinate
        -dataBox: DataBox
        -leanPlayDate: LocalDate
        -lsd: LocalDate
        -sequence: int
    }
    class VSMNode {
    }
    note for VSMNode "完整定義見子圖 A"

    RoughScheduleResult "1" *-- "*" ScheduledNode
    ScheduledNode ..> VSMNode : 可回溯(快照自)

設計決策(步驟 1–2 已拍板 / 待確認)

已拍板(2026-06-05)

  • VSM = per-工單 instanceProductionOrder *──組合──> VSMVSMNode 綁工單。與 Stage 2 identity「工單-料號-…」一致,不 cascade 回 Stage 2。

  • RoughScheduleResult = 快照複製ScheduledNode 攜帶當時節點參數的不可變副本,符合「不反查、減少資訊誤差」;result 為獨立可追溯 record。

  • 抽出共用 VO OperationCoordinateVSMNodeOccupation.occupier 共用(§3 兩個接縫之一);識別欄位順序 opSeq → processId → workCenterId。

  • 產能鍵=工作站×日(非節點×日):不同工單的節點搶同一實體工作站(§RCS-FS-2 排擠本質)。屬 OOA 精修,Stage 2「節點×日」用語不動。

  • Freeze 不做獨立類別:實現為 source=FREEZEOccupation(進來只標記哪些產能被佔)。

  • IN_RUN 不落地於本 model:共用 CapacitySlot 只存 FREEZE;本次 run 的暫時佔用由引擎 overlay 持有 →「不寫回」為結構保證。

已拍板(2026-06-08)

  • VSM 導航為 Published Language 共用契約upstreamOfdownstreamOf 回傳遞移節點集且依拓樸序、allNodes 可正/反向拓樸走訪、lastNode 為樹的唯一匯流末站。粗排為首個消費方(§2/§4、§RCS-FS-1 fallback),未來 feature 一律依此契約,不各自定義。

已拍板(2026-06-09,eligibility cascade,承 FS #17

  • 產能鍵再下沉:工作站×日 → 資源×日:一個工作站(製程-工作站)底下對應多台資源(如 L 底下 LA01/LA02),CapacitySlot 改鍵 (resourceId, day)。上方 2026-06-05「產能鍵=工作站×日」決策併此細化,同方向更細一層,非推翻。
  • 新增 ResourceWorkCapabilityResourcePoolResource 為產能承載主體、持 WorkCapabilityResourcePool 管資源主檔 + eligible(processId, workCenterId) 查詢,與 AvailableCapacity(何時有空)分離(SRP:能力為相對靜態主檔、產能為隨時間變動的讀模型;引擎在 overlay 層合用)。
  • eligibility = exact 製程-工作站 pair(FS-faithful)WorkCapabilityMap<ProcessId, Set<WorkCenterId>>canPerform 本期 exact;「鄰近工作站」彈性點預留於 canPerform 內、本期不實作(YAGNI)。LASR-LLASR-L2 差在工作站(L/L2)、製程同為 LASR。
  • WorkCenter 實體移除、Resource→WorkCenter 歸屬不建模WorkCenterId 降為純 VO(活在能力 pair 與 OperationCoordinate);eligibility 完全由能力的製程-工作站 pair 驅動,不靠歸屬。
  • OperationCoordinate 不加 resourceId粗排不指派實際資源、輸出不含資源;資源是引擎 overlay 的暫時分配(見 ../rough-cut-scheduler/rough-cut-scheduler.ooa.mdResourceAllocationStrategy)。

備註(非阻擋)

  • Leadtimeceil((CT×qty+CO)/480)、受 x-y 三策略影響)不放本 model,屬引擎 Strategy(../rough-cut-scheduler/rough-cut-scheduler.ooa.md)。
  • 佔用顆粒度目前為「天」(節點佔 L 連續日)。
  • Repository(取外部物件)本輪不引入,留實作/消費方 OOA。

4. SOLID 審查 + 設計模式

Part A — SOLID

✅ 穩固

  • SRP:VO 各司其職(OperationCoordinate 識別、DataBox 製程時間、MaterialRequirement 物料);VSM 管樹導航、VSMNode 管節點資料+鄰接、CapacitySlot 管單格產能。ResourcePool 管「資源主檔 + eligibility 查詢」、AvailableCapacity 管「資源×日 可用性」,兩關注點分離;eligibility 邏輯收斂於 WorkCapability.canPerform
  • DIP:純 domain、零 infra 依賴。
  • 避免 Primitive Obsession:id 一律 VO(PoNoWorkCenterIdProcessId)。
  • LSP:零繼承(全 VO + flat entity),無替換風險。

⚠️ 風險與處置

  • SRP × 不寫回(已處置)CapacitySlot 若同時裝 FREEZE+IN_RUN,共用物件會在排程中被 mutate、「不寫回」僅靠紀律。決議:共用 CapacitySlot 只存 FREEZEIN_RUN 由引擎 per-run overlay 持有(見 ../rough-cut-scheduler/rough-cut-scheduler.ooa.md)。「不寫回」成為結構保證。
  • OCP(觀察點)CapacityOccupationSource 目前為純 tag(無行為),OK;若未來各來源要不同處理,改多型、勿用 switch。

Part B — 設計模式

#模式用於決議
1Value ObjectOperationCoordinateDataBox/ids✅ 採用
2SnapshotScheduledNode 快照 VSMNode✅ 採用
3Repository取外部物件⏸ 先不引入(取得方式留實作/消費方 OOA)
4CompositeVSM❌ 不採(VSMNode 鄰接 list 已足,避免 pattern-itis)
5Strategy(x-y leadtime三策略切換➡️ 屬引擎,留 ../rough-cut-scheduler/rough-cut-scheduler.ooa.md
6Specification(eligibility 匹配)node 製程-工作站 ↔ 資源 WorkCapability❌ 不採(本期純集合比對;避免 pattern-itis,彈性點留 WorkCapability.canPerform,要長條件再抽 Specification)
7Strategy(資源分配)eligible 資源選哪台➡️ 屬引擎 ResourceAllocationStrategy,留 ../rough-cut-scheduler/rough-cut-scheduler.ooa.md

5. functional spec 對照

Stage 2 domain-model 物件對應 OOA 類別
VSMVSM + VSMNode + DataBox + MaterialRequirement + OperationCoordinate(+ WorkCenter
投產單ProductionOrder
可用產能(資源×日)AvailableCapacity + CapacitySlot(鍵 ResourceId)+ Occupation
資源 + eligibility(#17 / §RCS-FS-16–B19)Resource + WorkCapability + ResourcePool.eligible()
FreezeCapacityOccupationSource.FREEZE佔用來源
RoughScheduleResultRoughScheduleResult + ScheduledNode

簽核

  • 編輯者:Alan / 日期:2026/06/09
  • Reviewer:Alan / 日期:2026/06/09