弁護士ドットコム株式会社 Creators’ blog

弁護士ドットコムがエンジニア・デザイナーのサービス開発事例やデザイン活動を発信する公式ブログです。

弁護士ドットコムサービスでモノリスをマイクロサービス化しようとして失敗した原因を振り返る

初めまして。弁護士ドットコム株式会社でエンジニアをやっている井出です。

弊社は 2022 年 2 月から Creator's Blog を始めております。
その記念すべき最初の記事として 弁護士ドットコムサービスのビジネスと共にみるマイクロサービスの進化 を投稿いたしました。
こちらの記事で弊社がマイクロサービス化に挑戦したこと、その後の課題をどう解決していったかについて分かりやすくまとめられておりますので、ぜひ一読してから今回の記事を読んでみていただければと思います。

さて、上記記事内でも触れられているとおりマイクロサービス化プロジェクトである Gavel プロジェクトでは様々な課題が出てきました。
今回はその中の 1 つである、

ビジネス分析が不十分で一部のサービス境界が想定と異なり、開発速度が低下

について、何をどう間違え、なぜ開発速度が低下したのかについて振り返ります。
もしこれからモノリスを分割してマイクロサービスにしようと考えているエンジニアの方がいましたら、参考にしていただけると幸いです。

当初のサービス分割案

弁護士ドットコムは下記図のようなモジュール構成になっており、(多少の問題はあるだろうが)そのとおりにサービス分割できると考えていました。

弁護士ドットコムのモジュール構成図: 個別機能として、みんなの法律相談、弁護士ドットコムニュース、弁護士検索、一括見積もり、その他。共通機能として、メール配信、認証、決済、ユーザー管理、その他。
モジュール構成図

みんなの法律相談
弁護士ドットコムニュース
弁護士検索
一括見積もり

一見、違和感はないと思います。
私は上記のうち、認証決済と弁護士ドットコムニュースの分離を担当しました。

では、この図のとおりに分割しようとしたときに何が起きたのでしょうか。

システムから見た弁護士ドットコムサイト

実際に分割するために、それぞれの担当者が各モジュールのコードを精査しました。
その結果、弁護士ドットコムサイトは下記のような構成になっていることが判明しました。

弁護士ドットコムの実際のモジュール構成図: 「カテゴリ」が新たに存在し、全てのモジュールにカテゴリが食い込んでいる。
実際のモジュール構成図

カテゴリ管理機能のコードが、各モジュールのコードの細部に渡り入り込んでおり非常に密結合になっていました。
カテゴリは関係ないと考えていた認証決済機能(ついでに言うと、認証と決済とユーザー管理は密結合していました)にまで影響が及んでおり、とても大きな問題になりました。

つまりシステムから見た弁護士ドットコムは、カテゴリを中心とした一つの泥団子状のサービスだった、というわけです。

カテゴリとは

カテゴリの総数は 800以上 あり、主に法律分野などで分類しています。
主にみんなの法律相談機能や弁護士ドットコムニュース機能が大きな影響を受けます。

これらがどのように使われているか簡単に説明します。
下記は弊社のとある弁護士ドットコムニュースの URL になりますが、この「c_18」がカテゴリ ID になります。

https://www.bengo4.com/c_18/n_15202/

「c_18」がどのようなカテゴリであるかについては下記 URL を見てると分かります。

https://www.bengo4.com/c_18/

つまり上記ニュースは民事・その他のカテゴリに属している、となります。

カテゴリはネストすることも可能です。

https://www.bengo4.com/c_3/c_1020/c_1147/

上記 URL は、 離婚・男女問題 > 離婚原因 > DV となっています。

カテゴリは上記汎用カテゴリだけではなく みんなの法律相談用カテゴリ 弁護士用カテゴリ レコメンド用カテゴリ といった特定機能・データ特化のカテゴリも存在し、膨大なカテゴリ数と相まって理解と管理は難易度が高いです。
さらに当時はサブドメインにもカテゴリ ID が振られており、インフラも含めてカテゴリ管理が徹底されていました。

現在もカテゴリは重要であり、弁護士ドットコムサイトを見ていただければ分かるとおり「お悩みから解決方法を探す」(カテゴリ)が表示上位の大きな部分を占めています。

組織構造から見た弁護士ドットコムサイト

システムから見た弁護士ドットコムサイトはどのようなものか分かりました。
では組織構造から見たらどうでしょうか。

当時のエンジニアチームは ミッション制 という、提供したいユーザー体験ごとにチームを作る方式を採用していました。
この方式ではミッションを達成したらチームは解散することになります。

チームの一つとして弁護士ドットコムサイトを横断的に見てユーザー戦略を考える ナビゲーションチーム が存在していました。
この ナビゲーションチーム がカテゴリに深く関わっておりました。
また 法律相談チーム も当時は存在していました。
これらのチームはミッション達成後解散となりました。
一方それ以外の ニュースチーム認証決済チーム といったものは存在していませんでした。

このように組織構造は頻繁に変更されており、チームが解散したり新しく結成されたりして、どの機能をどのチームが見るかは難しい問題でした。
結果、担当チームがいないかったり、いなくなってしまった機能は他のチームや、かつて開発に携わってた人が手が開いた隙にサポートするといったことが行われていました。
組織的に見ても安定して分割可能な境界というのを見極めるのが難しい状況でした。

しかしカテゴリ管理を担当するチームはチーム名やメンバーは変更になりつつも長い間存在していました。
最近になり重要度は下がり専任チームはなくなりましたが、当時は組織的に見て重要な立ち位置にありました。

つまり当時の組織構造から見た弁護士ドットコムサイトは、カテゴリを中心とした一つの団子状のサービスでした。

分割を試みる

当時の方針としてはモジュール内の再設計・リファクタリングは最小限とする方針になっていました。

疎結合化 >> 運用 >> 内部設計

モジュール内のコードを変更せずに分割するとなると、当然ですがカテゴリ管理機能のコードが残ったままになります。
そのため、分割した各サービスにカテゴリ管理機能のモジュール群のコピーを持たせる必要がありました。
この決定は上記優先順位のとおり、綺麗に分割できない場合は分割することそのものを優先し、不都合のある部分を運用でカバーするという方針に沿っています。
コピーするためのちょっとしたツールや一部モジュールの composer 化(PHP のパッケージ管理)なども行いましたが、カテゴリ周辺の開発を行うときにモジュール群を各サービスにコピーしなければならないという問題は解決しません。

しかしプロジェクトが進行していくに従って問題が深刻化していったため、カテゴリ管理機能をカテゴリサービス・ URL 生成サービスとして分離することも検討されましたが、実施には至りませんでした。

問題解決を阻んだもの

Gavel プロジェクトではプロジェクト終了期限が決まっていました。
様々な問題でプロジェクト進行が遅れている中、分離作業を期日までに一定終えることが優先順位として高い位置にありました。
そのため、カテゴリ問題を含むその他問題の優先順位は低く設定されていました。

この結果、カテゴリ問題は未解決のまま Gavel プロジェクトは終了することとなりました。

Gavel プロジェクト終了直後の構成

Gavel プロジェクト終了直後のサービス分割は下記図のようになりました。
いくつかの機能は作業が期日までに間に合わなかったため、分割されませんでした。

Gavelプロジェクト終了直後のサービス構成図: 個別機能として、みんなの法律相談、弁護士ドットコムニュース、その他。共通機能として、メール配信、認証+決済、その他。
Gavelプロジェクト終了直後のサービス構成図

これだけ見ると一定マイクロサービスとして分割できたと思われるかもしれません。
しかしもう少し詳しく見ると実態は下記のような感じでした。

Gavelプロジェクト終了直後の詳細なサービス構成図: 各サービスの中にカテゴリ管理、ユーザー管理のコードが入り込んでいる。
Gavelプロジェクト終了直後の詳細なサービス構成図

カテゴリ管理だけでなく他にも密結合され分離できないモジュール群(例えばユーザー管理)が存在し、各サービスでモジュール群のコピーを持つことになりました。

分割後の開発

カテゴリ管理は弁護士ドットコムでは重要な機能です。
カテゴリ管理周辺のモジュールには頻繁に改修が入りますが、改修のたびに他のサービスへのモジュールコピーとテストを実施する必要がありました。
またデプロイ順序なども気をつけないと障害を招くことがあり、デプロイにも気を付ける必要がありました。

その結果、開発速度はモノリスの時よりもさらに遅くなり、苦痛を伴うものとなりました。

カテゴリ以外の問題

今回紹介したカテゴリ絡みの問題以外にも下記のような問題があり、開発に大きな支障をきたすことになりました。

  • インフラである kubernetes 環境が不安定で、デプロイのたびにエラーがでる
  • デプロイしなくてもエラーが頻発する
  • 全ての機能を分離して kubernetes 環境に持ってこれなかったため新旧まざった複雑な環境 (※1) になり、エラーが発生しても原因の特定が困難を極める
  • 複雑な環境で開発するため、開発時に気にしないといけない箇所が多く非常に開発体験が悪い

※1 弁護士ドットコムサービスのビジネスと共にみるマイクロサービスの進化より抜粋 マイクロサービス導入期 アーキテクチャ図

その後

その後、このマイクロサービス化がどのような変遷を経たのかは、冒頭でも紹介した 弁護士ドットコムサービスのビジネスと共にみるマイクロサービスの進化 の記事でよくまとまっておりますので、ぜひ読んでいただければと思います。

振り返り

モノリスを分割するときの接合部はマイクロサービスアーキテクチャ本で書かれているとおり、

境界づけられたコンテキストが優れた接合部になります。

マイクロサービスアーキテクチャ 94p

コンテキストを観察することが大切です。
しかし当時は見かけ上の機能を接合部として考えていました。
ドメイン理解が進みより良い対応方法が見えてきてもリソースが足らず対応することができませんでした。

スパゲッティ化したモノリスのコードであってもコードは組織構造やドメインを表していました。
システムを設計する組織は、そのコミュニケーション構造をそっくりまねた構造の設計を生み出してしまうという コンウェイの法則 はもちろんですが、結局はドメインもどのような形であれ設計やコードにちゃんと反映されていると思いました。

モノリスを分割するときは真摯にコードを読み解き、エンジニアだけでなくプロダクトオーナー/マネージャーなどの方々を巻き込んで境界づけられたコンテキストを明らかにすることが大切だと身をもって経験することになりました。
分割をするという結論ありきではなく、巨大なコンテキストである場合は無理に分割はしないという判断も冷静に柔軟に行う必要もあります。

しかし、巨大な境界づけられたコンテキストによって、やむを得ない統合の要求に対応でき、しかもモデルそのものの複雑さを別にすれば、1つのコンテキストを作ることが現実的であるなら、コンテキストを分割するのは最善の策ではないかもしれない。

エリック・エヴァンスのドメイン駆動設計 403p

また作業が進むにつれてドメイン理解は進みます。その結果、作業開始時の設計が間違っているのではと疑念を抱いたときはちゃんと立ち止まり議論をする必要があります。間違った設計のまま進むとさらなる悪化を招きます。ましてや途中でやめてしまうと運用不能になるリスクがあります。
設計を変更する可能性も考慮に入れた開発方針をあらかじめ用意しておき、進め方はなんであれ、必ずやり切ることが大切になります。

最後に

ここまで読んでいただきありがとうございます。
DDD 関連の本やマイクロサービス関連の本を読み知識としては持っていても、実際の現場ではどういった問題になるか分かりづらいことが多いかと思いますが、少しでも理解が得られたのであれば嬉しくおもいます。

またマイクロサービス化は長く険しい戦いになります。
長期間に渡り多くの人の協力が不可欠になりますので、技術的な話とは別に、人と人とを大切にすることも重要になります。
私も優秀な同僚の数々の助けがあり、最終的にはなんとか安定した基盤を構築できました。

今回のこの記事が、マイクロサービスに挑戦するエンジニアの成功の助けになれれば幸いです。