Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合...

TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める

2026/06/10開催のTSKaigi Night Talks 2026の登壇資料

More Decks by ギークプラス ソフトウェア事業部

Other Decks in Programming

Transcript

  1. Moving the world intelligently ©Geekplus Co., Ltd. 2 ⾃⼰紹介 ⽒名

    ⻲⼭ 創樹(Soju Kameyama) 所属 株式会社ギークプラス ソフトウェア事業部 経歴 株式会社インフォマティクス 入社 キヤノン株式会社 入社 KINTOテクノロジーズ株式会社 入社 株式会社ギークプラス 入社 2017 2019 2022 2026 サプライチェーンに関わるお客様の業務を支援するSkylaaの開発を行っています 担当
  2. INDEX Moving the world intelligently ©Geekplus Co., Ltd. 3 1.

    同じ事実を別々に持つと、ズレる問題 2. 発想を変えてみる:保存するのは"流れ"だけ 3. まとめ 今回はバックエンドのお話になります
  3. 「品切れ」が上へさかのぼる例で考える ④発注先の⼯場から納品 (⼯場 → 倉庫) 100個 ⑤⾃社倉庫からお店へ転送 (倉庫 → お店)

    100個 ①お店の棚が100個 品切れ!! Moving the world intelligently ©Geekplus Co., Ltd. ②転送依頼 ③発注依頼 この場合のモノの動きは2種類 = 「外部から取り寄せる(発注系)」と「内部で移動(転送 系)」 ⾃社倉庫 取引先メーカー
  4. 同じ100個の動きを、別々の表で⾒たい さきほどの“⼀連の流れ” (⼯場→倉庫→お店 / 100個の動き) Moving the world intelligently ©Geekplus

    Co., Ltd. 「⼯場への発注リスト(⼯場→倉庫 100)」 「お店への転送リスト(倉庫→お店 100)」 「『品切れをどう埋めたか』全体の流れ」
  5. Moving the world intelligently ©Geekplus Co., Ltd. 整合性が崩れる罠 • 「転送リスト」だけ直して他を直し忘れる

    → 発注リスト‧全体の流れとズレる • 表を増やすたびに、 整合チェックのコードも増える 同じ事実を個別に持つと、バグの温床に
  6. Moving the world intelligently ©Geekplus Co., Ltd. 発想を変えてみる:流れだけ保存して、表は計算する 保存するのは「流れ」だけで各リストは流れからの計算 =

    Reactの「派⽣stateは持たず計算する」と同じで、状態は1つ+表⽰は計算 ⼯場 → 倉庫 → お店 「⼀連の流れ」(唯⼀の正解) 関数で計算 (保存しない) 発注リスト 転送リスト 全体リスト その他記録 保存するやつ 計算で出すやつ ※ 物の移動 物の移動 物の移動 物の移動 物の移動
  7. 使う道具は、主に2つ 判別可能ユニオン 「この変数は、何種類かのうちのどれか1つ」という型 type Shape = | { kind: 'circle';

    r: number } | { kind: 'square'; size: number } // 種類によってプロパティ構成も変えられる 各種類に⾒分けるためのラベル(タグ)がある 純粋関数 同じ⼊⼒なら、必ず同じ出⼒(外の状態に触れない) const add = (a: number, b: number) => a + b // DB や グローバルオブジェクト には触らない 結果が予測できる → テストが楽、どこでも再利⽤できる この2つを⽤いて 「流れ → 表」 を安全に組み⽴てることができる Moving the world intelligently ©Geekplus Co., Ltd.
  8. 流れは、2種類のノードでできた“⽊構造”と捉える 外から取り寄せる(発注) 中で運ぶ(転送) // 流れの中の1ノード(2種類) type Node = | {

    kind: 'order'; // 発注 // ...(プロパティは省略) children: Node[] // 次のノード } | { kind: 'transfer'; // 転送 // ...(プロパティは省略) children: Node[] // 次のノード } 「種類は2つのうち1つだけ(判別可能ユニオン)」を型で宣⾔(取り違えはコンパイルエラー) ※スライド⽤に名前を簡略化しています Moving the world intelligently ©Geekplus Co., Ltd.
  9. 流れは、2種類のノードでできた“⽊構造”と捉える ※スライド⽤に名前を簡略化しています Moving the world intelligently // ノード定義(流れの中の1ノードにあたり、今回は2種類用意) type Node

    = | {kind:'order'; from:string;to:string;qty:number;children:Node[]} | {kind:'transfer'; from:string;to:string;qty:number;children:Node[]} // ①「流れ」をこういう入れ子のオブジェクトに格納(実際には DBから取ってくる) const flow: Node[] = [{ kind: 'order', from: '工場', to: '中央倉庫', qty: 180, children: [ {kind:'transfer',from:'中央倉庫',to:'渋谷店',qty:100,children:[]}, {kind:'transfer',from:'中央倉庫',to:'新宿店',qty: 80,children:[]}, ], },] // ② 木を歩いて、指定した kind のノードだけ集める関数(純粋関数) function collect(nodes: Node[], kind: Node['kind']): Node[] { return nodes.flatMap((n) => [ ...(n.kind === kind ? [n] : []), // ← unionだからkindで絞り込める ...collect(n.children, kind), ]) } // 上記関数を使って注文リスト・配送リストを作成(表示用) const orderList = collect(flow, 'order') // 工場→中央倉庫 const transferList = collect(flow, 'transfer') // 中央倉庫→各店舗
  10. さらに拡張すると(卸売業者パターン) Moving the world intelligently ©Geekplus Co., Ltd. 発注 転送

    転送 転送 転送 転送 出荷 出荷 出荷 出荷 複雑な⽊構造も1つの流れとして組み⽴てることができる • 外部の会社に持ち出す「出荷型」を新たにNode定義(内から外) • ⽊構造なので枝分かれも表現できる ex.外部⼯場から親倉庫へ ex.親倉庫から⼦倉庫Aへ ex.⼦倉庫から孫倉庫へ ex.孫倉庫から外部⼩売業者へ ex.親倉庫から⼦倉庫Bへ
  11. Moving the world intelligently ©Geekplus Co., Ltd. 16 3. まとめ

    もう少しだけお付き合いください