🗿

なぜ今、モジュラーモノリスという意思決定をしたのか ― 課題認識の変化とタイミングの記録 ―

に公開

こんにちは。PIVOTのテックリード @tawachan です。普段はWebフロントエンド、バックエンド、インフラを横断的に見ながら、チームの技術的な意思決定や開発効率化に取り組んでいます。

この記事では、PIVOTで新機能(コメント機能)を開発するにあたり、なぜこのタイミングで「モジュラーモノリス」という判断をしたのかを振り返ります。

特定のアーキテクチャの優位性を主張するものではありません。書きたいのは、課題認識の変化とタイミングの判断という意思決定プロセスの記録です。

出発点:PHPモノリスからGoモノリスへの移行途中

元々の構造とGo移行の背景

PIVOTのバックエンドは、元々PHPで書かれた典型的なモノリスでした。単一のアプリケーションで全ての機能を処理し、データベースも一つという、多くのWebアプリケーションが採用する構成でした。

段階的にGoへの移行を進めていましたが、これは設計思想を変えるためではなく、主に以下の理由からでした。

  • 型安全性とパフォーマンスの向上
  • コンテナ化とクラウドネイティブ対応
  • 運用・保守性の改善

重要なのは、この時点では「PHPモノリスをGoモノリスに書き換える」という方針だったことです。アーキテクチャパターンを変える意図はまだありませんでした。

PHPからGoへの移行では、まず運用面での健全化を重視していました。

  • ステートレスでコンテナ化可能なアプリケーションにすること[1]
  • 運用・保守を前提とした最低限の技術的健全性を確保すること

これらが、このフェーズの主眼でした。設計の理想形を作ることではなく、これ以上レガシーを増やさないことが最優先だったのです。

しかし、既存の実装をそのままGoに移した部分も多く、根本的な構造上の問題は依然として残っていました。

見えていたが優先度の低かった設計課題

以前から認識していた課題がありました。

  • 実装がユースケース単位の手続き的な構造になっていること[2]
  • ドメインがコード上で明示的に表現されていないこと
  • 変更時に影響範囲を読みづらいこと

ただし当時は、数ある改善課題の一つに過ぎませんでした。「いずれ向き合うもの」という位置づけに留まっていました。

なぜ「まだその時ではなかった」のか

機能開発も並行して進んでおり、PHPからの移行も完全には終わっていませんでした。チームとしては、まず基盤を安定させることが最優先でした。

抜本的な設計変更に踏み切る必然性は、まだ強くありませんでした。新機能としてコメント機能の開発が決まり、AI時代におけるエンジニアリングの役割について考える機会が増えていた時期に、転換点が訪れることになります。

課題の重要度認識が変わった転換点

川島さんとの対話で見えた設計の根本的課題

技術アドバイザーとして入っていただいている川島さん(@kawasima)との継続的な対話の中で、この課題に対する捉え方が大きく変わっていきました。

本質的複雑さと偶有的複雑さの区別[3]ニューレガシーアンチパターンという言語化[4]、画面駆動・テーブル駆動設計への自覚を通じて、問題が単なる実装技術の話ではなく、ビジネスロジックをどう整理し、どこに境界を引くかという設計の根本的な話であることがはっきりしてきました。

特に印象的だったのは、AI時代において人間がやるべき仕事は、依存の中心となるドメインを定義することだけであり、それ以外は原理的にAIが代替・支援できるという視点でした。

AI時代における事業理解とドメイン設計の重要性

実装や配線、定型的なコード生成はAIによっていくらでも加速できます。しかし、どこに境界を引き、どの概念を同じ言語として扱うかという判断は人間にしかできません。

特に重要だと気づいたのは、ドメインの話は技術の世界に閉じるものではないということです。PIVOTの事業が現状どうなっているのか、また今後どういう方向に変化させていくのかという事業理解に深く依存します[5]

AIだけではとてもできないもので、事業を進めようとする人間の側がコントロールしないとどうしようもないことです。そこを色濃く反映していくことがシステムアーキテクチャとして重要だということが分かりました。

この認識を持ったことで、ドメイン設計の位置づけが変わりました。改善項目ではなく、今後の開発効率と持続性を左右する中核的な課題として捉えるようになったのです。

川島さんには大きな方向性をアドバイスしてもらいつつも、自分でも詳細に腹落ちさせるために、オライリーを中心とした関連書籍を読み漁りました[6]。実際に見えている課題に対してドメイン駆動の本を読むと、理解度が全く違うことを改めて実感しました。

モジュラーモノリスという判断

ドメインごとに分割する前提での選択

ドメイン設計が重要だと認識した以上、ドメインごとに実装を疎結合にする必要がありました。そのためには、境界を明確に分けた構造にしなければなりません。

選択肢は主に2つでした。

  • マイクロサービス:ドメインごとに実行・デプロイ単位も分割
  • モジュラーモノリス:ドメインごとに境界は分けるが、実行・デプロイ単位は統合

マイクロサービス vs モジュラーモノリスの現実的判断

この段階でマイクロサービス化を検討しなかったわけではありません。しかし、チームと組織の現実を考えると最適解ではありませんでした

当時のチーム構成では、バックエンドの正社員エンジニアは実質的に一人でした。実行環境を分割するほどの複雑性や運用コストを許容できる状況ではありませんでした。

逆コンウェイの法則を考えると、組織が分かれていないのに実装だけを分けるのは不自然でした。不用意な分割は、結果としてコストと複雑さを増やすだけだと判断しました。

そこで選んだのが、境界(Bounded Context)は明確に分けるが、実行・デプロイ単位は分けない、というモジュラーモノリスのアプローチでした。今のフェーズにおける現実解として、これが最もバランスの取れた判断だと考えています。

実践の場としての新しいBounded Context

コメント機能という全く違う文脈

そのタイミングで始まったのが、コメント機能の開発でした。

既存のシステムは、PIVOTではビジネス映像メディアとして[7]コンテンツを一方向に届ける体験を前提としていました。ユーザーは基本的に「閲覧する側」として完結しており、双方向のやり取りはほとんど想定されていない世界でした。

一方で、コメント機能はユーザー参加型であり、投稿・表示・通知・モデレーションなど、既存機能とは明らかに異なる関心やルールを持つ世界です。全く違うBounded Contextというべきものでした。

この機能を既存の構造にそのまま組み込むと、ドメインの意味が混線し、将来的な変更耐性を著しく下げることが想像できました。

既存コードを壊さずに始められる好機

同時に、これは好機でもありました。

  • 新しいContextであるため既存コードを大きく壊さずに始められる
  • 境界を自然に定義できる
  • 今後の指針となるロールモデルを作れる

これらの条件が揃っていたため、このタイミングでドメイン設計に本気で取り組む条件が整ったと判断しました。

ロールモデル化と既存部分への展開

コメント機能は、モジュラーモノリス移行の最初のロールモデルとして位置づけています。

今後の新機能は、この方針で作っていくという合意をチームで持っています。また、既存の映像閲覧側についても、このロールモデルを参考にしながら、徐々に境界を明確にする移行を進めていく計画です。

具体的な実装指針:ドメイン設計とAI時代の開発

モジュラーモノリスにしたからといって、必ずしも全てのモジュールでドメイン駆動開発をする必要はありません。シンプルなものならトランザクションスクリプト的なアプローチでも構いません。

しかし、ある程度の複雑さがある場合は、適切なドメイン設計をすることが重要だと考えています。ファイル数やコード量が増えたとしても、AI時代においては足枷になりづらいからです。

実際に採用しているディレクトリ構成では、以下のような構造でPort/Adapterパターンを適用しています:

modules/<context>/
├── controller/     # Connect-go RPC エンドポイント
├── usecase/        # アプリケーションサービス
├── domain/entity/  # ドメインエンティティ
├── ports/          # インターフェース定義
├── adapters/       # 外部システム実装
└── renderer/       # エンティティ→proto変換

データベース以外にも外部接続するインターフェースは全てPortとして定義し、依存関係を明確にしています。これにより、各Bounded Contextが完全に内部カプセル化され、root.goのファクトリのみが公開APIとなる構造を実現できました。

ただし、モジュールごとに多少違いや緩急はあります。各モジュールをやりやすいようにやることを重視しており、本来疎結合なので躍起になって統一することはしていません。この温度感も、モジュラーモノリスの利点の一つだと考えています。

「今だった」という意思決定

2週間をアーキテクチャに投資する判断

結果として、機能開発に入る前に約2週間、アーキテクチャと構造の整理に時間を使うことにしました

短期的には機能開発が若干後ろ倒しになりますが、PMやチームとすり合わせた上で、全体としてはスピードは落ちないという判断をしました。

実際に得られた結果

新機能開発の効率向上

コメント機能で作ったロールモデルの効果は、その後の通知機能やクイズ機能などの新しい機能開発で実感しています。同じドメインを取り扱うものは手軽にRPCを実装できるようになりました。

特に映像コンテンツの一覧を返すRPCは色々なパターンがありますが、どんな条件で取得するにしても、ドメインの境界が明確なためシンプルに実装できるようになっています。

並行開発の安全性確保

全く違うドメインであれば、コード上疎結合になっているため安心して並行開発できます。境界を越えた予期しない影響を心配することなく、複数の機能を同時に進められるようになりました。

コードの理解性とレビュー品質向上

ドメインがしっかりと定義されているため、別の人が触る場合でも大きくコードの意味を理解しそこねることがありません。レビューもドメインの関心事に集中でき、品質向上に繋がっています。

AI時代におけるスピードと品質の関係

ソフトウェア開発において「スピードと品質はトレードオフではない」という考え方がありますが、AI時代においてその傾向がさらに加速しているように感じています。

クリーンに定義されたドメインこそが結果的にスピードを生む。この現象は、AIがコード生成や実装を支援する現在において、より顕著になっています。境界が曖昧だとAIも迷いますが、ドメインが明確だとAIの力を最大限活かせるためです。

今回の判断は技術選定の話というよりも、優先順位と意思決定の話でした。

おわりに

この記事で書いたのは、技術選定の話ではなく意思決定のタイミングについてでした。課題は以前からありましたが、重要度の認識が変わり、新しいコンテキストという条件が揃ったため、このタイミングで取り組むことにしました

もし書き換えても違和感が残っている場合は、次に向き合うべきは実装技術ではなく、設計そのものかもしれません。

なお、技術アドバイザーの川島さんのお力添えがあって前に進められております。少人数ながらも技術的に安心して取り組めているのは、川島さんがいてこそです。この場をお借りしてお礼申し上げます。

関連記事

脚注
  1. 当時の課題例として、ローカルファイルシステムへのキャッシュファイル保存、VM上で動作するCronジョブ、サーバー固有の設定ファイルへの依存など、コンテナ化と相性の悪いコードベースになっていました。 ↩︎

  2. この状態は、いわゆるトランザクションスクリプト的な実装でした。 ↩︎

  3. 川島さんの「古典ドメインモデリングパターンの解脱」では、ドメインモデリングの本質として「複雑さへの対処」が挙げられ、本質的複雑さを明らかにすることの重要性が述べられています。 ↩︎

  4. ニューレガシーアンチパターンとは、レガシーシステム再構築時に新たなレガシーを生み出してしまう設計手法を指します。画面駆動・テーブル駆動設計、配線プログラミング、形式的なレイヤリングなど、古い問題を新しい技術で焼き直しただけの状態になることを指しており、川島さんが「ニューレガシーアンチパターン」で詳しく説明されています。 ↩︎

  5. この点については、川島さんの「ドメインモデルの分類学」で示される概念ドメインモデル・仕様ドメインモデル・実装ドメインモデルの区別を意識しました。いきなりシステム用語から考えるのではなく、まず日々のビジネスレイヤーでの会話で使われる言語の意味や定義を厳密に言語化していくことが重要です。哲学を専攻していた身としては、言語の意味や定義を厳密に捉え、定義することがシステム設計に役立つという点が非常に興味深いポイントでした。 ↩︎

  6. 具体的には『ドメイン駆動設計をはじめよう』『ソフトウェアアーキテクチャの基礎』『実践ドメイン駆動設計』などを読み返しました。過去にもドメイン周りの本は当然読んだり、コード改善に活かしたりしていましたが、事業というレイヤーから考えて、その意思決定を実際に担うという立場になってこそ身になるものもあるなと痛感しました。理論と実践、そして責任を伴う判断の場面が揃って初めて、真の理解に繋がるのかもしれません。 ↩︎

  7. PIVOTは「日本をPIVOTする」をミッションに掲げるビジネス映像メディアで、映像コンテンツを日々配信しています。公式サイト: https://pivotmedia.co.jp/ ↩︎

PIVOT Tech Blog

Discussion