コドモン Product Team Blog

株式会社コドモンの開発チームで運営しているブログです。エンジニアやPdMメンバーが、プロダクトや技術やチームについて発信します!

モノリスの緩やかなリアーキテクチャ - 7年を振り返る

プロダクト開発部の髙橋です。先日、入社から丸7年が経過し、8年目に入りました。 開発部のメンバーも当初は10名ほどでしたが、現在は100名に迫る規模になっており、さらに増えていく見込みです。8年前には中途採用で一番新しいメンバーだった私も、部署内で2番目に古い人間となってしまいました。

弊社の主力サービスであるコドモンは、2016年のサービス開始以来急激な成長を遂げており、2026年3月時点で25,000を超える施設にご利用をいただいています。

サービスの成長に伴い、システムの設計も、緩やかではありますが、段階的に見直しが行われてきました。この資料では、私が入社した2019年ごろから現在まで行われた、モノリスバックエンドの主要な設計判断を振り返り、その判断がどのようなトレードオフをもたらしたかを述べたいと思います。

過去の設計判断

2018年まで - リリース優先がもたらした技術負債

PHPで実装されたバックエンドはフレームワークが導入されておらず、ビジネスロジックの大半はコントローラーに実装されていました。薄い自前フレームワークのレイヤーの上に、いわゆるfat controllerがのっているような構造です。 すでに相当数の機能を提供していましたが、機能間には境界が引かれていませんでした。それぞれの機能が、必要に応じて連携する機能のテーブルに直接アクセスして、データを取得・更新していました。

また、構造の似た別機能のデータを種別カラムを持つ単一のテーブルに保管し、共有して使うというテーブル設計が行われており、APIエンドポイントも共有していました。(汎用テーブル・汎用API) さらに、多くのテーブルはJSONでデータを保存し、柔軟なデータを受け入れることができるようになっていました。

汎用テーブルの問題についてはこちらの記事に詳しくまとめています。 tech.codmon.com

当時、自動テストは全く存在しておらず、全てのテストは手動で行われていました。

システムはAWS上に構築されており、単一ノードのEC2サーバーとRDSのシンプルな構造でした。 外部連携用のAPIサーバーが存在しており、一部はモノリスのAPIを叩いていたものの、DBに直接アクセスして読み書きも行っています。

また、AWS環境のほか、自治体向けのオンプレミス環境も存在しており、2環境の微妙な差 (利用できるサービスの違いや、環境差分) はコードの各所に分岐を生み、さらなる複雑さをもたらすという状況でした。

リリースは手作業で行われており、サーバー上でgit pullコマンドを実行してデプロイを行なっていました。

2019年 - 品質重視への方針転換

利用施設数が伸び始め、3000施設を超えました。

エンジニア数も増え始め、引き続き新規機能追加・改善が積極的に行われていましたが、不具合起因のシステムトラブルも多発していました。 この時点で設計の大きな見直しは行われませんでしたが、開発環境のDocker化や、PHPUnitの導入などの改善が行われています。自動テストは安定稼働と開発スピードを両立する肝となるもので、ここから数年かけてCI/CDの仕組みとともに継続的に改善が行われていきます。

リリース作業が手動によるgit pullからAnsibleを使ったデプロイに改善されました。リリースも19時にほぼ毎日リリースされる形になっています。

あまりにも技術的負債が大きく、維持・改善が困難な状況のため、モノリスのコードベースは捨てて、完全に一から作り直すべきという意見が大勢を占めていました。しかし、実際にはそのようなリソースの余裕は持てなかったため、大半のコードはこの後も維持されていきます。

また、この時期に小池社長をエンジニアが囲み、プロダクトの困難な状況について、率直に意見交換する場が設けられました。結果として、リリースまでのスピードを重視するあり方から、継続的な開発・改善のために、プログラムの品質を重視する方向に方針転換されることとなりました。

2020年 - 初のマイクロサービス化とDDD導入

利用施設数は5000を超えました。 モノリスには、学童施設向けの幾つかの新機能や、ドキュメンテーション機能が追加されました。これらはスケジュールの制約から、リリースが優先され、既存のアーキテクチャを踏襲する形で実装されています。

この年、シフト機能のリニューアルが決まりましたが、モノリスに新規機能を追加するのはリスクが高いという判断から、マイクロサービスとして実装する試みが行われました。言語もPHPではなくGoが選択されました。ドメイン駆動設計が導入され、DDD + クリーンアーキテクチャはこれ以後バックエンド設計の基本となりました。 シフト機能はモノリス側とのデータ連携が必要でしたが、この部分は「モノリスプロキシー」と呼ばれる腐敗防止層に集約した上で、DBから直接データを取得しています。これは負債感の大きいモノリスとの依存関係を避け、将来連携先ドメインがリプレイスされた時に、置き換える意図で行われた選択です。

この時点ではPHPのバージョンは7.2が使われており、クラスのプロパティで型が宣言できないなど、型システムのサポートが不十分でした。大規模になったモノリスで曖昧な型指定が不具合の原因にもなっていました。厳密な型宣言のある言語が求められ、PHPを忌避する雰囲気もありました。

モノリスではAutifyが導入され、Webテストでハッピーパスが網羅されて、さらにデプロイプロセスに組み込まれました。 処理の入り組んだ fat controller に後付けでユニットテストを入れる難易度は高く、型宣言もないため、修正の影響範囲を特定するのも容易ではありませんでした。網羅的なE2Eテストの存在によって、意図せぬリグレッションをある程度防ぐことができるようになりました。

2021年 - モジュラモノリスとペアプロ文化

利用施設数は8000を超えています。 この時期、モノリスでは新規機能の追加はなく、小規模な機能追加・改善と、リファクタリングや不具合修正が継続的に行われています。

前年に請求管理ドメインのバッチ処理で大規模な障害が発生したことを受け、請求管理機能の作り直しを進めることとなりましたが、いくつかの理由から、リプレイスではなく、モノリスの中でリファクタリングを行うこととなりました。 大きな理由の一つは、リプレイスすると自治体向けのオンプレミス環境で対応が難しくなり、ソースコードが二重管理になってしまう可能性が高いこと、もう一つは、データ構造とロジックがあまりに複雑なため、0から書き直すと計算結果を再現できない可能性があることでした。 そのため、モノリスの中にモジュラモノリスの領域を作り、モデルやリポジトリを一つずつ切り出しては、本番相当のデータを使って検証する作業を繰り返し、完全に書き換えを行いました。(作業完了は翌年です)

請求管理ドメインのリファクタリングについてはこちらの記事をご参照ください。 tech.codmon.com

これと並行する形で資料室機能のリニューアルが開始されます。機能を担当するチームが責任を持って技術選定するという考えのもとで、言語はGoではなくKotlinが選択されました。これ以後は新規実装されるマイクロサービスのバックエンドはKotlinとなっていきました。資料室は、認証基盤をモノリスに依存しつつ、施設や保護者などの情報をAPI経由でモノリスから取得する関係になっています。

アーキテクチャの話ではありませんが、この年の後半からXPが導入され、現在も続く、ペアプロ文化が始まりました。コードの共同所有がこの時から始まり、設計判断の軸足が個人からチームに移る契機だったと思います。

モノリスではgaugeが導入されAPIのテスト実装が徐々に始まりました。

2022年 - PHP 8.0アップデートとオンプレ脱却の始動

利用施設数は11000を超えています。 モジュラモノリスとして段階的にリファクタリングしていくという型が一応できたため、モノリスでもDDD + クリーンアーキテクチャで新規実装・リファクタリングが徐々に進み始めます。

PHPのバージョンを8.0にアップデートしたことで、型システムも7.2と比べかなり強化されました。 この時、自動テストはAutifyによるE2Eテストと部分的にgaugeのAPIテストが存在しましたが、大部分は手動テストしか存在しない状況でした。そのため、非互換のコード修正と動作確認に、非常に大きなコストをかけることとなりました。

またこの年、自治体向けのオンプレミス環境をAWSベースの環境に移行するプロジェクトが始動しました。この後、徐々に契約施設はオンプレミス環境からAWSベースの環境に移行し、2024年には重要な修正を除きリリースの停止、2026年中頃にはオンプレミス環境は完全に終了します。オンプレミス環境は環境差分のためコード上に多数の分岐を生み、リリース作業とそれに伴う動作確認もエンジニアにとって大きな重しになっていました。設計の変更ではありませんが、これは大きな改善でした。

2023年 - 打刻機能の分離と1日2回リリース

利用施設数は14000を超えています。

モノリスの中でのリファクタリングが進む一方で、登降園管理ドメインに含まれる打刻機能のバックエンドが、モノリスから水平分離されマイクロサービス化されました。 園児の登降園で使われる打刻機能は、ユーザー影響が大きく安定稼働が必須です。モノリスのシステムトラブルが打刻機能に影響しうる状態にあり、コアドメインの一つであるため、変動性も高い機能です。

打刻機能はこの前年までにフロントエンドがリニューアルされていましたが、旧フロントエンドも諸事情により廃止できず使われ続けています。新旧のフロントエンドは同じバックエンドAPIを使用していましたが、新フロントエンドはマイクロサービス側API、旧フロントエンドはモノリス側のAPIを使う形となりました。 打刻機能が物理的にモノリスから分離されたことで、安定稼働が実現でき、修正による影響範囲が限定されたことで機能追加・改善も進みやすくなりました。 どちらも同じDBのテーブルにデータを格納するため、ビジネスロジックは2箇所に分散することになりました。「旧フロントエンドは段階的に廃止する」、「データ保存に関するビジネスロジックは当面の間は変動性が低い」という二つの想定からの判断だったと思います。

写真販売・連絡帳製本機能の一部もマイクロサービスとして分離されました。この後写真販売・連絡帳製本に関わる機能は事業部としても分離され、順調に分離独立していきます。

この年の中頃から、モノリスでは平日の日中に、1日2回の定期リリースが行われるようになりました。 もともとコドモンでは2020年ごろから、1日1回のリリースが行われていました。ユーザー影響を最小化するために毎日19時の定期リリースです。その後、段階的に自動テストが拡充され、監視やロールバックの手順が整備されたことで、2022年に平日の15時にリリースされる形に変更されています。 さらに開発とリリースのサイクルを短くして、アジリティを上げるために1日2回のリリースに改善されています。これは継続的なCI/CD、監視、リリースプロセス改善の成果であり、現在も1日2回リリースは維持されています。

2024年 - 汎用テーブルの解体着手

利用施設数は18000を超えています。

この年、DBの負荷高騰が原因でシステムトラブルが発生したため、モノリス側でも広く対策が行われました。最も重大な負荷問題は汎用テーブル (一つのテーブルに、種別で分類された複数機能のデータが格納される形式) によるもので、誤った構造のデータが同じ領域に格納されることで、深刻な負荷問題を引き起こしていました。汎用テーブルは複数存在し、レコード件数は近いうちに10億を超える見込みでした。 この問題を解消するため、適切な構造を持った新テーブルを作成して、徐々に書き込み・読み込みを移行していく手法 (Expand and Contract Pattern) がとられました。このテーブル解体作業は、対象を変えつつ現在も続いています。

モノリスから離れたところでは、用品販売や請求に関する新機能がマイクロサービスとしてリリースされています。これらの機能も、資料室と同様に、認証基盤をモノリスに依存しつつ、連携先機能の情報をAPI経由でモノリスから取得する関係になっています。

テストの改善も行われます。Autifyはここまでほぼ全機能のハッピーパスを網羅するようになっていましたが、小さく修正しつつテストを繰り返し実行するような開発スタイルには合わず、コスト面の問題もありました。そこでgauge + playwrightで置き換えるプロジェクトが始まります。Autifyはこの翌年に置き換えが完了し、完全に脱却しました。 また、このプロジェクトの中で生まれたgaugeのフレームワークは、基本的なWeb・APIのアサーションステップを提供するとともに、setupでspecごとに最小限のデータセットを作成して、終了時にデータをクリーンアップする仕組みを備えていました。この仕組みにより、テストシナリオとデータの紐付きが明確になり、さらにテストデータが独立したことで並列実行も可能になります。結果、Web・APIテストを作成・実行する効率が格段に上がりました。

2025年 - セキュリティ強化とコーディングエージェント本格活用

利用施設数は21000を超えています。 前年から続くDBの負荷対策が続く一方で、プロダクトのセキュリティ強化や脆弱性対策が行われました。詳細は控えますが、認証・認可やセッション管理など、本来フレームワークが組み込みで提供する機能の改修・実装が必要になりました。さらに、通常は1箇所で済む修正を何箇所にも適用する必要がありました。ここまで温存されていた自前フレームワークが大きなコストを産むこととなったのです。

一方で、この前年あたりから試験的にコーディングエージェントが導入されていましたが、性能が向上し始めたこともあり、Claude Code や Github Copilot Coding Agent が本格的に活用されるようになりました。コードのリファクタリングや自動テストの実装が高速に終わるようになり、改善の速度が飛躍的に上がった感覚があります。

2026年 - Laravel導入

利用施設数は25000を超えています。 引き続き、セキュリティ・脆弱性対策と、汎用テーブルの解体が大きな課題として対策が進められています。

自前フレームワークのメンテナンスの難しさを解消するため、Laravelを導入することとなりました。ミドルウェアの再実装と試験的なAPIの移行が完了し、今後は時間をかけてLaravel基盤に移行していくことになります。

AIエージェントの支援が受けられることになったこともあり、自動テストの拡充が進み、APIのテストがほぼ100%存在する状態となりました。 さらに、PR作成時にほぼ全ての自動テスト (ユニットテスト、APIテスト、Webテスト)が比較的迅速に(15-20分程度) に実行されるように改善されました。実装、テスト、リリースが短いサイクルで確実に行える状態となりました。

7年を振り返って、今後の課題

当初、モノリスでは機能間の依存性が管理されておらず、コンテキストの境界を超えて直接DBにアクセスしたり、ビジネスロジックが他のドメインに直接記述されてしまっており、全く関係がない(と思っていた)ところが壊れるということが頻繁に起こっていました。(蛇口を直したら天井が落ちてきたというような表現を何度か聞きました)

7年が経過して、幾つかの理由によりこのような状況は改善しています。

  1. モジュラモノリスへのリファクタリングが多少進み、依存関係が少し整理された
  2. 自動テストの拡充により、不具合を精度高く検知できるようになった
  3. モノリスに知見を持つエンジニアが増え、リスクの高い箇所が認識されるとともに、ドメイン知識が深いエンジニアが増え、どの機能がどこに影響するか修正前に把握できるようになった

現状は2.と3.による効果が大きいように思います。今後はさらにモジュラモノリスへのリファクタリングをすすめて、凝集度を高めて結合度の低い構造にしていく必要があります。今後開発速度をさらに高めるためにも重要な点だと思います。

エンジニアの数は当初の10倍近くに増えていますが、モジュラモノリス化をすすめて、コンテキスト間の依存関係を疎に保ち、十分なテストで品質を担保すれば、モノリスを比較的低コストで維持しつつ、開発のスピードを維持・向上して行けるかもしれません。モノリスを分割すべきか、分割するとしたらどのような粒度で分割するかは慎重な検討が必要です。

登降園の打刻機能など、マイクロサービス化されたいくつかの機能については、ドメインの知識 (ビジネスロジックやDBテーブルへのアクセス) がモノリスとマイクロサービスで分散した状態になっています。これは明確にアンチパターンであり生産性を下げているため、コアなドメインについては是正していく必要があります。分離による安定性向上というメリットを享受できた一方、構造の是正が後回しになったことで新たなコスト・リスクを発生させている側面があります。

モノリスの基盤としてLaravelを導入しましたが、移行完了までの道のりは長く、今後長い時間がかかるものと思います。基盤の話にかかわらず、モノリスでは問題のあるモジュールを避けるために、新しいモジュールを作るということが何度か行われてきました。これは短期的には生産性の向上につながるものの、新旧二つのコードが存在することで認知負荷が上がったり、共通の修正が必要になった時に倍のメンテナンスコストがかかるという問題があります。新しい仕組みを作った時に、古いものを終わらせる道筋まで責任を持って考える必要があります。Laravel基盤への移行についても、今後は完了までの道筋を明確化していく必要があります。

おわりに

モノリスの負債に向き合う姿勢については、特に最初の数年は「モノリスはいずれ捨ててリプレイス(マイクロサービス化)するので」という意識があったように思います。少なくとも私はそのように考えていました。これは当初あまりに技術負債が重く、メンテナンスが困難な状態で、現在のような比較的安定した状態になるとは考えていなかったためでした。結果論ではありますが、モノリスは現在も存続し、おそらく今後も長い期間、サービスを支え続けると思います。

現在のモノリスの状況は、様々なエンジニアがそれぞれのやり方で課題を発見し、地道な改善を続けてきた結果です。私もその一人としてモノリスに向き合い、今後も地道な改善を続けたいと思います。