Slide 1

Slide 1 text

物流システムにおける リファクタリングと アーキテクチャの再構築 〜依存関係とモジュール分割の重要性〜

Slide 2

Slide 2 text

©OPENLOGI Inc. 組織とプロジェクトの背景 2

Slide 3

Slide 3 text

©OPENLOGI Inc. 資 ⾦ 調 達 15年3⽉ 16年5⽉ 17年7⽉ 20年10⽉ 24年3⽉ 0.6億円 シード 2.1億円 シリーズA 7.3億円 シリーズB 17.5億円 シリーズC 37.5億円 シリーズD 2014年10⽉ サービス ローンチ 導⼊アカウント数の推移 13,000+ 14/10 15/3 15/9 16/3 16/9 17/3 17/9 18/3 18/9 19/3 19/9 20/3 20/9 21/3 21/9 22/3 22/9 23/3 23/9 24/3 24/9 成⻑とともに抱える課題 オープンロジは、これまで10年の歴史を経て、着実に成⻑してきました。 しかし、これからの成⻑を⽀えるには、技術的負債が⼤きな障害となっています。 組織とプロジェクトの背景 3 従業員数 194 エンジニア 組織の人数 58

Slide 4

Slide 4 text

©OPENLOGI Inc. 組織とプロジェクトの背景 4 巨⼤なコードベース ⻑い歴史を積み重ねてきたコードは、160万⾏にも達しています。 ファイル数 14,125 コードの総行数 1,616,326 erikbern/git-of-theseus

Slide 5

Slide 5 text

©OPENLOGI Inc. 組織とプロジェクトの背景 5 技術的負債 オープンロジの根幹を⽀える、超重要なクラスの⾏数は… 超重要クラスの行数 3,308

Slide 6

Slide 6 text

©OPENLOGI Inc. 組織とプロジェクトの背景 6 変更容易性の⽋如 わずか⼗数⾏の変更のリリースに3ヶ⽉かかったものがある ● 複雑な依存関係により、些細な変更がシステム全体に影響 ● テストやデプロイにかかる⼿間が増加 ● 予期せぬバグの修正により、開発が遅延 変更容易性の向上が必須!

Slide 7

Slide 7 text

©OPENLOGI Inc. パフォーマンス課題とアーキテクチャ 7

Slide 8

Slide 8 text

©OPENLOGI Inc. パフォーマンス課題とアーキテクチャ 8 パフォーマンス課題の表出 重要機能である「引当処理」のパフォーマンスが課題 ● 構造はパフォーマンスに直接的な関係はないが… ● ⼤きくパフォーマンスを改善するためには、抜本的な変更が必要 ● 現状の構造では、少しの変更に⻑い時間が掛かってしまい、改善が進まな い

Slide 9

Slide 9 text

©OPENLOGI Inc. パフォーマンス課題とアーキテクチャ 9 引当処理とは? 引当処理とは、商品やリソースを在庫や注⽂データと照合し、必要な数量を確 保するプロセスで、システム全体の運⽤において重要な役割を果たしている ● 注⽂された商品の在庫をチェックして確保する ● この処理が遅れると、配送遅延に直結する

Slide 10

Slide 10 text

©OPENLOGI Inc. パフォーマンス課題とアーキテクチャ 10 パフォーマンス改善のための⼤規模な構造改⾰ ● 数百倍のパフォーマンス向上が必要 ● 従来の構造では、その変更に対応できない ● 構造の⾒直しと変更容易性の確保が必須 そのために必要なことは… ● 依存関係の整理 ● テストやリリース効率の向上

Slide 11

Slide 11 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 11

Slide 12

Slide 12 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 12 従来のMVCアーキテクチャとその限界 オープンロジでは、主にMVCアーキテクチャを採⽤していたが、 ⻑い年⽉を経て以下のような問題が顕在化 ● 重要なサービスクラスが 3000行を超える状態 となり、変更や拡張が困難に ● 適切なモジュール化ができず、ロジックが一極集中 ● サービスクラスやモデル間の依存関係が複雑化し、変更が難しくなった ● 変更容易性の欠如により、廃止したコードが 放置されている

Slide 13

Slide 13 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 13 シンプルなアーキテクチャの設計 ● 変更の影響を最⼩限に抑え、安定性を向上させる ● 理解と保守を簡単にし、⻑期的な開発効率を向上させる ● テストの容易さを確保し、迅速かつ安全なリリースを実現する そのために… ● モジュール間の依存を明確にし、インターフェースで分離 ● 各機能が明確な責務を持つようにし、再利⽤可能なコンポーネントを設計 ● モジュールを⼩さな単位に分割し、各機能が独⽴して変更可能にする

Slide 14

Slide 14 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 14 ⽬的の明確化 アーキテクチャに良し悪しは無く、「⽬的を達成できるかどうか」が重要 ● 変更の影響を最⼩限に抑え、安定性を向上させる ● 理解と保守を簡単にし、⻑期的な開発効率を向上させる ● テストの容易さを確保し、迅速かつ安全なリリースを実現する 明確な⽬的を定義し、そこにフォーカスすることで 迷いのないシンプルな構造を⽬指す

Slide 15

Slide 15 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 15 依存の⽅向 依存の⽅向を揃えることで、安定性と柔軟性を明確化 ● 軽々しく変更してはいけないもの ● 柔軟性を保たなければいけないもの に切り分けることができる 依存する側 依存される側 安定性 低 ⾼ 柔軟性 ⾼ 低

Slide 16

Slide 16 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 16 MVCからモジュールを重視した構造へ Model View Controller 引当 在庫 入庫 出庫

Slide 17

Slide 17 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 17 フレームワークとは仲良くする ● フレームワークは再利⽤可能な機能を提供し、開発をスピードアップする 頼もしいパートナー ● フレームワークの強みを最⼤限に活かしつつ、⽬的を達成することを重視

Slide 18

Slide 18 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 18 フレームワークとの共存 Model (ORMapper) Controller 引当 フレームワークに依存したアプリケーション コアな仕様を含むモジュール 依存

Slide 19

Slide 19 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 19 フレームワークとの共存 Model (ORMapper) Controller 引当 フレームワークに依存したアプリケーション コアな仕様を含むモジュール ? 依存

Slide 20

Slide 20 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 20 処理の整理 引当処理の流れは… 引当 優先度判定 結果の永続化

Slide 21

Slide 21 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 21 処理の整理 抽象化と細分化 引当 在庫情報取得 優先度判定 引当可否判定 結果の永続化 割当アルゴリズム 処理を分割

Slide 22

Slide 22 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 22 処理の整理 抽象化と細分化 在庫情報取得 引当可否判定 結果の永続化 割当アルゴリズム

Slide 23

Slide 23 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 23 インターフェースの重要性 依存の⽅向を変化させられる魔法 実装 実装 実装 実装 インターフェース 依存

Slide 24

Slide 24 text

©OPENLOGI Inc. xxの商品をyy個 zzの場所から予約 xxの商品がyy個 zzの場所にある xxの商品が yy個ほしい リファクタリング戦略とアーキテクチャの設計 24 処理の整理 依存関係の整理 在庫情報取得 引当可否判定 結果の永続化 割当アルゴリズム 注文 在庫情報 引当結果 依存 input output input input output input input

Slide 25

Slide 25 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 25 フレームワークとの共存 Model (ORMapper) Controller フレームワークに依存したアプリケーション 注文 引当結果 在庫情報 在庫情報取得 割当アルゴリズム 結果の永続化 コアな仕様を含むモジュール 依存 引当可否判定

Slide 26

Slide 26 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 26 なぜDDDではないのか? DDDは強⼒なパターン集だが… ● DDDの代表的な書籍である、「エリック‧エヴァンスのドメイン駆動設 計」は538ページにもおよぶ内容で、学習にはかなりの時間がかかる ● DDDの複雑な概念やパターンを理解し、実装に適⽤するには、⾼度な理解 と設計スキルが必要 ● ⼤規模なチームでは、全メンバーが同じレベルに到達するのが難しい

Slide 27

Slide 27 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 27 可能な限りシンプルに ● 構造化すればするほどコード量も増え、複雑に⾒える ● 設計思想を知らなければ、構造をキャッチアップすることが困難 ● 複雑な仕様や要件を、シンプルでわかりやすい構造に落とし込むことが 課題

Slide 28

Slide 28 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 28 シンプルな設計を⽬指すために ● ⾔葉をむやみに増やさない ● 不要なパターンや概念を取り⼊れない ● 明確な⽬的を定義する

Slide 29

Slide 29 text

©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 29 ⽬的に合致するシンプルさを追い求める ● モジュールを独⽴した機能に分割 ● インターフェースの明⽰ ● 依存の⽅向を揃える

Slide 30

Slide 30 text

©OPENLOGI Inc. リファクタリングの進め方 30

Slide 31

Slide 31 text

©OPENLOGI Inc. リファクタリングの進め方 31 インクリメンタルに進める 千⾥の道も⼀歩から ● 巨⼤な変更を⼀度に加えようとしても上⼿く⾏かない ● スコープを⼩さく区切って、少しずつ⼿を加えていくことが重要 ● ⼩さな成功の積み重ねが、最終的に⼤きな成功に繋がる

Slide 32

Slide 32 text

©OPENLOGI Inc. リファクタリングの進め方 32 インクリメンタルに進める ⾊々やりたいことはあるが… 引当 在庫 入庫 出庫 請求 連携

Slide 33

Slide 33 text

©OPENLOGI Inc. リファクタリングの進め方 33 インクリメンタルに進める ⼩さく始める 引当 在庫 入庫 出庫 請求 連携

Slide 34

Slide 34 text

©OPENLOGI Inc. リファクタリングの進め方 34 テストを追加する 重要機能のリファクタリングにおいて、安全に進めるのは⾮常に重要な観点 ● 異常が起きると、即事業存続に関わる 超重要処理 ● データを活⽤し、単体テストのパターンを拡充 ● 更に、包括的なテストとして、既存のデータを機械的に分析し、 あらゆるパターンを網羅

Slide 35

Slide 35 text

©OPENLOGI Inc. リファクタリングの進め方 35 抽象と具象 ● 構造設計は抽象的な話になりがち ● しかし最終的な成果物は具体のコードである ● 抽象と具象のレイヤー間でギャップが⽣まれる class InventoryAllocator : def __init__ (self): self.inventory = {} def add_item (self, item_name , quantity ): if item_name in self.inventory: self.inventory[item_name] += quantity else: self.inventory[item_name] = quantity def allocate_item (self, item_name , request_quantity ): if item_name not in self.inventory: raise ValueError ("Item not found" ) if self.inventory[item_name] < request_quantity: raise ValueError ("Insufficient stock" ) self.inventory[item_name] -= request_quantity return f"{request_quantity } units of {item_name } allocated"

Slide 36

Slide 36 text

©OPENLOGI Inc. リファクタリングの進め方 36 抽象と具象の溝を埋めるには? ● ホワイトボードに図を書く? ● 設計書を起こす? ● プロトタイピング?

Slide 37

Slide 37 text

©OPENLOGI Inc. リファクタリングの進め方 37 この図をコードに表現する 在庫情報取得 引当可否判定 結果の永続化 割当アルゴリズム 注文 在庫情報 引当結果

Slide 38

Slide 38 text

©OPENLOGI Inc. リファクタリングの進め方 38 ⽅針のみをコードに表現し、PR上でやり取り interface AllocationAlgorithm { allocate(order: Order, availableStock: Inventory): AllocationResult; } 割当アルゴリズム interface InventoryInformation { getInventory(itemId: string): Inventory; } 在庫情報取得 class AllocationService { private inventoryInfo : InventoryInformation ; private allocationAlgo : AllocationAlgorithm ; processAllocation (order: Order): boolean { const availableStock = this.inventoryInfo .getInventory (order.itemId ); if (availableStock.quantity < order.quantity ) { console.log(`Not enough stock for item ${order.itemId }`); return false; } return this.allocationAlgo .allocate (order, availableStock ); } } 呼び出し元 ※実際のコードとは異なります

Slide 39

Slide 39 text

©OPENLOGI Inc. リファクタリングの進め方 39 構造に関するレビューを終えてから詳細を実装 ⼤きな⼿戻りを防⽌しつつ、着実な⼀歩を積み重ねていく ● 最後まで実装してしまってからのレビューでは、構造に関する認識齟齬が あった場合に⼤きな⼿戻りが発⽣するリスクがある ● 最初に構造とインターフェースの仕様についての合意さえできていれば、 詳細やテストの実装はタスクを分割でき、並列化も可能 ● インターフェースの明確な定義が鍵

Slide 40

Slide 40 text

©OPENLOGI Inc. リファクタリングの進め方 40 抽象に戻って考え直すこともある 実装の都合でインターフェースや構造を考え直さなければならないことも… ● ⼤筋で合意が取れていれば、⼤幅な⼿戻りになることは少ない ● チーム全体で⼤きな⽅針について認識を揃えることが⼤事 ● 明確な設計思想と、適切なコミュニケーションが必須

Slide 41

Slide 41 text

©OPENLOGI Inc. リファクタリングの進め方 41 使われていない機能の削除も⼤切 廃⽌された機能のコードが残っていると、効率に⼤きく影響を与える ● リファクタリングは、振る舞いを変更しないことが前提 ● 廃⽌された機能があると、使われていないにもかかわらず、新しいアーキ テクチャに移⾏するコストが掛かる ● リファクタリングの際の認知負荷も増⼤し、効率的な変更が難しくなる ● 並⾏して、使われていない機能のコードや、モデルの削除も進める

Slide 42

Slide 42 text

©OPENLOGI Inc. 取り組みの結果 42

Slide 43

Slide 43 text

©OPENLOGI Inc. 取り組みの結果 43 モジュール化と依存関係の整理により、変更容易性を確保 ● ⼿続き的にひとつの関数に書かれていた、数百⾏のコードを4つの処理 に分割し、モジュール化 ● インターフェースを定義し、モジュール毎の仕様を確定させることで、 実装を差し替え可能に ● モジュール毎にテストを⾏うことが出来るようになり、安全性が向上 ● カナリアリリースや、ストラングラーフィグパターンなどの強⼒な置き 換え⼿法を適⽤可能に

Slide 44

Slide 44 text

©OPENLOGI Inc. 今後の展望と継続的な改善 44

Slide 45

Slide 45 text

©OPENLOGI Inc. 今後の展望と継続的な改善 45 継続的なアーキテクチャ改善 システムの成⻑に伴い、アーキテクチャも進化し続ける必要がある ● 今回のテーマである引当処理以外についても、モジュール化と依存の整理 を引き続き進める ● 疎結合な構造により安全かつ迅速なリリースを可能に ● 技術者組織全体が構造を維持し続けられる仕組みを構築 持続可能なシステムへ!

Slide 46

Slide 46 text

結論 46

Slide 47

Slide 47 text

©OPENLOGI Inc. 結論 47 依存関係の整理とモジュール分割が鍵 ⽐較的⼤規模な組織におけるレガシーシステムのリアーキテクチャでは、機能 の依存を整理し、疎結合なモジュールに分割することが鍵となる ● 疎結合なモジュール設計 ● 依存を意識した設計 ● インクリメンタルな改善戦略 ● 効率的なコミュニケーション これらを意識することで、効果的なリアーキテクチャを推進!

Slide 48

Slide 48 text

Are Hiring! We 現在急成⻑期で今がチャンス! 全職種募集しています! 48

Slide 49

Slide 49 text

©OPENLOGI Inc. 物流の未来を、動かす