Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

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

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 ECのグロース パートナー 倉庫 75社

    従量課⾦ API連携 倉庫 1-2社 物流版クラウド サービス 倉庫 250社 フィジカル インターネット 倉庫 500社 倉庫NW拡⼤ 配送連携 倉庫 15社 • EC物流の原型 • メニュー化/標準化 • 多数荷主の⼀元管理 • 倉庫の空きスペースを 有効活⽤ • 在庫分散⇒コスト減 • 越境キャリアと連携 • EC事業の幅広いニーズ をカバー • ⾃動出荷率を向上 • マルチチャネル対応 • データ活⽤‧可視化 • ロボティクス化による オペレーション効率化 • ECの波動に対応 • モノの流れをデータで 捉え、究極に効率化 • 海外やSCM含めた コマース全体の最適化 フェーズ3 フェーズ4 フェーズ5 フェーズ1 フェーズ2
  2. ©OPENLOGI Inc. 自己紹介 辻井 福嗣 2024年2⽉⼊社 CTO室所属 引当処理の改善に取り組んでいます。 どちらかといえば⽝派です。 【経歴】

    - 教育系事業会社 Software Engineer, 研究開発を経て、 Architectとしてアーキテクチャの価値に向き合う - 通信系事業会社 Architectとして、プロジェクト⽴ち上げの0→1 のアーキテクチャを構築 - 個⼈事業 Sound Engineerとして ⾳楽のアーキテクチャに向き合う など、様々なアーキテクチャに向き合ってきた⼈⽣でした。
  3. ©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年の歴史を経て、着実に成⻑してきました。 しかし、これからの成⻑を⽀えるには、技術的負債が⼤きな障害となっています。 組織とプロジェクトの背景 20 従業員数 194 エンジニア 組織の人数 58
  4. ©OPENLOGI Inc. パフォーマンス課題とアーキテクチャ 25 パフォーマンス課題の表出 重要機能である「引当処理」のパフォーマンスが 課題 • 構造はパフォーマンスに直接的な関係はない が…

    • ⼤きくパフォーマンスを改善するためには、 抜本的な変更が必要 • 現状の構造では、少しの変更に⻑い時間が掛 かってしまい、改善が進まない
  5. ©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 29 従来のMVCアーキテクチャとその限界 オープンロジでは、主にMVCアーキテクチャを採⽤していたが、 ⻑い年⽉を経て以下のような問題が顕在化 • 重要なサービスクラスが 3000行を超える状態

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

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

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

    引当結果 在庫情報 在庫情報取得 割当アルゴリズム 結果の永続化 コアな仕様を含むモジュール 依存 引当可否判定
  9. ©OPENLOGI Inc. リファクタリング戦略とアーキテクチャの設計 43 なぜDDDではないのか? DDDは強⼒なパターン集だが… • DDDの代表的な書籍である、「エリック‧エ ヴァンスのドメイン駆動設計」は538ページにも およぶ内容で、学習にはかなりの時間がかかる

    • DDDの複雑な概念やパターンを理解し、実装に 適⽤するには、⾼度な理解と設計スキルが必要 • ⼤規模なチームでは、全メンバーが同じレベル に到達するのが難しい
  10. ©OPENLOGI Inc. リファクタリングの進め方 48 インクリメンタルに進める 千⾥の道も⼀歩から • 巨⼤な変更を⼀度に加えようとしても上 ⼿く⾏かない •

    スコープを⼩さく区切って、少しずつ⼿ を加えていくことが重要 • ⼩さな成功の積み重ねが、最終的に⼤き な成功に繋がる
  11. ©OPENLOGI Inc. リファクタリングの進め方 51 テストを追加する 重要機能のリファクタリングにおいて、安全に進めるのは⾮常に重要な観点 • 異常が起きると、即事業存続に関わる 超重要処理 •

    データを活⽤し、単体テストのパターン を拡充 • 更に、包括的なテストとして、既存の データを機械的に分析し、あらゆる パターンを網羅
  12. ©OPENLOGI Inc. リファクタリングの進め方 52 抽象と具象 • 構造設計は抽象的な話になりがち • しかし最終的な成果物は具体のコードである •

    抽象と具象のレイヤー間でギャップが⽣まれる 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"
  13. ©OPENLOGI Inc. リファクタリングの進め方 55 ⽅針のみをコードに表現し、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 ); } } 呼び出し元 ※実際のコードとは異なります
  14. ©OPENLOGI Inc. リファクタリングの進め方 56 構造に関するレビューを終えてから詳細を実装 ⼤きな⼿戻りを防⽌しつつ、着実な⼀歩を積み重ね ていく • 最後まで実装してしまってからのレビューで は、構造に関する認識齟齬があった場合に⼤き

    な⼿戻りが発⽣するリスクがある • 最初に構造とインターフェースの仕様について の合意さえできていれば、詳細やテストの実装 はタスクを分割でき、並列化も可能 • インターフェースの明確な定義が鍵
  15. ©OPENLOGI Inc. リファクタリングの進め方 58 使われていない機能の削除も⼤切 廃⽌された機能のコードが残っていると、効率に⼤きく影響を与える • リファクタリングは、振る舞いを変更しない ことが前提 •

    廃⽌された機能があると、使われていないに もかかわらず、新しいアーキテクチャに移⾏ するコストが掛かる • リファクタリングの際の認知負荷も増⼤し、 効率的な変更が難しくなる • 並⾏して、使われていない機能のコードや、 モデルの削除も進める
  16. ©OPENLOGI Inc. 取り組みの結果 60 モジュール化と依存関係の整理により、変更容易性を確保 • ⼿続き的にひとつの関数に書かれていた、数百 ⾏のコードを4つの処理に分割し、モジュール 化 •

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