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

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

deep-rain
November 27, 2024

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

本発表では、10年の歴史を持つ物流システムにおけるリファクタリングとアーキテクチャ構築の戦略について紹介します。システムのパフォーマンス向上を目的に、変更容易性と拡張性を重視したシンプルなアーキテクチャを構築。従来の複雑な依存関係を整理し、モジュールの分離と役割分担を明確化することで、システムの柔軟性を高め、効率的なリファクタリングを実現しました。適切なモジュール化と、依存関係の整理がアーキテクチャ設計においていかに重要かを、実例を通じて詳述します。

deep-rain

November 27, 2024
Tweet

More Decks by deep-rain

Other Decks in Programming

Transcript

  1. ©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
  2. ©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 12 従来のMVCアーキテクチャとその限界 オープンロジでは、主にMVCアーキテクチャを採⽤していたが、 ⻑い年⽉を経て以下のような問題が顕在化 • 重要なサービスクラスが 3000行を超える状態

    となり、変更や拡張が困難に • 適切なモジュール化ができず、ロジックが一極集中 • サービスクラスやモデル間の依存関係が複雑化し、変更が難しくなった • 変更容易性の欠如により、廃止したコードが 放置されている
  3. ©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 13 シンプルなアーキテクチャの設計 • 変更の影響を最⼩限に抑え、安定性を向上させる • 理解と保守を簡単にし、⻑期的な開発効率を向上させる •

    テストの容易さを確保し、迅速かつ安全なリリースを実現する そのために… • モジュール間の依存を明確にし、インターフェースで分離 • 各機能が明確な責務を持つようにし、再利⽤可能なコンポーネントを設計 • モジュールを⼩さな単位に分割し、各機能が独⽴して変更可能にする
  4. ©OPENLOGI Inc. xxの商品をyy個 zzの場所から予約 xxの商品がyy個 zzの場所にある xxの商品が yy個ほしい リファクタリング戦略とアーキテクチャの設計 24

    処理の整理 依存関係の整理 在庫情報取得 引当可否判定 結果の永続化 割当アルゴリズム 注文 在庫情報 引当結果 依存 input output input input output input input
  5. ©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 25 フレームワークとの共存 Model (ORMapper) Controller フレームワークに依存したアプリケーション 注文

    引当結果 在庫情報 在庫情報取得 割当アルゴリズム 結果の永続化 コアな仕様を含むモジュール 依存 引当可否判定
  6. ©OPENLOGI Inc. リファクタリングの進め方 34 テストを追加する 重要機能のリファクタリングにおいて、安全に進めるのは⾮常に重要な観点 • 異常が起きると、即事業存続に関わる 超重要処理 •

    データを活⽤し、単体テストのパターンを拡充 • 更に、包括的なテストとして、既存のデータを機械的に分析し、 あらゆるパターンを網羅
  7. ©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"
  8. ©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 ); } } 呼び出し元 ※実際のコードとは異なります
  9. ©OPENLOGI Inc. 取り組みの結果 43 モジュール化と依存関係の整理により、変更容易性を確保 • ⼿続き的にひとつの関数に書かれていた、数百⾏のコードを4つの処理 に分割し、モジュール化 • インターフェースを定義し、モジュール毎の仕様を確定させることで、

    実装を差し替え可能に • モジュール毎にテストを⾏うことが出来るようになり、安全性が向上 • カナリアリリースや、ストラングラーフィグパターンなどの強⼒な置き 換え⼿法を適⽤可能に