Architecture as Code:システム設計をプログラマティックに定義する - Archyl Blog

Architecture as Codeは、Infrastructure-as-Codeが運用にもたらしたのと同じ厳密さをアーキテクチャにもたらします。このガイドでは、AaCとは何か、なぜビジュアルのみのアプローチより優れているか、YAMLやDSLの実例、バージョン管理ワークフロー、CI/CD統合、そしてArchylがそれをどう実用化するかを解説します。

Architecture as Code:システム設計をプログラマティックに定義する

ソフトウェアエンジニアリングには、数年ごとに繰り返されるパターンがあります。手動でビジュアルだった実践がコード化され、バージョン管理され、自動化される -- そしてすべてが良くなります。

インフラで起きました。クラウドコンソールのクリック操作からTerraformファイルの作成へ。設定で起きました。サーバー上の設定ファイルの編集からKubernetesマニフェストでの望ましい状態の宣言へ。データベーススキーマで起きました。手動でSQLスクリプトを実行することからマイグレーションファイルの作成へ。

そして今、アーキテクチャドキュメンテーションで起きています。Architecture as Codeは、システム設計をプログラマティックに定義する実践です。つまり、アプリケーションコードと同じパイプラインを通じてバージョン管理、レビュー、テスト、デプロイできる構造化されたテキストファイルで定義します。

このガイドでは、Architecture as Codeについて知るべきすべてを解説します。それは何か、なぜ重要か、ビジュアルのみのアプローチとの比較、そして実践での実装方法です。

Architecture as Codeとは何か?

Architecture as Code(AaC)は、ソフトウェアアーキテクチャを機械可読で人間が書けるテキストファイルで定義する実践です。ビジュアルツールでボックスと矢印を描く代わりに、YAML、JSON、または専用のDSLのような構造化フォーマットでシステム、コンテナ、コンポーネント、それらのリレーションシップを記述します。

YAMLで定義されたアーキテクチャのシンプルな例を示します。

version: "1.0"

project:
  name: "Payment Platform"
  description: "組織のすべての決済処理を担当"

systems:
  - name: Payment Platform
    type: software_system
    description: "コア決済処理システム"
    containers:
      - name: Payment API
        type: api
        description: "決済操作のためのREST API"
        technologies: [Go, gRPC, OpenAPI]
      - name: Payment Processor
        type: service
        description: "決済トランザクションの処理"
        technologies: [Go]
      - name: Transaction Database
        type: database
        description: "トランザクション記録の保存"
        technologies: [PostgreSQL]
      - name: Payment Queue
        type: queue
        description: "非同期決済処理キュー"
        technologies: [Kafka]

  - name: Stripe
    type: external_system
    description: "サードパーティ決済ゲートウェイ"

relationships:
  - from: Payment API
    to: Payment Processor
    label: "Forwards payment requests"
    type: uses
  - from: Payment Processor
    to: Transaction Database
    label: "Persists transactions"
    type: writes_to
  - from: Payment Processor
    to: Payment Queue
    label: "Publishes payment events"
    type: publishes_to
  - from: Payment Processor
    to: Stripe
    label: "Charges cards via"
    type: uses

このファイルがアーキテクチャの完全な唯一の真実の源です。Archylのようなツールがこれを読み取り、C4モデルを構築し、インタラクティブな図をレンダリングし、すべてを同期します。ファイルはGitリポジトリ内に、記述するコードのすぐ隣に存在します。

ビジュアルのみのアプローチが不十分な理由

Architecture as Code以前は、チームは通常ビジュアルツール(Lucidchart、draw.io、Miro、Figma)を使用してアーキテクチャを文書化していました。これらのツールはブレインストーミングや初期設計セッションには優れていますが、長期的なドキュメンテーションとしては根本的な限界があります。

バージョン管理がない

ビジュアルな図はバイナリまたはプロプライエタリなファイルとして保存され、意味のある差分ができません。誰かが図を変更した場合、変更があったことはわかりますが、何が変わったかはわかりません。draw.ioファイルに対する git diff に相当するものはありません。コード変更をレビューするように、プルリクエストで図の変更をレビューすることはできません。

Architecture as Codeでは、すべての変更はテキスト差分です。新しいサービスの追加は数行のYAMLです。コンポーネントの名前変更は1行の変更です。レビュアーは何が変わったか、なぜ変わったか(コミットメッセージから)を正確に確認し、承認または修正を要求できます。

自動化がない

ビジュアルな図は孤立して存在します。アクションをトリガーしたり、ルールを検証したり、CI/CDパイプラインと統合したりすることができません。図が10のサービスがあると示していてもKubernetesクラスターが12を実行している場合、その不一致を検出するものは何もありません。

Architecture as Codeは自動化を可能にします。アーキテクチャ定義を実際のインフラに対してチェックする検証ルールを書くことができます。アーキテクチャファイルからドキュメンテーションを生成できます。アーキテクチャファイルが現実と乖離した時にアラートをトリガーできます。

スケールでのコラボレーションがない

2人が同じビジュアルな図を同時に編集すると、コンフリクトは通常、一方の変更がもう一方を上書きすることで解決されます。ビジュアルファイルにはマージ戦略がありません。

Architecture as Codeでは、標準的なGitマージワークフローが適用されます。2つのチームがアーキテクチャファイルの異なる部分を変更でき、Gitがクリーンにマージします。コンフリクトが発生した場合、コードのコンフリクトと同じ方法で解決されます。議論と意図的な解決を通じてです。

一貫性の保証がない

ビジュアルな図には何でも含めることができます。ボックスのラベルが不一致になることがあります。矢印は同じ図の異なる部分で異なることを意味する場合があります。スキーマ、検証、命名規則の強制がありません。

Architecture as Codeファイルにはスキーマがあります。ツールは変更ごとにファイルを検証します。存在しないコンテナを参照した場合、検証がキャッチします。無効なリレーションシップタイプを使用した場合、変更がマージされる前にフラグが立てられます。

ロックインとポータビリティ

ビジュアルな図は作成したツールにロックインされがちです。LucidchartからDraw.ioに移行するには、すべての図を手動で再作成する必要があります。あるArchitecture as Codeツールから別のツールへの移行はフォーマット変換です -- 自動化され、再現可能です。

Architecture as Codeの利点

唯一の真実の源

アーキテクチャが単一のファイル(またはファイルセット)で定義されている場合、参照する場所はまさに一つです。どの図が最新か、どのConfluenceページに最新バージョンがあるか、先月誰かがメールしたPDFがまだ正確かどうかという疑問はありません。

アーキテクチャ変更のコードレビュー

これはおそらく最も変革的な利点です。アーキテクチャの変更がプルリクエストを通じて行われると、コード変更と同じ精査を受けます。シニアアーキテクトは提案されたサービス分割を実行前にレビューできます。チームは新しい依存関係が導入される前にその影響を議論できます。

+ - name: Notification Service
+   type: service
+   description: "メール、SMS、プッシュ通知を処理"
+   technologies: [Python, Celery, Redis]
+
+ - from: Order Service
+   to: Notification Service
+   label: "Triggers order notifications"
+   type: uses

この差分は明確なストーリーを伝えます。誰かがNotification Serviceを追加し、Order Serviceに接続しています。レビュアーは質問をしたり、代替技術を提案したり、異なるサービス境界を提案できます -- すべてアプリケーションコードの1行が書かれる前にです。

Git履歴がアーキテクチャ履歴

アーキテクチャファイルへのすべてのコミットが、アーキテクチャがどのように進化したかの永続的な記録を作ります。以下のような質問に答えることができます。

  • Search Serviceはいつ追加されたか?
  • MySQLからPostgreSQLへの移行を誰が承認したか?
  • 6ヶ月前のアーキテクチャはどのようなものだったか?
  • サービスの数は時間とともにどのように増加したか?

この履歴はシステムの進化を理解し、新しいチームメンバーをオンボーディングするために非常に価値があります。

CI/CD統合

Architecture as Codeは継続的インテグレーションと継続的デプロイメントパイプラインに自然に統合されます。すべてのプルリクエストで以下が可能です。

  • アーキテクチャファイルをそのスキーマに対して検証する
  • 適合性ルールをチェックする(例:すべてのサービスに文書化されたオーナーが必要)
  • 更新された図を生成する
  • 文書化されたアーキテクチャと稼働中のシステム間のドリフトを検出する
  • アーキテクチャをドキュメンテーションプラットフォームにパブリッシュする

これにより、アーキテクチャドキュメンテーションは劣化する静的文書ではなく、生きた成果物になります。

リファクタリングと自動化

アーキテクチャ定義は構造化データであるため、それを操作するスクリプトを書くことができます。すべてのリレーションシップにわたってサービスの名前を変更する必要がある場合、YAMLファイルでの単純な検索と置換です。PostgreSQLを使用するすべてのサービスのレポートが必要な場合、YAMLをパースして技術でフィルタリングします。命名規則を強制する必要がある場合、リンターを書きます。

Architecture as Codeのフォーマットとdsl

アーキテクチャをコードとして定義するためのいくつかのフォーマットとDSLが存在します。最も一般的なアプローチの概要を示します。

Structurizr DSL

Simon Brown(C4モデルの作成者)によって作られたStructurizr DSLは、最も初期のArchitecture as Codeフォーマットの一つです。カスタムDSL構文を使用します。

workspace {
    model {
        user = person "User"
        softwareSystem = softwareSystem "My Software System" {
            webapp = container "Web Application" "Delivers content" "Java"
            database = container "Database" "Stores data" "PostgreSQL"
        }
        user -> webapp "Uses"
        webapp -> database "Reads from and writes to"
    }
    views {
        systemContext softwareSystem {
            include *
            autolayout lr
        }
    }
}

StructurizrはC4モデルのためのArchitecture as Codeの概念を開拓しました。ただし、カスタムDSL構文には学習曲線があり、レンダリングにはStructurizr固有のツールが必要です。

YAMLベースのアプローチ

YAMLはDevOpsにおける宣言的な設定のデファクトスタンダードになっています(Kubernetes、Docker Compose、GitHub Actions、Terraform HCLを含む)。アーキテクチャ定義にYAMLを使用することには、親しみやすさという利点があります。ほとんどの開発者は既にYAMLの読み書きを知っています。

Archylの archyl.yaml フォーマットはこのアプローチを採用しています。

version: "1.0"

systems:
  - name: E-Commerce Platform
    type: software_system
    containers:
      - name: Web Frontend
        type: webapp
        technologies: [React, TypeScript, Next.js]
      - name: API Service
        type: api
        technologies: [Go, gRPC]
        components:
          - name: Auth Handler
            type: handler
            technologies: [JWT, OAuth2]
          - name: Product Handler
            type: handler
            technologies: [REST]
      - name: Product Database
        type: database
        technologies: [PostgreSQL]

relationships:
  - from: Web Frontend
    to: API Service
    label: "Makes API calls to"
  - from: API Service
    to: Product Database
    label: "Reads/writes product data"

ネスティングはC4階層を直接反映しています。システムにはコンテナが含まれ、コンテナにはコンポーネントが含まれます。リレーションシップは曖昧さを解消するためのドット記法で人間が読める名前を使用します。フォーマットはgrepで検索可能、差分で比較可能で、読むために専門的なツールを必要としません。

JSONとその他のフォーマット

一部のツールはJSON、TOML、その他の構造化フォーマットを使用します。具体的なフォーマットは原則よりも重要ではありません。アーキテクチャ定義はテキストベースで、バージョン管理可能で、機械でパース可能であるべきです。

Architecture as Codeの実装:実践的ワークフロー

チームにArchitecture as Codeを導入するためのステップバイステップのワークフローを示します。

ステップ1:既存のものから始める

初日にアーキテクチャ全体を文書化しようとしないでください。Container図(サービスランドスケープ)から始めます。すべてのデプロイ可能なサービス、その技術スタック、サービス間の主要なリレーションシップをリストアップします。

Archylを使用する場合、UIでビジュアルにモデルを作成してから archyl.yaml としてエクスポートするか、ゼロからYAMLファイルを書くことができます。どちらのパスでも同じ結果になります。

ステップ2:リポジトリにコミットする

アーキテクチャファイルをプライマリリポジトリのルート(またはコードベースが多くのリポジトリに分割されている場合は専用のアーキテクチャリポジトリ)に配置します。場所は原則よりも重要ではありません。ファイルはGitに存在し、コードレビューを通過すべきです。

my-platform/
  archyl.yaml        # アーキテクチャ定義
  src/
  docker-compose.yml
  .github/
    workflows/
      architecture.yml  # アーキテクチャ用CIパイプライン

ステップ3:CI/CD同期を設定する

メインブランチへのマージごとにアーキテクチャファイルをArchylと同期するCI/CDパイプラインを設定します。これにより、Archylのビジュアルな図とインタラクティブなドキュメンテーションが常に最新のコミット済みアーキテクチャを反映します。

GitHub Actionsワークフローは以下のようになります。

name: Sync Architecture

on:
  push:
    branches: [main]
    paths: [archyl.yaml]

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Sync to Archyl
        run: |
          curl -X POST https://api.archyl.com/v1/sync \
            -H "Authorization: Bearer ${{ secrets.ARCHYL_TOKEN }}" \
            -H "Content-Type: application/yaml" \
            --data-binary @archyl.yaml

ステップ4:プルリクエストを通じてアーキテクチャ変更を行う

この時点から、アーキテクチャの変更はコード変更と同じワークフローに従います。

  1. ブランチを作成する
  2. archyl.yaml ファイルを変更する
  3. プルリクエストを開く
  4. チームにレビューしてもらう
  5. メインにマージする
  6. CI/CDが変更をArchylに同期する

これにより、アーキテクチャの変更にコード変更と同じ可視性、説明責任、追跡可能性が与えられます。

ステップ5:適合性ルールを追加する

Architecture as Codeの実践が成熟するにつれ、アーキテクチャ定義を自動的に検証する適合性ルールを追加します。例:

  • すべてのコンテナに少なくとも1つの技術が指定されている
  • すべての外部システムに説明がある
  • 孤立したコンテナがない(すべてのコンテナが少なくとも1つのリレーションシップに参加している)
  • 命名規則に従っている(例:サービスは「Service」で終わる)

Archylの適合性ルールエンジンはこれらのルールを自動的に評価し、CIパイプラインまたはArchylダッシュボードで違反を報告できます。

ステップ6:定義を時間とともに進化させる

システムとコンテナから始めます。特定のサービスが内部ドキュメンテーションを正当化するほど複雑になった時にコンポーネントを追加します。重要なアーキテクチャの意思決定を行った時にADRを追加します。サービス境界を正式化する際にAPIコントラクトを追加します。

アーキテクチャファイルはシステムとともに有機的に成長します。すべての詳細を前もって用意する必要はありません。

Architecture as Code vs Infrastructure as Code

Architecture as CodeとInfrastructure as Code(IaC)は補完的ですが異なる実践です。

Infrastructure as Code(Terraform、Pulumi、CloudFormation)は何をデプロイし、どう設定するかを定義します。運用的です。サーバーのプロビジョニング、ネットワークの設定、クラウドリソースの管理を行います。

Architecture as Codeはシステムがどのように見え、その部品がどのように関連するかを定義します。記述的です。概念的な構造、技術の選択、サービス境界を文書化します。

理想的なセットアップは両方を組み合わせます。

  • Terraformファイルがインフラを定義する
  • archyl.yaml がアーキテクチャを定義する
  • 適合性ルールが両者の整合性を保つ

Terraformが新しいサービスを追加したがアーキテクチャファイルにはそれが記載されていない場合、ドリフト検出が不一致をキャッチします。

Architecture as CodeとAIアシスタント

Architecture as Codeの最も説得力のある利点の一つは、AIアシスタントがそれを読んで推論できることです。アーキテクチャが構造化テキストで定義されている場合、Claude CodeやCursorのようなツールは以下が可能です。

  • YAMLファイルをクエリしてアーキテクチャに関する質問に答える
  • 現在の状態に基づいてアーキテクチャの変更を提案する
  • 文書化されたアーキテクチャを尊重するコードを生成する(例:適切なサービスに適切なデータベースを使用する)
  • コードとアーキテクチャ定義の間の不整合を検出する

ArchylはMCPサーバーでさらに一歩進めます。AIアシスタントは単にアーキテクチャファイルを読むだけでなく、ライブのアーキテクチャモデルをクエリし、リレーションシップを辿り、変更を提案することもできます。アーキテクチャは静的文書ではなく、プログラマティックでクエリ可能なデータソースになります。

よくある落とし穴

フォーマットの過剰設計

YAMLや既存のフォーマットで機能する場合、カスタムDSLを設計しないでください。目標は採用であり、フォーマットが馴染みのあるものであれば採用は容易です。ほとんどの開発者はDocker Compose、Kubernetes、CI/CD設定からYAMLを既に知っています。

すべてを捉えようとする

Architecture as Codeはシステムの構造的な側面を捉えるべきです。何が存在し、どう接続し、どの技術が使用されているかです。運用の詳細(スケーリングポリシーなど)、ランタイム設定(環境変数など)、振る舞いの仕様(APIレスポンスフォーマットなど)をアーキテクチャファイルに埋め込もうとしないでください。

ワークフローを強制しない

Architecture as Codeは変更が定義されたワークフローを通じて行われる場合にのみ機能します。アーキテクチャファイルをバイパスしてビジュアルツールで直接変更を行う人がいると、ファイルが古くなります。どちらの方向が正規であるかについて明確な慣例を確立してください。

ビジュアル出力を無視する

Architecture as Codeはビジュアルな図の代替ではありません。それらを生成するより良い方法です。テキストファイルが真実の源ですが、レンダリングされた図が人々が日常的に実際に見るものです。ビジュアル出力がアクセス可能で、最新で、ナビゲートしやすいことを確認してください。

Archylで始める

ArchylはArchitecture as Codeをサポートするためにゼロから設計されています。プラットフォームは以下を提供します。

  • システム、コンテナ、コンポーネント、リレーションシップ、テクノロジーをカバーする完全なC4モデルのためのYAMLベースのDSL
  • UIでビジュアルにモデル化してYAMLにエクスポート、またはYAMLを書いてUIに同期する双方向同期
  • すべてのコミットでの自動同期のためのCI/CD統合
  • アーキテクチャ定義を標準に対して検証する適合性ルール
  • AIアシスタントからアーキテクチャをクエリ可能にするMCPサーバー
  • コードレビュー、コメント、チームオーナーシップを備えたコラボレーション機能

ゼロから始める場合でもビジュアルな図から移行する場合でも、Archylはあらゆる規模のチームにArchitecture as Codeを実用的にします。

Architecture as Codeを始めましょう。インフラとアプリケーションコードに既に適用している厳密さをアーキテクチャドキュメンテーションにもたらしてください。