C4 Component図:実例で学ぶ完全ガイド
コンポーネント図はC4モデルのレベル3であり、最も評判の悪いレベルです。レベル1(System Context)は描きやすく、めったに変わりません。レベル2(Container)はデプロイするものにきれいに対応します。しかしレベル3は?これは1つのコンテナの内部にズームインするので、開発者がパッケージを再構成するたびに変わります。ほとんどのチームは、これを完全に飛ばすか、一度描いて腐らせるかのどちらかです。
それは残念なことです。なぜなら、よく維持されたコンポーネント図は、コードベースに加わる開発者にとって最も有用な単一の成果物だからです。それは新参者全員が尋ねる問いに答えます。「Xのロジックは実際どこに存在するのか?」
本ガイドは、C4コンポーネント図とは何か、UMLのコンポーネント図とどう違うか、レベル3がメンテナンスコストに見合うのはいつか(そして見合わないのはいつか)、完全な実例、コンポーネント図を無用にする間違い、そしてすべてを手作業でやらずに正確に保つ方法を扱います。
C4モデルそのものが初めての方は、まずC4モデルの完全ガイドから始めて、それからここに戻ってきてください。
C4 Component図とは?
コンポーネント図は1つのコンテナ、つまりコンテナ図の1つのデプロイ可能なユニットを開き、その内部のコンポーネントを明らかにします。
C4の用語では、コンポーネントとは、明確に定義されたインターフェースの背後にある、関連する機能のまとまった集まりです。アプリケーション内部の主要な構成要素を思い浮かべてください。
- HTTPコントローラーまたはハンドラーのグループ
- ビジネスロジックの一部を所有するドメインサービス
- リポジトリまたはデータアクセス層
- 外部APIをラップするクライアント
- ミドルウェア、インターセプター、バックグラウンドワーカー
キーワードはまとまりです。コンポーネントはコードに対する抽象化であり、単一のクラスやファイルではありません。OrderServiceコンポーネントは十数個のファイルにまたがるかもしれませんが、概念的には1つのもの、つまり注文のビジネスロジックを担うコンテナの部分です。用語集の定義が述べるように、C4のコンポーネントは「単一の責務を持つ、より高レベルなコードのまとまり」であり、プログラミング言語のクラスへの1対1のマッピングではありません。
コンポーネント図が答えること
- このコンテナは内部でどう構造化されているか?
- どのコンポーネントがどの責務を所有するか?
- リクエストを処理するためにコンポーネントはどう協調するか?
- データベースや外部システムへの呼び出しはどこから始まるか?
意図的に省くもの
- 個々のクラス、インターフェース、関数(それはレベル4、コード図)
- 他のコンテナの内部
- デプロイとインフラの詳細
1つの図につき1つのコンテナ、コンポーネントのみ。クラスを描いている自分に気づいたら、ズームしすぎです。
C4コンポーネント図 vs UMLコンポーネント図
これは絶えず人々をつまずかせます。なぜなら、UMLにも「コンポーネント図」と呼ばれるものがあり、それは同じものではないからです。
UMLのコンポーネント図は、ソフトウェアコンポーネントを、提供インターフェースと要求インターフェース(有名なロリポップ&ソケット表記)を持つユニットとしてモデル化し、しばしば物理的なパッケージング、アーティファクト、デプロイメント関係を強調します。形式的な表記規則と精密なメタモデルが伴います。
C4のコンポーネント図は、よりゆるく、より実用的です。それは単にズーム階層のレベル3です。コンポーネントを表すボックス、関係を表す矢印、それぞれに短いテキスト説明。学ぶべき特別な表記はありません。価値は階層から来ます。すべてのコンポーネントボックスは特定のコンテナの中に存在し、それは特定のシステムの中に存在します。表記そのものからではありません。
実際には、誰かがC4の文脈で「コンポーネント図」を求めるなら、新しい開発者が2分で読める1つのコンテナ内部の構造マップが欲しいのです。ロリポップインターフェースや<<artifact>>ステレオタイプが欲しいなら、それはUMLを求めているのです。
レベル3を維持する価値があるのはいつか?
ほとんどのガイドが飛ばす正直な答えはこうです。レベル3は、C4モデル全体の中で労力対安定性の比率が最も悪いレベルです。
コンテキスト図は年に数回変わります。コンテナ図はサービスを追加したり削除したりするときに変わります。おそらく月次です。コンポーネント図は、誰かがパッケージをリファクタリングしたり、サービスを抽出したり、モジュールをリネームしたりするたびに変わります。手作業で維持するなら、絶え間ない手入れに申し込むことになり、やめた瞬間に図は嘘をつき始めます。
だから、その労力をどこに費やすかについて意図的になりましょう。
コンポーネント図を作成すべきとき:
- コンテナが本当に複雑なとき。 15個のパッケージ、複数の層、自明でない境界を持つバックエンドAPIはマップに値します。3つのエンドポイントのCRUDサービスは値しません。
- 構造が意図的な設計を体現しているとき。 ヘキサゴナルアーキテクチャ、CQRS、クリーンな層構造を使っているなら、コンポーネント図は意図された構造を明示し、プルリクエストがそれに違反したときに指し示せるものを与えます。
- 複数のチームが同じコンテナに触れるとき。 共有コードには共有のメンタルモデルが必要です。
- オンボーディングがボトルネックのとき。 新しい開発者がコンテナを把握するのに何週間もかかるなら、コンポーネント図は最初の採用で元を取ります。
飛ばすべきとき:
- コンテナが小さく、そのフォルダ構成が自己説明的なとき。
- コンテナがサードパーティのとき(Redisの内部は描きません)。
- 誰も更新し続けることにコミットしないとき。陳腐化したコンポーネント図は何もないより悪いのです。もはや存在しないコードへ、開発者を自信満々に送り込みます。
これがまさに、レベル3がほとんどのチームによって飛ばされるか自動化されるかのいずれかになる理由です。実際のコード構造からコンポーネントを生成し、それに対して検証することは、手描きの図を殺すメンテナンス税を取り除きます。詳細は後述します。
実例:APIコンテナの内部
これを具体的にしましょう。私たちのC4モデルガイドのeコマースプラットフォームを取り上げ、1つのコンテナ、**Order API (Go)**にズームインします。レベル2では1つのボックスです。レベル3では、コンポーネントへと開きます。
コンポーネント
| コンポーネント | 責務 |
|---|---|
| Auth Middleware | 受信リクエストのJWTトークンを検証し、ユーザーのアイデンティティをリクエストコンテキストに注入する |
| Order Controller | 注文の作成、読み取り、キャンセルのためのRESTエンドポイント。リクエスト検証とレスポンスのシリアライズ |
| Admin Controller | バックオフィスの注文管理(返金、手動ステータス変更)のためのRESTエンドポイント |
| Order Service | コアビジネスロジック:注文のライフサイクル、価格ルール、在庫チェック、決済のオーケストレーション |
| Order Repository | データアクセス層。SQLを介して注文を永続化・クエリする |
| Payment Client | Stripe APIをラップ。承認、キャプチャ、返金を処理する |
| Email Adapter | SendGrid経由でトランザクションメール(注文確認、配送更新)を送信する |
| Event Publisher | ドメインイベント(OrderPlaced、OrderCancelled)をKafkaに発行する |
関係
[Auth Middleware] --> [Order Controller] : Passes authenticated requests
[Auth Middleware] --> [Admin Controller] : Passes authenticated requests (admin role)
[Order Controller] --> [Order Service] : Delegates business operations
[Admin Controller] --> [Order Service] : Delegates back-office operations
[Order Service] --> [Order Repository] : Reads/writes orders
[Order Service] --> [Payment Client] : Authorizes and captures payments
[Order Service] --> [Email Adapter] : Triggers transactional emails
[Order Service] --> [Event Publisher] : Emits domain events
[Order Repository] --> [Order Database (PostgreSQL)] : SQL
[Payment Client] --> [Payment Gateway (Stripe)] : HTTPS/REST
[Email Adapter] --> [Email Service (SendGrid)] : HTTPS/REST
[Event Publisher] --> [Message Queue (Kafka)] : Publishes events
データベース、Stripe、SendGrid、Kafkaが端に登場することに注意してください。それらはこのコンテナのコンポーネントではなく、隣接するコンテナと外部システムです。しかし、外向きの呼び出しがどこから始まるかを示すことこそが、図を有用にするものです。
この図が新しい開発者に教えること
30秒で、このチームに加わる開発者は次のことを学びます。
- リクエストパス:ミドルウェア → コントローラー → サービス → リポジトリ。ビジネスロジックが存在する場所はちょうど1つで、それはコントローラーではありません。
- 境界:すべての外部呼び出しは専用のアダプター(
Payment Client、Email Adapter)を通ります。Stripeと通信する必要があるなら、クライアントを拡張します。コントローラーでSDKをインポートしません。 - 副作用:注文はKafka上のイベントとSendGrid経由のメールを生成します。メールが送信されないなら、どの2つのコンポーネントをチェックすればいいか分かります。
- コードを追加する場所:新しい「ギフトカード」機能は明らかに、コントローラー、サービス、そしておそらく新しいクライアントに変更が必要であり、図は従うべきパターンを示します。
その最後のポイントは過小評価されています。コンポーネント図は構造を記述するだけでなく、それを規定するのです。それは貢献者に「このコードベースと一貫している」とはどういうことかを伝えます。
コンポーネント図でよくある間違い
1クラスを1コンポーネントにマッピングする
最も頻繁な間違いです。コンテナに80個のクラスがあり、コンポーネント図に80個のボックスがあるなら、余計なステップを踏んでクラス図を描いたことになります。コンポーネントはまとまりです。OrderController(1つのコンポーネント)は5つのハンドラークラスをカバーするかもしれません。コンテナあたり5〜15個のコンポーネントを目指してください。20を超えるなら、抽象化が細かすぎるか、コンテナがやりすぎているかです。
すべてのコンテナをレベル3でドキュメント化する
対称性は魅力的です。「12個のコンテナがあるから、12個のコンポーネント図が必要だ。」抵抗してください。それらの図のほとんどは決して読まれず、決して更新されません。複雑さが実際に害を及ぼす2つか3つのコンテナをドキュメント化し、残りはフォルダ構成に語らせましょう。
図を腐らせる
レベル3は他のどのレベルよりも速くドリフトします。1月に描かれたコンポーネント図は、6月までにはおそらく存在しないコンテナを記述します。すべてのリファクタリング、抽出されたすべてのパッケージ、リネームされたすべてのモジュールが、ギャップを広げます。そして自信満々に間違った図は、図がないより悪いのです。2四半期前に削除されたコンポーネントを開発者に探させます。メンテナンスを自動化できないなら、せめてそのコンテナのプルリクエストのチェックリストに「コンポーネント図を更新する」を入れてください。
責務のないコンポーネントを描く
説明のないOrderManagerとラベル付けされたボックスはノイズです。すべてのコンポーネントは一行の責務記述を持つべきです(「JWTトークンを検証し、ユーザーのアイデンティティを注入する」)。その文を書けないなら、コンポーネント境界がおそらく間違っています。
構造の代わりに実装の詳細を示す
ジェネリクス、デザインパターン、ヘルパーユーティリティ — どれもレベル3には属しません。コンポーネントの興味深い部分がどう実装されているかなら、それはコード図(あるいは、もっと多くの場合、コードそのもの)の仕事です。
Archylはどうコンポーネント図を正確に保つか
上記すべてが同じ結論を指しています。コンポーネント図は価値がありますが、手作業で維持するのは負け戦です。これこそがArchylが解決するために作られた問題です。
AIディスカバリがコードからコンポーネントを抽出する
コンポーネントを手作業で描く代わりに、リポジトリを接続してAIディスカバリを実行します。Archylはコード構造(パッケージ、モジュール、層、命名規則)を分析し、各コンテナ内のコンポーネントとそれらの間の関係を含むC4モデルを提案します。handlers/、service/、repository/パッケージを持つGoサービスは、上の例で示したまさにその種類のコンポーネント図として返ってきます。提案をレビューし、受け入れるか調整すると、数分でレベル3のモデルが手に入ります。
ドリフト検知が乖離をフラグする
これがレベル3を生かし続ける部分です。Archylはコンポーネントをリポジトリ内の実際のファイルやディレクトリにリンクするので、ドキュメント化されたコンポーネントがコードにまだ存在するかどうかを継続的にチェックできます。誰かがパッケージを削除したり、モジュールをリネームしたり、層を再構成したりすると、ドリフトスコアが下がり、影響を受けたコンポーネントがフラグされます。あなたのコンポーネント図はスナップショットであることをやめ、監視される契約になります。
Architecture as Code
ディスカバリより明示的な定義を好むなら、ArchylはC4モデル(システム、コンテナ、コンポーネント、関係)をリポジトリ内に存在するYAMLで定義することをサポートします。コンポーネント図はバージョン管理され、プルリクエストでレビューされ、自動的にレンダリングされます。ドリフト検知と組み合わせれば、コードのメンテナンス特性を持つレベル3ドキュメントが得られます。
FAQ
C4コンポーネント図とUMLコンポーネント図の違いは何ですか?
UMLコンポーネント図は、提供/要求インターフェース、アーティファクト、ステレオタイプを持つ形式的な表記で、コンポーネントをパッケージ化されたユニットとしてモデル化することに焦点を当てます。C4コンポーネント図は、C4ズーム階層のレベル3です。1つの特定のコンテナ内部のコンポーネントの、実用的なボックス&矢印のビューです。C4には形式的な表記要件がありません。その価値は、一貫した階層(システム → コンテナ → コンポーネント → コード)から来るのであって、記号からではありません。
コンポーネントはクラスと同じですか?
いいえ。C4のコンポーネントは、明確に定義されたインターフェースの背後にあるまとまったコードの集まりです。1つのクラス、十数個のクラス、パッケージ、モジュールとして実装されるかもしれません。コンポーネント図が1クラスにつき1ボックスなら、1つ深いズームレベルに行きすぎています。
すべてのコンテナにコンポーネント図が必要ですか?
いいえ。そして、すべてのコンテナをレベル3でドキュメント化しようとすることは、C4を放棄する最速の方法の1つです。複雑な、チームをまたいで共有される、またはオンボーディングの中心となるコンテナにのみコンポーネント図を作成してください。残りには、コンテナ図と読みやすいフォルダ構成で十分です。
コンポーネント図にはコンポーネントがいくつあるべきですか?
おおよそ5〜15個です。5未満なら、図はおそらくコンテナ図が示さなかった何かを教えていません。20を超えるなら、コンポーネント境界が細かすぎるか、コンテナ自体を分割すべきかのどちらかです。
正確であり続けるコンポーネント図が欲しいですか?Archylを無料で試して、数分でコードベースからコンポーネントを含むC4モデルを生成しましょう。あるいは読み進めてください:C4モデルとは?完全ガイド | C4コンテナ図ガイド | C4コード図ガイド | 用語集のコンポーネント図。