產能模組 (Capacity Module) OOA

來源:fp(已簽核 2026-06-15) 上游消費(domain-model OOA,不重定義):Resource/WorkCapability/ResourcePool/AvailableCapacity/CapacitySlot/Occupation§DM-OA-4/8/9/10/11/12)、OperationCoordinate/DataBox 生產端(對外契約):resource-selection consumed port ResourceCouplingQuery.allowedTargets§RS-OA-5)+ eligible/WorkCapability 配對 下游:pseudo(尚未建立) 性質:單一 feature OOA;產能真相源頭,經 ACL 投影餵粗排等下游 狀態:草稿 — Step 1–3 完成(詞彙+類別圖+SOLID);Fork A/B 已拍板;patterns 裁定不新增;進簽核前須跑 consistency-audit(動到共用 domain-model Resource delta 與 AvailableCapacity 投影)


1. 領域詞彙(Ubiquitous Language)

沿用既有(consume、不重定義)

詞彙英文出處在本模組的角色
資源Resource§DM-OA-4產能承載主體;本 OOA 將其 flat → abstract base + 4 子類(capacity 為此 delta 的 owner,見子圖①)
工作能力WorkCapability§DM-OA-8Map<ProcessId, Set<WorkCenterId>>;UC-B 配對依據(合一 pair 形狀不變)
資源主檔ResourcePool§DM-OA-9eligible(processId, workCenterId) 生產端(UC-B,沿用、不重建)
可用產能產能格AvailableCapacityCapacitySlot§DM-OA-10/11資源×日讀模型=ACL 投影目標(只定 port 邊界,不在本模組寫)
工序座標OperationCoordinate§DM-OA-5下游佔用的 occupier(本模組不碰佔用)
製程時間參數DataBox§DM-OA-6changeOverTime 供下游推 CO 佔用量(不在本模組算,§CAP-FS-11

本模組新建模

型別階層(domain-model §DM-OA-4 delta,capacity 為 owner;不另給新 CAP 碼)

詞彙英文說明
資源(抽象)Resource(abstract)flat → abstract,加 type() 自報型別、allowedTargets() 多型生產端
機台/模具/操作員/技師Machine/Mold/Operator/Technician4 子類,皆第一級資源(§CAP-UR-1/10)

時間軸子系統(domain-model 從無 → 本 OOA 首建;UC-A/UC-D 核心)

  • §CAP-OA-1
    • 詞彙:行事曆
    • 英文:Calendar
    • 說明:日曆天框架;把週期班表展開到日期範圍每一天
    • 對應 FS:UC-A 步驟2
  • §CAP-OA-2
    • 詞彙:班表
    • 英文:RecurringShift
    • 說明:dayOfWeek+起始時間+Duration+多組休息;含跨日
    • 對應 FS:UC-A 步驟1、§CAP-UR-12/13
  • §CAP-OA-3
    • 詞彙:加班/休息例外
    • 英文:CalendarException
    • 說明:日曆天+起始+時長+kind(加班∪/休息−),對班表區間做集合運算
    • 對應 FS:UC-A 步驟3、§CAP-FS-2
  • §CAP-OA-4
    • 詞彙:可用時間區段
    • 英文:TimeSlot
    • 說明:[起, 迄) + 歸屬日欄位(跨日切段皆歸起班日,§CAP-FS-1
    • 對應 FS:UC-A 輸出
  • §CAP-OA-5
    • 詞彙:資源班表(聚合)
    • 英文:ResourceSchedule
    • 說明:keyed by ResourceId,聚合三件套(行事曆/班表/加班休息)
    • 對應 FS:UC-A 步驟1(資源自帶班表)
  • §CAP-OA-6
    • 詞彙:資源可用時間解析器
    • 英文:ResourceAvailabilityResolver
    • 說明:UC-A:給 schedule + 日期範圍 → List<TimeSlot>(已集合運算/扣休息/跨日切段)
    • 對應 FS:UC-A
  • §CAP-OA-7
    • 詞彙:可用產能彙整
    • 英文:CapacityAvailabilityView
    • 說明:UC-D:{Resource → List<TimeSlot>},raw 可用、不扣佔用(§CAP-FS-10
    • 對應 FS:UC-D

對外契約 / port(生產端,給下游消費)

  • §CAP-OA-8
    • 詞彙:資源班表載入
    • 英文:ResourceScheduleProvider
    • 說明:port:隱藏班別/班表設定來源(前端設定介面)→ 載入 ResourceSchedule
    • 對應 FS:UC-A
  • §CAP-OA-9
    • 詞彙:單向限制關聯查詢
    • 英文:ResourceCouplingQuery
    • 說明:allowedTargets(restrictive) → Set<ResourceId>對齊 resource-selection consumed port §RS-OA-5
    • 對應 FS:UC-C
  • §CAP-OA-10
    • 詞彙:日投影 port
    • 英文:DailyCapacityProjection
    • 說明:ACL port:資源×時間區段 → 資源×日 AvailableCapacity只定介面,演算法留 pseudo
    • 對應 FS:UC-D、§CAP-FS-12

內部 VO 維持本地號、不掛全域碼:BreakPeriodExceptionKind(enum)、ResourceType(enum)。


2. 關係草圖

Resource(abstract) ──type()──> ResourceType
  ├─ Machine / Mold / Operator / Technician
  ├─ has 1 ──> WorkCapability        〔eligible 配對依據, §DM-OA-8〕
  └─ allowedTargets() 多型           〔Operator→機台集 / Technician→模具集, UC-C〕

ResourceAvailabilityResolver(schedule, 日期範圍)        〔UC-A〕
  ├─ schedule 經 ResourceScheduleProvider 載入
  ├─ RecurringShift.materialize ── Calendar 展開到每日
  ├─ 套 CalendarException (加班 ∪ / 休息 −) → 扣班表自帶休息 (−)
  └─ 產 List<TimeSlot>(帶歸屬日;跨日切段皆歸起班日)

CapacityAvailabilityView(pool, 日期範圍)               〔UC-D〕
  └─ 對 ResourcePool 每個 Resource 跑 Resolver → {Resource → List<TimeSlot>}
        └─(ACL port) DailyCapacityProjection ──> AvailableCapacity/CapacitySlot(資源×日)  〔§CAP-FS-12〕

ResourceCouplingQuery.allowedTargets(restrictive) ──> Set<ResourceId>   〔UC-C, 對齊 §RS-OA-5〕

3. 類別圖

子圖① — Resource 型別階層(domain-model §DM-OA-4 delta,capacity 為 owner)

classDiagram
    direction TB
    class Resource {
        <<abstract>>
        -id: ResourceId
        -capability: WorkCapability
        +canPerform(processId, workCenterId) boolean
        +type() ResourceType
        +allowedTargets() Set~ResourceId~
    }
    class Machine
    class Mold
    class Operator {
        -allowedMachines: Set~ResourceId~
    }
    class Technician {
        -allowedMolds: Set~ResourceId~
    }
    class ResourceType {
        <<enumeration>>
        MACHINE
        MOLD
        OPERATOR
        TECHNICIAN
    }
    Resource <|-- Machine
    Resource <|-- Mold
    Resource <|-- Operator
    Resource <|-- Technician
    Resource --> ResourceType : type()
    note for Resource "§DM-OA-4 flat→abstract(形狀同 resource-selection 子圖 A,本份為權威 owner)。type() 子類自報型別當輸出 bucket;allowedTargets() 為單向限制關聯多型生產端:Machine/Mold 回空集,Operator/Technician 回受限目標集——讓 4 子類賺到存在理由(Fork B)"

地理位置(§CAP-UR-3)/隸屬工作站(§CAP-UR-11behavior 層不建模:隸屬工作站已摺進 WorkCapability 的 pair、地理位置為純資訊欄位不參與任何行為(§CAP-FS-7),沿用 domain-model 對「隸屬工作站」的 YAGNI 處置。

子圖② — 時間軸子系統(本 OOA 首建,UC-A/UC-D 核心)

classDiagram
    direction TB
    class ResourceAvailabilityResolver {
        <<Domain Service>>
        +resolve(schedule, dateRange) List~TimeSlot~
    }
    class CapacityAvailabilityView {
        <<Domain Service>>
        +availability(pool, dateRange) Map~ResourceId, SlotList~
    }
    class ResourceScheduleProvider {
        <<interface>>
        +findFor(resourceId) Optional~ResourceSchedule~
    }
    class ResourceSchedule {
        -resourceId: ResourceId
        -calendar: Calendar
        -shifts: List~RecurringShift~
        -exceptions: List~CalendarException~
    }
    class Calendar {
        <<value object>>
        +daysIn(dateRange) List~LocalDate~
    }
    class RecurringShift {
        <<value object>>
        -dayOfWeek: DayOfWeek
        -startTime: LocalTime
        -duration: Duration
        -breaks: List~BreakPeriod~
        +isOvernight() boolean
        +materialize(date) List~TimeSlot~
    }
    class BreakPeriod {
        <<value object>>
        -startTime: LocalTime
        -duration: Duration
    }
    class CalendarException {
        <<value object>>
        -date: LocalDate
        -startTime: LocalTime
        -duration: Duration
        -kind: ExceptionKind
        +applyTo(slots) List~TimeSlot~
    }
    class ExceptionKind {
        <<enumeration>>
        OVERTIME
        REST
    }
    class TimeSlot {
        <<value object>>
        -start: LocalDateTime
        -end: LocalDateTime
        -attributionDate: LocalDate
    }
    class ResourcePool {
        +eligible(processId, workCenterId) List~Resource~
    }

    ResourceAvailabilityResolver ..> ResourceScheduleProvider : 載入 schedule
    ResourceAvailabilityResolver ..> ResourceSchedule : 讀
    ResourceAvailabilityResolver ..> TimeSlot : produces
    CapacityAvailabilityView ..> ResourceAvailabilityResolver : per resource
    CapacityAvailabilityView ..> ResourcePool : 取資源池
    ResourceScheduleProvider ..> ResourceSchedule : findFor
    ResourceSchedule "1" *-- "1" Calendar
    ResourceSchedule "1" *-- "*" RecurringShift
    ResourceSchedule "1" *-- "*" CalendarException
    RecurringShift "1" *-- "*" BreakPeriod
    RecurringShift ..> TimeSlot : materialize
    CalendarException --> ExceptionKind
    CalendarException ..> TimeSlot : applyTo

SlotList = List<TimeSlot>(避免 Mermaid 巢狀泛型)。

集合運算固定順序(§CAP-FS-2,Fork 1a)RecurringShift.materialize 產底 → 逐筆 CalendarException.applyTo(OVERTIME ∪、REST −)→ 扣班表自帶 BreakPeriod(−)。跨日由 materialize 在日界切兩段、TimeSlot.attributionDate 皆=起班日(§CAP-FS-1,Fork 2a)。重疊衝突依序運算、不擋(模組外 UI alert)。

子圖③ — 對外 port + ACL 投影邊界(只定介面)

classDiagram
    direction TB
    class ResourceCouplingQuery {
        <<interface>>
        +allowedTargets(restrictive) Set~ResourceId~
    }
    class DailyCapacityProjection {
        <<interface>>
        +project(availability) AvailableCapacity
    }
    class AvailableCapacity {
        +slotOf(resourceId, day) CapacitySlot
    }
    note for ResourceCouplingQuery "生產端,對齊 resource-selection consumed port §RS-OA-5;本模組實作委派 Resource.allowedTargets() 多型"
    note for DailyCapacityProjection "ACL port:資源×時間區段 → 資源×日(§DM-OA-10)。只定介面;投影演算法留 pseudo(§CAP-FS-12 / S4)"
    DailyCapacityProjection ..> CapacityAvailabilityView : 吃彙整輸出
    DailyCapacityProjection ..> AvailableCapacity : 產

4. 設計決策

Step 1 已拍板(2026-06-15)

  • Fork 1(a) 集合運算統一CalendarExceptionkind(OVERTIME∪/REST−),Resolver 依 §CAP-FS-2 固定順序套用;不用例外子類(pattern-itis)。
  • Fork 2(a) 歸屬日掛 TimeSlot:每段自帶 attributionDate,跨日切兩段各帶歸屬日(最貼 FS I/O)。
  • Fork 3(a) eligible 沿用 ResourcePool:不另建配對服務;本模組職責止於「定義 Resource 帶哪些 WorkCapability」,配對查詢沿用 §DM-OA-9
  • 型別階層 owner(已拍板):capacity-module 為 §DM-OA-4 flat→abstract+4 子類 delta 的 owner,與 resource-selection 子圖 A 對齊同形狀;resource-selection 那份 audit 的未結 ❌(abstract 化致 2 處 new Resource(...) 無法編譯)交由本 OOA 簽核前那次 consistency-audit 一併誠實化。
  • ACL 投影深度(已拍板):本 OOA 只定 DailyCapacityProjection 邊界 + port 介面;投影演算法留 pseudo。

Step 2 已拍板(2026-06-15,Fork A / B)

  • Fork A(a) 另立 ResourceSchedule 聚合(keyed by ResourceId),Resolver 經 ResourceScheduleProvider 載入。理由:時間軸是 capacity-module 的,不把欄位灌進共用 Resource,把 DM delta 鎖在「abstract+子類」最小範圍(對齊 resource-selection 那份待收 ❌「Resource delta 須手術式最小」)。
  • Fork B(a) allowedTargets() 多型在子類(Operator→allowedMachines、Technician→allowedMolds、Machine/Mold→{}),ResourceCouplingQuery 實作只委派多型。理由:兌現 resource-selection LSP 觀察點「耦合差異行為必須長在子類」,讓 4 子類賺到存在理由、capacity 當 owner 名實相符;耦合資料由 provider 載入填進子類實例(資料來源 vs 行為歸屬分離)。

FS 待裁定收斂(FS §待裁定 Q1~Q3)

  • Q1(歸屬日 vs 日投影)TimeSlot.attributionDate 隨投影帶進 DailyCapacityProjection;下游粗排「日粒度怎麼吃歸屬日」的細節屬投影演算法(pseudo)與粗排對齊,OOA 只保證歸屬日穿透 port 不遺失
  • Q2(配對特例 vs 節點單一對)eligible 維持 exact pair(§CAP-FS-4),特例(同製程異工作站)不在本層放寬,走下游 resource-selection 指派路徑(§CAP-FS-5)。「節點=工作站-製程單一對」不變量在本層保住。
  • Q3(Resource 多型 delta):capacity 為 owner,與 resource-selection 子圖 A 同形狀(abstract + 4 子類 + type()/allowedTargets());code 衝擊誠實化交簽核前 audit。

5. SOLID 審查 + 設計模式

Step 3 完成(2026-06-15)。四檢點逐一具名判 + pattern 逐一裁定「不新增」。

Part A — SOLID 檢查

原則對象結論
SRPResourceAvailabilityResolver / CapacityAvailabilityView / DailyCapacityProjection✅ 三者各一句可述、不含 and:Resolver「解析單一資源的可用時間區段」、View「彙整資源池每個資源的可用區段」、Projection「把資源×時間區段投影成資源×日」。Resolver 內的「展開/集合運算/跨日切段」是同一演算法的步驟、非三職責(呼應 resource-selection ResourceSelector 判法)。ResourceSchedule/RecurringShift/CalendarException 行為長在自己資料上(materialize/applyTo),非貧血模型。
OCPResource.allowedTargets() / Resource.type()(多型);CalendarException.kind(資料驅動)✅(多型面)allowedTargets()/type() 子類各自實作,新增 restrictive 型別不改既有方法、無 instanceof
⚠️ 觀察點(非現在的問題):Fork 1(a) 以 kind(OVERTIME∪/REST−) 資料驅動取代例外子類;集合運算封閉(聯集/差集兩種),目前安全。若出現第三種非集合語意的例外 kindapplyTo 會長出 switch → 屆時才回頭收 Template Method(即 Fork 1 的 (b))。
LSP四子類 Machine/Mold/Operator/Technician無違反,且本 OOA 正面解掉 resource-selection 的 LSP 紅旗:經 Fork B,Operator/Technician 有了真行為(allowedTargets 回各自集合)、Machine/Mold{}誠實的有效答覆(它們不限制誰),非 UnsupportedOperationException。四子類就此賺到存在理由。
保持誠實的條件:未來型別差異行為仍須長在子類;若只能塞回 service 的 if-else,即階層失格、該回退 flat(DM delta 回退訊號)。
ISPResourceScheduleProvider / ResourceCouplingQuery / DailyCapacityProjection✅ 三 port 各單一方法findFor/allowedTargets/project),夠窄、無實作者被迫空實作。沿用的 ResourcePool.eligible 亦單一查詢面。
DIP三 port✅ domain 定介面、infra/ACL 實作的依賴反轉:ResourceScheduleProvider 藏班表設定來源(前端設定介面)、DailyCapacityProjection 藏資源×日投影、ResourceCouplingQuery 為對齊下游的生產端契約。domain 服務不碰具體儲存/格式。

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

#Pattern候選用途裁定
1Strategy / Template Method加班/休息集合運算、例外型別不採。即 Fork 1 的 (b);僅 ∪/− 兩種、集合運算封閉、非 runtime 抽換,抽象化會打散內聚的解析演算法(§CAP-FS-2 固定順序)。出現非集合語意 kind 時再回來。
2Specification能力配對 / 篩選不採eligible 為 exact pair 單一謂詞(§CAP-FS-4)、無 and/or/not 組合需求;耦合過濾屬下游 resource-selection。
3RepositoryResourcePool + ResourceScheduleProvider已隱含採用。兩者本質即資源主檔/班表 repository 抽象,標註即可、不新增工。
4Adapter (ACL)DailyCapacityProjection已隱含採用。port 本身就是資源×時間區段 → 既有資源×日讀模型的防腐轉接(§CAP-FS-12),設計使然、標註即可。

健康結論:本設計目前不缺 pattern,硬塞為 pattern-itis(對齊 resource-selection OOA 結論)。


6. functional spec 對照

FS 項目對應 OOA
UC-A 解析資源可用時間區段ResourceAvailabilityResolver.resolve + RecurringShift.materialize + CalendarException.applyTo + Calendar.daysIn
UC-B 能力-工作站配對ResourcePool.eligible§DM-OA-9)+ WorkCapability.canPerform§DM-OA-8,exact pair)
UC-C 單向限制關聯查詢ResourceCouplingQuery.allowedTargets§CAP-OA-9)+ Resource.allowedTargets() 多型
UC-D 彙整資源×時間區段可用產能CapacityAvailabilityView.availability§CAP-OA-7)+ DailyCapacityProjection§CAP-OA-10,投影)
§CAP-FS-1 跨日歸起班日RecurringShift.isOvernight + materialize 日界切兩段;TimeSlot.attributionDate 皆=起班日
§CAP-FS-2 集合運算固定順序Resolver 編排:materialize → 逐筆 applyTo(∪/−) → 扣 BreakPeriod(−);重疊衝突不擋(模組外 alert)
§CAP-FS-3 無班別日空輸出該日無 RecurringShift match → 空;Resolver 回空清單、不報錯
§CAP-FS-4 eligible exact pairWorkCapability.canPerform exact(§DM-OA-8
§CAP-FS-5 特例不在本層eligible 維持 exact、不放寬工作站;特例走下游指派路徑(本 OOA 不建模)
§CAP-FS-6 能力空集回 {}ResourcePool.eligible 回空 List、不拋(fail-fast 屬下游 §RS-FS-5
§CAP-FS-7 地理位置純資訊不建模(§3 子圖①下方 note,YAGNI)
§CAP-FS-8 限制單向allowedTargets 單向、無反向查詢
§CAP-FS-9 未設定/設空回 {}Operator/Technician.allowedTargets 預設空集;Machine/Mold 恆回 {}
§CAP-FS-10 raw 可用、不扣佔用CapacityAvailabilityView 只彙整 TimeSlot、不碰 Occupation/FREEZE/IN_RUN
§CAP-FS-11 模治具/技師佔用量屬下游四型別皆第一級資源、同走時間軸;DataBox.changeOverTime 佔用量計算不在本模組
§CAP-FS-12 日投影由 ACLDailyCapacityProjection port(只定介面,演算法留 pseudo)

簽核

  • 編輯者: / 日期:
  • Reviewer: / 日期:
  • 草稿階段;Step 1–2 完成。進簽核前須:① 確認 Fork A/B;② 完成 Step 3 SOLID + FS 對照;③ 跑 consistency-audit(動到共用 domain-model Resource delta 與 AvailableCapacity 投影關係,且與 resource-selection 子圖 A/其未結 ❌ 交織)。