モノリスからマイクロサービス、モジュラーモノリスへの変遷を示す概念図

Goで実装するバックエンドのモジュラーモノリス構成

1. はじめに 私が担当しているサービスのバックエンドでは モジュラーモノリス を採用しています。本記事では、採用に至った判断軸、実際の構成、運用してみての所感を整理します。 以降の説明は、例として架空の オンラインレッスンプラットフォーム を題材に進めます。クライアントとしてはユーザー向けサイト・講師向けサイト・カスタマーサポート向けサイトを想定します。会員・講師・ポイント・レッスン予約・シフト・レビューといった共通のドメイン領域を、これら複数のクライアントが共有するサービスを思い浮かべてください。 クライアントごとに独立したバックエンドを並べる構成では、各バックエンドで類似の実装が増えがちです。ドメインの中核(会員管理、ポイント、受講履歴など)は本質的に1つしかないため、修正のたびに複数のリポジトリへ改修を加える必要があります。マイクロサービスとモジュラーモノリスの両方を比較した結果、最終的にモジュラーモノリスを採用する判断に至りました。 対象読者 複数のクライアント・チームから利用される中規模のバックエンドを設計し直そうとしている方 マイクロサービス化を検討しているが、本当に必要かを判断したい方 Goの go.work / go.mod でモジュール境界をどう引くかに興味がある方 TL;DR 対象は「ドメインは1つ、クライアントは複数」の構成。マイクロサービスのメリットより、単一トランザクションで処理できる利点のほうが大きかった 「業務モジュール群」と「クライアント別バックエンド」を分離した。業務モジュールは1つのリポジトリ(以降「コアモジュール群リポジトリ」と呼ぶ)の中で account / lesson / point / system などのサブモジュールに分けた。各バックエンドはそれらをGoの require 経由で取り込む モジュール単位で go.mod を切り、go.work で開発時だけ束ねる。本番ビルドではバージョン付きモジュールを取り込むので、依存方向と境界が物理的に強制される 将来マイクロサービス化したくなったら、モジュール単体を切り出しやすい構造にしておく、というのが導入時の合意事項 2. 当時の状況と課題 今回のアーキテクチャ刷新は、運用中のサービスを動かしながら行ったわけではなく、新規サービスの初期設計・実装を進めている途中で方針転換したものです。最初は従来どおり「クライアントごとに独立したバックエンドを並べる」構成で書き始めていました。しかし検討が深まるにつれて「このままリリースすると技術負債が制御できなくなる」と判断し、設計を全面的に見直しました。リリース後に修正するよりも、構築段階で構造を変えるほうが長期的に安いという見積もりです。 書き始めていた構成を具体的に示すと、次のとおりです。 ユーザー向けサイト用バックエンド・講師向けサイト用バックエンド・カスタマーサポート向けサイト用バックエンドがそれぞれ独立したリポジトリ 共通DBを直接参照する 共通処理は社内パッケージとして切り出していたが、ビジネスロジック層は各リポジトリ内に重複 この構成では、次のような状況が起きていました。 同じドメインルールが複数箇所に散らばる。「ポイント消費時の残高検証ロジック」のように、ユーザー向けサイトとカスタマーサポート向けサイトの双方から呼び出される処理が両リポジトリで似て非なる実装になりがち 修正の波及がレビューしきれない。テーブル定義を変えると、3〜4リポジトリでマイグレーションと修正をセットで進める必要がある トランザクション境界が曖昧。同じテーブルを別アプリから書き込むため、整合性は実質的にアプリ側の実装規律に依存 「サービスを分ける」方向に振るか、「中身を共通化する」方向に振るかをここで決める必要がありました。 3. マイクロサービスを比較対象として置いた 最初に検討したのはマイクロサービス化です。会員・講師・ポイント・レッスンをそれぞれ独立したサービスにして、gRPC経由で通信させる構成です。 マイクロサービスのメリット(私たちのケースで) 責任が分割されるので各サービスのソースコードはシンプルに保てる 将来、特定ドメインだけスケールアウトしたい場合に独立してスケーリングできる マイクロサービスのデメリット(私たちのケースで) 単一トランザクションで処理できない。ポイント残高・予約・受講履歴など同時に整合していないと厳しい業務が多く、課金系で部分失敗が起きた場合の運用設計をすべて自前で用意する必要がある ユーザー名やニックネームでの横断検索が一気に難しくなる。カスタマーサポートから「この名前で会員と講師を横断検索したい」というニーズは日常的にあり、サービス境界をまたいで実装するコストが大きい proto 定義に思いのほか時間がかかる。共通protoリポジトリにpush → 各サービスの go.mod を更新 → 反映、という流れがユースケース追加のたびに発生する そもそも私たちは複数チームで大規模な並列開発をするほどの規模ではない。マイクロサービス本来のメリットである「組織のスケール」が享受しにくい 「単純なモノリス」では戻したくない理由 一方で「全部1つの大きなアプリに戻す」という選択も適切ではありません。前述のとおり、アーキテクチャ刷新前の状態はまさに「重複のあるモノリス的運用」で、ドメインの境界が曖昧なまま規模だけ大きくなることの痛みは身をもって知っていたからです。 その中間として現実的なのは、1つのデプロイ単位の中で内部を明確に分割するモジュラーモノリスです。 4. モジュラーモノリスを選んだ判断軸 §2で挙げた課題と、本節で照らし合わせる観点は次のように対応します。 ...

2026年5月18日 · 読了時間: 5分 · 瀬戸敏文