C4 Code図(レベル4):必要なときと不要なとき - Archyl Blog

C4コード図(レベル4)は、コンポーネント内部のクラス、インターフェース、関数を示します。ほとんどのチームはこれを飛ばすべきですが、常にそうとは限りません。本ガイドでは、C4モデルのレベル4が価値を生むのはいつか、UMLクラス図が労力の無駄になるのはいつか、そして自動生成がトレードオフをどう変えるかを扱います。

C4 Code図(レベル4):必要なときと不要なとき

Code図は、C4モデルの最も誤解されているレベルです。ほとんどのチームが飛ばすレベルであり、Simon Brown自身がオプションだと述べるレベルでありながら、最も多くの質問を生むレベルでもあります。「本当にクラスを描く必要があるのか?」「これってただのUMLじゃないの?」「コードが変わったとき誰がこれを維持するのか?」

短い答え:ほとんどの場合、C4コード図は必要ありません。より長い答えはもっと興味深いものです。なぜなら、レベル4が実際に報われるケースは、まさにそれがないとチームが最も多くの時間を失うケースだからです。密なアルゴリズム、規制された領域、そしてもはや誰も完全には理解していないコードへのオンボーディングです。

本ガイドは、C4コード図が実際に何を示すか、手描きがほぼ常に間違いである理由、C4モデルのレベル4を持つ価値がある具体的な状況、そして自動生成がコストとベネフィットの計算をどう完全に逆転させるかを扱います。

C4モデルが初めての方は、まずC4モデルの完全ガイドから始めてください。この記事は4つのレベルを知っていることを前提としています。

C4 Code図とは?

Code図はC4モデルのレベル4であり、最も深いズームレベルです。コンポーネント図(レベル3)がコンテナ内部の主要な構成要素を示すのに対し、Code図は1つのコンポーネントにズームインし、その実装レベルの詳細を示します。

  • クラスとその関係(継承、コンポジション、依存)
  • インターフェースとそれを実装する型
  • 主要なシグネチャを含む関数とメソッド
  • エンティティ、値オブジェクト、DTOといったデータ構造

実際には、C4のレベル4図はたいていUMLクラス図かER図としてレンダリングされます。C4モデルはこのレベルの表記を規定しません。クラスを描く問題は何十年も前に解決されたので、既存の標準に明示的に委ねるのです。

Codeレベルを定義する2つの性質があります。

  1. スコープ:1つのコンポーネント。 Code図はコンテナ全体、ましてやシステムにまたがることは決してありません。ちょうど1つのコンポーネント(OrderRepositoryPaymentService)を開き、その内部を示します。
  2. 粒度:実際のコードシンボル。 Code図上のすべてのボックスは、ソース内の実際のクラス、インターフェース、関数に対応します。抽象化は残っていません。これは、図とコードが同じものであるべきレベルです。

その2つ目の性質こそが、レベル4がしばしば飛ばされる理由です。後で見ていきます。(簡単な参照は用語集のコード図の項目をご覧ください。)

Simon Brown自身の推奨:描くな

C4モデルに初めて出会う人々を驚かせる部分がここにあります。その作成者は、Code図を手作業で作成しないよう推奨しています。Simon Brownのガイダンスは、レベル4はオプションであり、それが必要なときは、手作業で描いて維持するのではなく、ソースコードからオンデマンドで生成すべきだというものです。IDEによって、ドキュメントツールによって、あるいは実際のコードを読む何かによって。

その理由は反論しがたいものです。

  • コードは絶えず変わる。 手描きのクラス図は、誰かが次のプルリクエストをマージするのにかかる時間ほどしか正確ではありません。すべてのリネーム、抽出されたすべてのメソッド、すべての新しいフィールドが、それを少しずつ間違いにします。
  • 情報はすでに存在する。 誰の頭の中にも、どの単一のファイルにも存在しない決定を捉えるコンテナ図とは異なり、Code図はソースコードがすでに正確に述べていることを複製します。
  • ツールの方が上手にやる。 モダンなIDE(IntelliJ、Visual Studioなど)は、数秒でコードからクラス図を生成します。それらは描かれるのではなく導出されるので、常に正確です。

したがって、C4を採用するどのチームのデフォルトの立場もこうあるべきです。レベル1〜3をモデル化し、レベル4は必要なときに生成する。 コンテキスト、コンテナ、コンポーネントの図は、コードにない知識を捉えます。Codeレベルはコードそのものです。

C4コード図が実際に価値があるとき

「たいてい飛ばす」は「常に飛ばす」ではありません。コードレベルのビューがドキュメントに居場所を得る、現実の状況があります。

1. 複雑なアルゴリズムと込み入った設計

一部のコンポーネントは、ファイルを上から下まで読んでも本当に追いにくいロジックを実装します。連鎖したストラテジーを持つ価格エンジン、注文ライフサイクルを統括する状態機械、深いビジターの階層を持つパーサーなどです。コードの構造こそが洞察であるとき、つまりどのクラスがどれに委譲するかを理解することが戦いのすべてであるとき、Code図は何時間ものコード読みを1枚の絵に圧縮します。

判断基準:シニアエンジニアが別のシニアエンジニアにそのコンポーネントを説明するのにホワイトボードが必要なら、そのホワイトボードのスケッチは、捉えられるのを待っているCode図です。

2. 規制され監査される領域

金融、医療、航空、その他の規制された産業では、監査人や審査官がしばしば実装レベルのドキュメントを要求します。どのクラスがカード会員データを扱うか、暗号化はどこで適用されるか、監査証跡はどう書かれるか。ここでレベル4の図はあれば嬉しいものではなく、証拠です。同じことが、特定のクラスを通るデータフローが重要なセキュリティレビューや脅威モデリングにも当てはまります。

3. 密で長命なコードへのオンボーディング

何年もの蓄積された決定を持つレガシーコンポーネントは、新入社員にとって過酷です。コードベースの中で最も恐ろしい3つか4つのコンポーネント、つまり暗黙知が集中しているものについてのCode図は、オンボーディング時間を劇的に短縮できます。新しい開発者は、8,000行に飛び込む前にコンポーネントの形を見ます。

4. 公開契約とデザインパターンのドキュメント化

コンポーネントが他のチームが構築する対象となるAPI面を公開している場合、または構造そのものがドキュメントであるパターン(Strategy、Observer、ヘキサゴナルのポート&アダプター)を実装している場合、コードレベルのビューは契約を明示します。

必要ないとき(ほとんどの場合がそうです)

逆のケースについて正直になりましょう。なぜなら、それらはどんなシステムでもコンポーネントの大多数をカバーするからです。

  • コードが読みやすい。 よく命名され、明確なパッケージを持つコンポーネントに図は必要ありません。フォルダツリーがです。
  • レベル3がすでに答えている。 「注文サービスは決済とどう通信するか?」と尋ねられたなら、それはコンポーネント図の問いです。クラスを描くのはシグナルではなくノイズを加えます。
  • コンポーネントが単純なCRUDである。 ハンドラー、サービス、リポジトリ、モデル。誰もがこの形を千回見たことがあります。それをクラスレベルでドキュメント化しても、何もドキュメント化しません。
  • 誰も求めていない。 ドキュメントは人々が実際に持つ問いに答えるべきです。誰もコンポーネントのクラスレベルのビューを必要としたことがないなら、それを作るのは塩漬けの成果物に費やされた労力です。

より広いC4アプローチからの指針原則がここに全力で当てはまります。作成するすべての図は、維持しなければならない図です。レベル1〜3では、メンテナンスコストは他のどこにも存在しない知識を買います。レベル4では、それはたいてい、ソースコードの少しきれいで少し陳腐化したコピーを買うだけです。

自動生成が方程式をどう変えるか

上記すべては、誰かが図を描いて維持しなければならないことを前提としています。その前提こそが、Simon Brownの「飛ばすか生成するか」の助言を正しい判断にしたものです。手作業のレベル4のコストが、ベネフィットを正当化することはほとんどありませんでした。

自動化はその方程式のコスト側を取り除きます。ArchylのAIディスカバリは、接続されたリポジトリを分析し、コードレベルの要素(クラス、インターフェース、関数)をソースから直接抽出し、C4モデルの適切なコンポーネントにそれらを結びつけます。開発者が作図ツールでPaymentServiceとその協力者を描く代わりに、モデルはコードベースに実際に存在するものから構築され、それが由来するリポジトリにつながったままになります。

これは問いを「レベル4は維持する価値があるか?」から「レベル4は持つ価値があるか?」へと変えます。はるかにクリアしやすいハードルです。コードレベルのビューが手描きではなく生成・更新されるとき:

  • それは決して嘘をつきません。誰かのソースの記憶からではなく、ソースから導出されるからです。
  • それはコンテキストの中でナビゲート可能です。システムからコンテナ、コンポーネント、コードへと1つのモデルでドリルダウンでき、wikiの中のPNGを探し回ることがありません。
  • 「コストにもかかわらず価値があった」ケース(監査、オンボーディング、複雑なコンポーネント)は、当たり前に価値があるものになり、限界的なケースは無料になります。

レベル4でどのコンポーネントが注目に値するかについては、依然として判断を働かせます。些細なCRUDコンポーネントの生成されたビューは、やはりノイズです。しかし失敗モードは「陳腐化した図がチームを誤解させる」から「コストゼロの使われないビュー」へと移ります。

実例:決済処理コンポーネントの内部

これを具体的にしましょう。あなたのコンポーネント図(レベル3)が、バックエンドAPIコンテナ内にPaymentProcessorコンポーネントを示しているとします。レベル4でそれにズームインすると、次のようなものが明らかになるかもしれません。

[PaymentService (interface)]
  ├── ProcessPayment(order, method) -> PaymentResult
  └── RefundPayment(paymentID, amount) -> RefundResult

[StripePaymentService] ──implements──> [PaymentService]
[StripePaymentService] ──uses──> [StripeAdapter] : wraps the Stripe SDK
[StripePaymentService] ──uses──> [RetryPolicy] : exponential backoff, 3 attempts
[StripePaymentService] ──uses──> [PaymentRepository] : persists payment records

[StripeAdapter] ──maps──> [PaymentResult] : translates Stripe responses to domain types
[RetryPolicy] ──raises──> [PaymentFailedError] : after exhausting retries
[PaymentRepository] ──persists──> [Payment (entity)]

このビューがレベル3にはできない形で答えることに注目してください。

  • テストの継ぎ目はどこか? PaymentServiceはインターフェースです。テストはStripeに触れずにフェイクを差し替えられます。
  • 2つ目のプロバイダーはどこに差し込まれるか? 同じインターフェースを実装するPaypalPaymentService。構造が拡張ポイントを明らかにします。
  • 障害時に何が起きるか? RetryPolicyクラスがバックオフ動作を所有します。PaymentFailedErrorがエスカレーションのパスです。「課金が失敗したら何が起きるか?」と尋ねる監査人は、図から答えを得ます。
  • 何がデータベースに触れるか? PaymentRepositoryだけです。カード会員データのフローが追跡可能であり、これはPCIスコープのコンポーネントで非常に重要です。

これはレベル4が元を取るコンポーネントです。金融的に機微で、障害処理が自明でなく、複数のクラスが意図的なパターンで協調します。たとえば、プロフィールレコードを読み書きするUserProfileHandlerと対比してください。それをクラスレベルで描いても、ファイルツリーが示さないことは何も教えてくれません。

C4レベル4でよくある間違い

クラス図を手作業で維持する

古典的な失敗です。意欲的なエンジニアがコードベース全体の美しいクラス図を描き、6ヶ月後にはもはや存在しないコードベースを記述し、いまや積極的に誤解を招きます。Code図がソースから生成されない、あるいは少なくとも定期的に再生成されないなら、それを描かれた日に期限切れとして扱ってください。

ゲッター、セッター、ボイラープレートをドキュメント化する

すべてのアクセサ、コンストラクタのオーバーロード、ユーティリティメソッドを列挙するCode図は、縮尺1:1の地図と同じ問題を抱えます。設計意図を運ぶ要素(インターフェース、主要な協調、契約を定義するメソッド)を含め、儀式を省いてください。生成されたビューは、ダンプではなくフィルタされるべきです。

レベル3で十分なところにレベル4を使う

「コード図」が「コントローラーがサービスを呼び、サービスがリポジトリを呼ぶ」のようなものを示しているなら、クラス形のボックスでコンポーネント図を描いたのです。レベル3に畳み直してください。レベル4は、内部構造こそが興味深い部分であるコンポーネントのために取っておきましょう。

コンテナ全体をクラスレベルで描く

サービス全体にまたがる200クラスの図はC4階層に違反し、いずれにせよ判読不能です。1つのCode図は1つのコンポーネントをカバーします。それほど厳密にスコープできないなら、レベル3図のコンポーネント境界をおそらく再考する必要があります。

「完全な」ドキュメントのためにレベル4を必須として扱う

一部のチームはチェックリスト的な心構えでC4を採用します。4つのレベルだから、4セットの図。結果は労力の無駄と、この実践全体への反感です。C4はレベル4がオプションであると明言しています。よく維持された3つのレベルは、腐っていく4つに毎回勝ります。

FAQ

C4のレベル4はただのUMLクラス図ですか?

ほとんどそうです。そしてそれは設計どおりです。C4モデルはCodeレベルのために独自の表記を定義しません。既存のものの再利用を推奨し、UMLクラス図が最も一般的な選択です(データ中心のコンポーネントにはER図が機能します)。C4が加えるのはスコープとコンテキストです。クラス図はちょうど1つのコンポーネントを記述し、そのコンポーネントはコンテナとシステムの図のナビゲート可能な階層の中に存在します。スタンドアロンのUMLクラス図は宙に浮きます。C4のコード図には住所があります。

すべてのコンポーネントにコード図を作成すべきですか?

いいえ。ほとんどのコンポーネントは、ソースコードが最良のドキュメントであるほど単純です。レベル4は、アルゴリズム的に複雑、セキュリティまたはコンプライアンスに機微、あるいは悪名高いオンボーディングの障害物であるコンポーネントのために取っておいてください。ドキュメントが自動生成されるなら、より多くのカバレッジを持つコストは下がりますが、注意力は依然として有限なので、何を表に出すかを厳選してください。

コード図をどう最新に保てばいいですか?

手作業で維持しないことによってです。生成してください。IDEからオンデマンドで、CIのドキュメントツールから、あるいはAIディスカバリ中にリポジトリからクラス、インターフェース、関数を抽出し、コードレベルをコンポーネントモデルに結びつけたまま保つArchylのようなプラットフォームから。リファクタリング後に人間がクラス図を更新するのを覚えていることに頼るプロセスは、どれも失敗します。

C4におけるコンポーネントとコード要素の違いは何ですか?

コンポーネント(レベル3)は、単一の責務を持つ論理的なまとまり(「決済を扱う部分」)であり、複数のクラスやファイルにまたがるかもしれません。コード要素(レベル4)は、ソース内の実際のシンボルです。特定のクラス、インターフェース、関数です。コンポーネントはあなたが選ぶ抽象化であり、コード要素はコンパイラが強制する事実です。

結論

C4コード図は、デフォルトでは飛ばし、意図的に投入すべきレベルです。レベル1〜3は他のどこにも存在しないアーキテクチャの知識を捉えます。レベル4はコードがすでに述べていることを映すので、構造そのものが難しい部分であるとき(複雑なアルゴリズム、規制されたコンポーネント、密なレガシーコード)にのみ、そして理想的には描かれるのではなく生成されるときにのみ、居場所を得ます。

メンテナンス税なしでコードレベルのビューが欲しいなら、リポジトリをArchylに接続し、AIディスカバリにクラス、インターフェース、関数をC4モデルに自動的に抽出させてください。必要なときに深さが手に入り、誰もクラス図を描き直す必要は二度とありません。


1つ上のレベルに行きたいですか?C4コンポーネント図ガイドを読むか、C4モデルの完全ガイドで復習するか、C4モデルの概要を探索してください。試す準備はできましたか?Archylを無料で始めましょう