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

コトダマンにおける技術的負債との戦い: サーバーアプリケーションのリアーキテクティング編 【MIXI TECH CONFERENCE 2023】

コトダマンにおける技術的負債との戦い: サーバーアプリケーションのリアーキテクティング編 【MIXI TECH CONFERENCE 2023】

MIXI TECH CONFERENCE 2023
にてお話した田中の資料です。

動画:https://youtu.be/PH5rLh4v5VU
セッション詳細:https://techcon.mixi.co.jp/2023/d2-10

MIXI ENGINEERS

March 02, 2023
Tweet

Video

More Decks by MIXI ENGINEERS

Other Decks in Technology

Transcript

  1. ©MIXI 自己紹介 田中恭友 (Kyosuke Tanaka) デジタルエンターテインメント事業本部 コトダマン事業部 エンジニアグループ マネージャー 2018年4月 株式会社ミクシィ(現

    MIXI)新卒入社       モンストドリームカンパニー       サーバーサイドの新規開発・運用 2020年3月 コトダマン事業部に異動       サーバーエンジニアとしてスクラム Tで開発・運用 2022年9月 エンジニアマネージャー就任 2
  2. ©MIXI コトダマンのアーキテクチャ概要 一般的なソーシャルゲームによくあるアーキテクチャ 14  APIサーバー  Java 8(Amazon Corretto) tomcat struts2

    DB (Amazon Aurora)  チャットサーバー  Java 8(Amazon Corretto) tomcat struts2 その他     運用用途サーバー ゲームクライアント (Unity製) マルチプレイサーバー       monobit engine APIサーバー : ゲーム上の様々な処理を実現するための APIを提供(クエスト開始・ガチャ・ etc…)
  3. ©MIXI コトダマンのアーキテクチャ概要 一般的なソーシャルゲームによくあるアーキテクチャ 15  APIサーバー  Java 8(Amazon Corretto) tomcat struts2

    DB (Amazon Aurora)  チャットサーバー  Java 8(Amazon Corretto) tomcat struts2 その他     運用用途サーバー ゲームクライアント (Unity製) マルチプレイサーバー       monobit engine APIサーバー : ゲーム上の様々な処理を実現するための APIを提供(クエスト開始・ガチャ・ etc…) 今回のリアーキテクティング対象
  4. ©MIXI APIサーバーのアーキテクチャ概要 17 Action Tool GachaTool QuestTool DAO Entity Action

    所謂 Controllerのレイヤ ゲームクライアントとのやりとりを担う リクエスト・レスポンスは独自 DSLで定義 Tool ビジネスロジックを受け持つレイヤ コンテキスト毎に分割されている 例: GachaTool / QuestTool … etc. DAO ORマッパー(Hibernate)が DB上のレコードと1:1対応するクラスを生成 MasterData マスタデータは起動時に全てメモリ上にキャッシュさ れ、singletonクラスが管理 Response Request MasterData ExchangeTool
  5. ©MIXI 問題点① : ドメインモデルの不在 18 CardTool DAO UserCardData オブジェクトのfieldの可視性が制御されず、 どのモジュールからも

    setterを利用できる Tool 上の様々なメソッドの中で自由に変更され、 ドメイン上でどのような制約があるのかが不明 DBレコードのあるカラムがどのように変更されるのか が参照先を全て追いかけないとわからない public static void getCard() UserCardData.setUserId(xxx) UserCardData.setLevel(xxx) UserCardData.setAtk(xxx) public static void levelUp() UserCardData.setLevel(xxx) UserCardData.setAtk(xxx) ORマッパーによってDB上のレコードから変換されたオブジェクト
  6. ©MIXI 問題点② : 密結合したビジネスロジック 19 Tool public static 巨大メソッドA public

    static 巨大メソッドB Toolは巨大なstatic methodの集合体 手続き的に書かれており、 それぞれの処理に暗黙的な依存関係がある (呼び出し元・先の処理内容が隠蔽されていない) これら巨大メソッドに自動テストはなく、 変更の影響範囲が読めない状態 public static 巨大メソッドC 呼び出し 呼び出し primitiveな値を大量に引数で要求するstatic methodの連鎖
  7. ©MIXI 巨大基底クラス 問題点③ : 複雑な依存関係 20 巨大UtilityTool A MasterData Cache

    Class 依存の方向性が制御されておらず、 モジュール間で複雑に依存しあっている 巨大ビジネスロジック Tool C 巨大UtilityTool B 巨大ビジネスロジック Tool D
  8. ©MIXI 問題点③ : 複雑な依存関係 21 public static GachaExecuteResponse Gacha実行処理( GachaExecuteRequest

    request ) { … do_gacha_core_logic(request.gacha_id, request.some_params) … GachaExecuteResult response = new GachaExecuteResult(); response.setGachaResult(...); if (response.getGachaResult == xxx) { … } … return response; } GachaExecuteResponse 特に、最も変更されやすいクライアントに返すレスポンス(≒ View)に依存した処理が問題 ResponseはAPI毎に定義された型であるため、ビジネスロジックの再利用性が全くなくなってしまう クライアント(View)の都合で変更することが困難になり、歪な実装を産んだり、Viewの変更を諦めることになる ガチャのコアロジック GachaExecuteRequest 依存の方向
  9. ©MIXI リアーキテクティングの狙い • ルールに従って実装しているうちに、 オブジェクト指向らしい設計ができるようになること ◦ ドメインモデルを作成・ビジネスロジックをモデルに集約することを強制し ルールに従った実装をしているうちに学習が進むような設計であること • ルールを元にレビューが行えること

    ◦ 設計の良し悪しを議論するための土台にできること ◦ レビューコストの低下 • 自動テストを書く文化を成熟させること ◦ ルールに従って実装すればテスタブルな設計になること ◦ 自動テストを必須とする部分・そうでない部分を分けて 少なくともコアなロジックに自動テストを書くことを当たり前にする ◦ TDDが自然と行われる下地を作る 23
  10. ©MIXI 新アーキテクチャ 24 Presentation UseCase DAO Entity Response Request MasterData

    Model Service IRepository Repository Action • オニオンアーキテクチャを参考にシンプルなアーキテクチャに設定 • ビジネスロジックをModel層に集約することを狙う • 依存の方向性を制御し、変動しやすいレイヤにコアロジックが依存しないようにする 依存の方向性
  11. ©MIXI Modelレイヤ  25 Presentation UseCase DAO Entity Response Request MasterData

    Model Service IRepository Repository Action • ビジネスロジックを受け持つ層 ◦ DDDでいう値オブジェクトやエンティティをこのレイヤに置く ◦ 例: User, Charactor, Gacha, Quest … etc • Modelレイヤのクラス中の公開メソッドには原則単体テストを必須とする ◦ 自動生成された getter は対象外
  12. ©MIXI Serviceレイヤ  26 Presentation UseCase DAO Entity Response Request MasterData

    Service IRepository Repository Action • 複数のModelを利用してアプリケーションに必要な機能を実装するレイヤ ◦ 複数のコンテキストで利用される、汎用的かつ複数の Modelが関連する処理を置く ◦ 例: ItemService(アイテムの付与) • できる限りModelのみで実装を行うが、綺麗にまとまらない場合には Serviceを使う • 旧実装(Tool)との繋ぎ込みを担う、腐敗防止層として利用されることもある Model
  13. ©MIXI UseCaseレイヤ  27 Presentation UseCase DAO Entity Response Request MasterData

    Service IRepository Repository Action • アプリケーションの機能を実現するために、処理順序を規定するためのレイヤ ◦ 例: ExchangeItemUseCase(アイテムを消費し、別のアイテムを獲得する) • DIされたRepositoryを利用してModel / Serviceを取り出し、ビジネスロジックを順に実行 Model
  14. ©MIXI Presentationレイヤ  28 Presentation UseCase DAO Entity Response Request MasterData

    Service IRepository Repository Action • ゲームクライアントとのやりとりを担うレイヤ • クライアントからのリクエストに応じて必要な UseCaseを実行し、 実行結果をクライアントの求める形式のレスポンスに変換し、返却する Model
  15. ©MIXI Presentation Repository 29 UseCase DAO Entity Response Request MasterData

    Service IRepository Repository Action • DB上に永続化されたユーザーデータや、メモリ上にキャッシュされたマスタデータから Modelを再構築する • ビジネスロジックは持たず、 Modelの永続化に集中する • UseCaseから利用する場合にはインタフェースを介して依存性を逆転する Model
  16. ©MIXI Keep : 依存の方向性の制御 • 依存関係が制御されるようになった ◦ クライアント都合でレスポンスが変化しても、ビジネスロジックは影響を受けづらい形に • 副次的な効果

    ◦ レスポンスが決まらないうちにコアロジックから実装を進める ◦ マスタデータの調整中に DummyのRespositoryを作成してコアロジックから実装を進める などの柔軟な実装順の制御が行いやすくなった ◦ 作業分担も行いやすくなった 33
  17. ©MIXI Keep : チーム内の設計に関する知見 • チーム内で設計に関する議論が活発に行われるようになった ◦ GitHub Discussionsに議論の場を用意 ◦

    普段の開発中に気になったことや提案を記録し、 週次定例で繰り返し議論の場を設けるようになった ◦ 議論の例 ▪ CQRSを導入してみたい! ▪ 細かい命名規則についての議論 • レビューがアーキテクチャを中心に行われるようになった ◦ 共通認識を持てているので議論が捗る 34 GitHub Discussionsを利用した議論
  18. ©MIXI Keep : テストを書く文化の醸成 • 新アーキテクチャ導入前のテストカバレッジ( Line) : 0.6% •

    新アーキテクチャ導入後のテストカバレッジ( Line) ◦ Model : 73% (※ 自動生成された getter はテスト不要としている ) ◦ Service : 65% ◦ UseCase : 47% ◦ Repository : 54% ◦ 全体 : 12% • Model以外のレイヤにもテストが書かれるようになった • PR上で「ここテスト欲しい!」という指摘が行われるように • 既存の巨大モジュールに関しては未だテストが書けていない状態 ◦ テスタブルでない設計のため機能実装の片手間では解決できない 35
  19. ©MIXI Problem : 曖昧な実装ルールによる混乱 • 曖昧なルールが混乱を呼ぶことも ◦ 特にServiceの用途が理解されづらく、 ビジネスロジックをとりあえず置く場になってしまいがちになった ▪

    Modelの実装が少なくなり、ドメインモデル貧血症に ◦ 検証期間は細部が曖昧なままの運用だったため、 現状のルールと異なる部分がちらほら • 新規メンバーのイニシャルコスト増加 ◦ 過去実装と新規実装でスタイルが異なり混乱してしまう 37
  20. ©MIXI Problem : Repository • Repositoryにビジネスロジックが実装されてしまいがちに ◦ クエリ効率を考慮するとやむを得ないことも多い • 暗黙的なRepository同士の依存関係

    ◦ ModelとDBレコードを切り離したことで単一責任でないテーブルを扱いやすくなった ◦ 一方で、同じテーブルを参照する Repository間で暗黙的な依存が生まれてしまった ▪ 一見関連のない別 Model・Repositoryで同じカラムを参照・変更しているなど • mockが多用されるテスト ◦ UseCaseのテストで都度Repositoryをmockしている ◦ 複数のテスト間で共通のRepositoryが利用できておらず、テスト実装効率が低下 38
  21. ©MIXI これからの取り組み : 過去実装のリライト • スクラム開発T内ではレガシーな過去実装の書き換えは進まない ◦ 片手間では改善できない巨大モジュールをコストをかけて改善する必要 ◦ エンバグを恐れて変更できない状態のモジュールがあることが

    選択肢を減らし、プロジェクトの成長可能性を狭めてしまう • チーム体制も含めた改善 ◦ 改善を推し進めるチームとしてSREチームを組成 ◦ ビジネスサイドと議論を重ね、どの時期にどのモジュールを改善するか 優先順位をつけて改善していく 41
  22. ©MIXI まとめ • サーバーアプリケーションの保守性向上のため、 新アーキテクチャの導入を進めました • 新規機能を新アーキテクチャで実装しました ◦ 実装スピードはやや低下したものの、テスタブルな設計になりました •

    チームもアプリケーションと共に成長しました • 既存機能の置き換えはまだまだこれから • より多くの価値をユーザーに届けるために これからも技術負債の解決を進めていきます! 42