はじめに
はじめまして。株式会社マネーフォワードのグループ会社である株式会社クラビスに所属している古濱です。前回の記事を担当した小笠原と同様、2018年にマネーフォワードに新卒入社し、2021年5月までは法人向け事業を展開するビジネスカンパニーに所属していました。入社後3年間はこの後説明する技術的な課題に取り組んでおり、中でも最後の1年間はその課題に取り組むための専任チームを組成し、リーダーとして活動していました。2021年6月からはクラビスに出向し、新規事業開発部の部長を務めています。
前回は、マネーフォワードのインフラ体制が全体の共通部門になってしまっていることで引き起こされる課題と、それを解決するためのインフラアーキテクチャの説明を行いました。残念ながらマネーフォワードにおいてこうした構造はインフラにのみ存在していたわけではありません。アプリケーションのレイヤーにおいても、複数存在するサービス群が共通部分を持っており、各サービスがそこへ依存することでサービスの安定性や開発速度を阻害する問題も起きていました。
今回はアプリケーション開発の観点から、マネーフォワードのサービス群に存在する依存関係や依存関係が引き起こしている問題と、その解決に向けての同社の取り組みを紹介します。
マネーフォワードのサービス群の歴史
マネーフォワードは創業当時、個人向けお金の見える化サービス「マネーフォワード ME」のみを提供していました。その後、ユーザーからのお金の見える化にとどまらず、確定申告までマネーフォワードで完結させたいという要望を受け、現在「マネーフォワード クラウド確定申告」と呼ばれる確定申告サービスを提供し始めました。
そこから事業者向けバックオフィスSaaS「マネーフォワード クラウド」を構成するサービスを展開していくようになりました。具体的には「マネーフォワード クラウド会計」「マネーフォワード クラウド請求書」「マネーフォワード クラウド給与」「マネーフォワード クラウド経費」などいくつものサービスが誕生していきました。
これらのサービスは独立して存在しているものの、ユーザーにはサービスごとの境界を意識することなくシームレスに利用してもらいたいと考えていました。例えばユーザーデータなどの核となるデータを複数のサービスから共通に参照したいといった要件が出てきた際は、サービスの数も少なく、それぞれ今よりも小さい規模のサービスだった当時は初期開発のコストを減らす意図もあり、共有のデータベース上にサービスを増やしていく選択をしました。これを図として表現すると以下のような変遷をたどっています。なお、この図に登場するサービスは全てRuby on Railsで記述されています。
データを共有することで、データだけでなくデータにひもづくバリデーションなどのロジックに関しても共有したいという要望が出てきました。その要望を叶えるため、Rubyのライブラリ形式であるGemとしてRuby on Railsの実装を共有できるRails Engineという仕組みを利用し、データとロジックの共有を行うようになりました。
さらに、実装を共有できるようになったことで、登録・退会などに代表される全てのサービスで必要とされる実装も共有できるようになりました。ロジックの共有に関するメリットを挙げましたが、ライブラリの共有ではなくそもそもデータベースを共有していることでデータベースのJOIN操作やトランザクションによる更新操作が障壁無く行えることも、アプリケーション開発の視点では大きなメリットだったと付記しておきます。
結果的に、限られた開発リソースのもとでスピード感を持ってサービス開発を行っていくことができました。しかし、サービス開発の初期フェーズにおいて補助輪として作用していたこの仕組みも、個々のサービスの成長および依存するサービスの増加により、いわゆる「分断されたモノリス」の状態となり、徐々に問題が見えるようになってきました。
共有リソースが引き起こす問題
今まで述べてきた通り、マネーフォワードのサービス群ではある時期までデータベースとライブラリの2要素を共有する選択を採ってきました。ユーザーから見たサービスとしては独立していましたが、データベースやライブラリを軸に見るとサービス群全体がモノリシックな状態になっていたわけです。この選択の結果として、サービスの成長や共有するサービス群の増加により見えてきた問題点について、それぞれ説明していきます。
共有データベースが引き起こす問題
データベースは、サービス群の安定的な稼働に対して問題を引き起こすようになっていきました。データベースはアプリケーション開発において限られたリソースとなることがしばしばあると思いますが、さまざまな要因からそのリソースが十分でない状況が発生するようになり始めたのです。
例えば、今のままのペースでデータが増えていくと、数年でディスクが枯渇してしまうというような問題や、特定のサービスでうっかりインデックスの効かないクエリを投げてしまうことでデータベースの性能が悪化し、依存している全サービスに影響が出る問題が起きるようになっていきました。
さらに、依存するサービスが増えてデータベース利用の全貌を誰も把握できないような規模になってからは、そうした問題一つひとつの原因調査の難易度が飛躍的に上がっていきました。明確に1つのクエリが重たい処理となっている場合は対応しやすいですが、複合する要因が積み重なって性能の悪化につながっている場合、どのサービスのどのクエリが主要因なのか、それに対しどういう対応を取るとよいのかの判断が難しくなることもありました。
共有ライブラリが引き起こす問題
データベースだけではなく、ライブラリもスピード感のある開発に対していくつかの問題を引き起こすようになっていました。
前述した通り「共通で利用したいデータにまつわるロジック」と「共通で持っているサービスの仕様に関わるロジック」を持っていたライブラリですが、実際には特定のサービスにのみ関わってくるロジックも入り込み始めました。具体的な例として、特定のサービスを始める際には必ず存在してほしいデータを、このライブラリ上で作成するようなロジックが組み込まれることがありました。こうした経緯でさまざまな目的のロジックがライブラリに追加されていき、ライブラリの全体像を把握することは徐々に難しくなっていきました。
さまざまなロジックを持って肥大化したライブラリは、その後手を入れようにもその難易度が非常に高いものでした。ライブラリおよびその利用サービス全体を完全に把握することは困難で、ライブラリの差分だけを見て入れようとしている変更が、全ての利用サービスにとって問題を引き起こさないと保証することが難しくなってしまったのです。
対応策として、ライブラリに変更を入れる際は全ての利用サービスに対して変更が問題ないことのレビュー・検証を行ってもらうフローが確立しました。しかし、サービス開発者にとっても自サービスに影響があるかをレビュー・検証することは簡単ではありません。結果的にレビューが滞ってしまったり、レビューを行えるメンバーが限定的になってしまったりと、誰も積極的には触りたくないライブラリになっていきました。
共有リソースそのものが引き起こす問題
共有データベースおよび共有ライブラリが引き起こす問題について別々に述べてきましたが、同時にこれらは類似した問題も持っていました。それは責任の所在の曖昧性とも言えるべき問題です。共有リソースそのものを責任持って管理するチームが組成できていなかったマネーフォワードでは、利用者の中の有志がベストエフォートで運用していくことになるケースが多くありました。ただし、その人たちも自分のチームと責務があり、そちらをメインに仕事をしています。
結果として、問題が起きた際に必ずしも迅速に対応できるわけではない、事後の調査に十分な時間を割くことができない、長期的に課題の根本解決を進める際にリソースを割きづらい、などの弊害が発生してしまうことがありました。