mcpField
A
architecture-design

architecture-design

github.com/swiftyjourney開発66 回インストール
リポジトリ

architecture-design について

保守性とテスト容易性を備えた設計を支援する開発者向けスキルです。要件分析から SOLID、クリーンアーキテクチャ、テスト計画、ADR 作成までの5段階で進め、一般的な指針と Swift/iOS 固有のパターンを扱います。

主な機能

  1. 015段階のアーキテクチャ設計
  2. 02SOLID とクリーンアーキテクチャの指針
  3. 03レイヤーと依存境界の設計
  4. 04ユニット・統合テスト戦略
  5. 05アーキテクチャ決定記録(ADR)
  6. 06Swift/iOS の並行処理パターン

活用シーン

  1. 01機能をコンポーネントと責務に分割
  2. 02コードベースのレイヤーと依存関係を計画
  3. 03各レイヤーのテスト範囲を定義
  4. 04ADR で設計判断を記録

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/swiftyjourney/architecture-design-skill --skill architecture-design

インストール後、Claude Code で/architecture-designを入力して読み込まれたか確認します。

詳細

アーキテクチャと設計原則のスキル

Clean Architecture の原則、SOLID 設計パターン、Essential Developer の手法に基づく業界のベストプラクティスを活用して、コードベースを保守しやすく、テストしやすく、スケーラブルなアーキテクチャへと変革します。

概要

このスキルでは、次の 5 ステップのプロセスを通じて、以下を実施します。

  1. 要件を分析する → コンポーネント、責務、境界を特定する
  2. SOLID 原則を適用する → SRP、OCP、LSP、ISP、DIP に基づいて設計する
  3. Clean Architecture を定義する → レイヤー、境界、依存関係のルールを確立する
  4. テスト戦略を設計する → ユニットテスト、統合テスト、テスト境界を計画する
  5. 決定事項を文書化する → アーキテクチャ決定記録(ADR)を作成する

中核となる考え方

「優れたアーキテクチャは、優れたチームプロセスとコミュニケーションの副産物です」

このスキルは、次の原則に従います。

  • フレームワークからの独立性 - ビジネスロジックはフレームワークに依存しません
  • テスト容易性 - アーキテクチャによってテストが容易になります
  • UI からの独立性 - ビジネスルールに影響を与えずに UI を変更できます
  • データベースからの独立性 - ビジネスルールはデータベースを認識しません
  • 外部エージェンシーからの独立性 - ビジネスルールは外部サービスに依存しません

5 ステップのプロセス

ステップ 1:要件を分析する

目的:コンポーネント、責務、アーキテクチャ上の境界を特定する

アクション:

  1. 機能を個別の責務に分解する
  2. 中核となるビジネスロジックとインフラストラクチャ上の関心事を特定する
  3. 横断的関心事(ロギング、分析、キャッシュ)を認識する
  4. システム内のデータフローをマッピングする
  5. アーキテクチャ上の境界となり得る箇所を特定する

確認すべき主な質問:

  • 中核となるビジネスロジックは何ですか?
  • 外部依存関係(ネットワーク、データベース、UI)には何がありますか?
  • 単独でテスト可能にする必要があるものは何ですか?
  • どのコンポーネントが個別に変更される可能性がありますか?
  • 各コンポーネントの入力と出力は何ですか?

出力:責務が明確なコンポーネント図

参考:コンポーネント特定のパターンについては、references/modular_design.md を参照してください。


ステップ 2:SOLID 原則を適用する

目的:SOLID 原則に従ってコンポーネントを設計する

S - 単一責任の原則(SRP)

各クラス/モジュールが変更される理由は 1 つだけです。ビジネスロジックとインフラストラクチャを分離します。

  • ❌ データの読み込みと表示の両方を行うクラス
  • ✅ ビジネスロジック用の UseCase と、ビューのロジック用の Presenter を分ける
O - オープン・クローズドの原則(OCP)

プロトコル/インターフェースを通じて拡張に開き、変更に閉じます。

// Open for extension via protocol (Swift example — same idea in any language)
protocol ItemLoader {
    func load() async throws -> [Item]
}

// Closed for modification — implementations extend behavior
final class RemoteItemLoader: ItemLoader { ... }
final class CachedItemLoader: ItemLoader { ... }
final class FallbackItemLoader: ItemLoader { ... }
L - リスコフの置換原則(LSP)

サブタイプは基底型と置き換え可能でなければなりません。すべての実装が契約を守る必要があります。

I - インターフェース分離の原則(ISP)

クライアントは、使用しないインターフェースに依存すべきではありません。目的を絞った具体的なプロトコルを作成します。

  • ❌ protocol DataStore { func save(_:); func load(); func delete(); func migrate(); func backup() }
  • ✅ protocol ItemReader { func retrieve() throws -> [Item]? } と、別の ItemCache
D - 依存関係逆転の原則(DIP)

上位モジュールは具象ではなく、抽象に依存します。

class ItemListViewController {
    private let loader: ItemLoader  // depends on abstraction, not concrete type
    init(loader: ItemLoader) { self.loader = loader }
}

出力:SOLID に準拠したコンポーネント設計

参考:詳しいパターンとアンチパターンについては、references/solid_principles.md を参照してください。


ステップ 3:クリーンアーキテクチャを定義する

目的:明確なアーキテクチャレイヤーを確立し、適切な依存関係の流れを実現します

クリーンアーキテクチャのレイヤー:

┌─────────────────────────────────────────┐
│          Presentation Layer             │
│    (UI, ViewModels, Presenters)        │
└─────────────────┬───────────────────────┘
                  │ depends on
┌─────────────────▼───────────────────────┐
│         Domain/Business Layer           │
│      (Use Cases, Entities, Rules)      │
└─────────────────┬───────────────────────┘
                  │ depends on
┌─────────────────▼───────────────────────┐
│         Infrastructure Layer            │
│   (Network, Database, Framework Code)  │
└─────────────────────────────────────────┘

依存関係のルール:ソースコードの依存関係は内側にのみ向けます

主なパターン:

  1. ユースケース — ビジネスルールを含み、データフローを調整します。UI やフレームワークには依存しません
  2. 境界(プロトコル) — レイヤー間の契約を定義し、テスト可能性を高めます
  3. アダプター — レイヤー間のデータを変換し、境界プロトコルを実装します
  4. コンポジションルート — 依存関係を結び付け、オブジェクトグラフを構成します

成果物:明確な境界を持つレイヤー化アーキテクチャ

参考:レイヤーの詳細な定義については references/clean_architecture.md を、言語に依存しない例については examples/generic/layered_architecture.md を、Swift/iOS の実装例については examples/swift/ を参照してください


ステップ 4:テスト戦略を設計する

目的:すべてのアーキテクチャレイヤーを対象とする包括的なテストを計画します

テストピラミッド:

        ┌──────────┐
        │    UI    │ Few - End to End
        ├──────────┤
        │Integration│ Some - Integration
        ├──────────┤
        │   Unit   │ Many - Fast & Isolated
        └──────────┘

テスト境界:

  1. ドメインレイヤー(単体テスト) — ユースケースを単独でテストし、すべての依存関係をモックします
  2. インフラストラクチャレイヤー(統合テスト) — 実際の依存関係を使用してアダプターをテストします
  3. プレゼンテーションレイヤー(単体テスト) — プレゼンター/ビューモデルをテストし、ユースケースをモックします

テストダブルの用語:

  • スタブ:あらかじめ用意した応答を返します
  • スパイ:検証用に呼び出しを記録します
  • モック:振る舞いに関する期待を検証します
  • フェイク:テスト用の動作する実装です

テスト戦略:

  • 実装の詳細ではなく、振る舞いをテストします
  • ネイティブの非同期テストプリミティブを使用します。言語が await をサポートしている場合は、手動のコールバック同期を避けます
  • ファクトリーヘルパーパターン:テスト対象システム(SUT)とその依存関係を makeSUT() ヘルパーでまとめて作成し、個々のテストメソッドをすっきり保ちます
  • Arrange、Act、Assert の構成を使用し、明確さのためにヘルパーを抽出します

成果物:包括的なテスト戦略ドキュメント

参考:パターンとベストプラクティスについては references/testing_strategies.md を参照してください


ステップ 5:決定を文書化する

目的:明確なアーキテクチャドキュメントと決定記録を作成します

ADR の形式:

# ADR-NNN: <Decision Title>
## Status: Accepted | Superseded | Deprecated
## Context: Why this decision was needed
## Decision: What was decided
## Consequences: Positive and negative outcomes
## Alternatives Considered: What was rejected and why

ADR の完全な例については examples/generic/ を参照してください。

ドキュメントの要件:

  1. アーキテクチャの概要 — システムコンテキスト図、コンポーネント図、レイヤー間の関係
  2. コンポーネントのドキュメント — 目的、依存関係、使用例
  3. 使用する設計パターン — 使用するパターンとその理由、トレードオフ
  4. テスト戦略 — テスト対象とその方法

成果物:完全なアーキテクチャドキュメント

参考:詳細なドキュメントパターンについては references/ を参照してください


ベストプラクティス

すべきこと ✅
  • ビジネスロジックをインフラストラクチャから分離します
  • 具象ではなく抽象に依存します
  • イニシャライザーインジェクションを通じて依存関係を明示します
  • すべてのビジネスロジックに対するテストを作成します
  • ネットワーク/ネットワーク境界では非同期 IO プロトコルを、プロセス内ストレージでは同期プロトコルを使用します
  • 言語の並行処理プリミティブを使って、プレゼンテーション型をメインスレッド/UI スレッドに隔離します
  • 最初からテスト可能性を考慮して設計します
すべきでないこと ❌
  • ビジネスロジックをフレームワークに依存させる
  • 依存関係の管理にシングルトンを使う
  • プレゼンテーションとビジネスロジックを混在させる
  • 複数の責務を持つ神クラスを作成する
  • 言語ネイティブの async/await が使用できる場合に、手動のコールバック/完了ハンドラーパターンを使う
  • UI スレッドへ手動でディスパッチする — 代わりに構造化並行処理を使用します

Swift/iOS: ネットワーク境界では async throws を、キャッシュ境界では同期的な throws を使用し、プレゼンテーション型には @MainActor を、アクター境界を越える値型には Sendable を使用します。以下の Swift 並行処理のセクションを参照してください。


一般的なアーキテクチャパターン

  1. クリーンアーキテクチャ(推奨)— 関心を明確に分離し、依存関係のルールは内向きのみとします
  2. ヘキサゴナルアーキテクチャ(ポート&アダプター) — ビジネスロジックを中心に置き、ポートで境界を定義します
  3. MVVM — UI とロジックを分離し、テスト可能なビューモデルを使用します
  4. MVC — 従来型の分離を行い、コントローラーをコンポジションルートとします

参考:言語に依存しないレイヤードアーキテクチャの例は examples/generic/layered_architecture.md を、Swift/iOS における MVVM+Coordinator、コンポジションルート、2 レイヤービューについては examples/swift/ を参照してください。


言語固有のガイダンス

Swift/iOS
  • ネットワーク境界には async throws プロトコルを使用します
  • キャッシュ/ストア境界には同期の throws を使用し、Scheduler プロトコル経由で非同期に橋渡しします
  • プレゼンテーション型(ResourceView、LoadResourcePresenter)に @MainActor を適用します
  • ドメインの値型に Sendable を指定します(例:FeedImage: Hashable, Sendable)
  • 汎用プレゼンター LoadResourcePresenter<Resource, View> を使用します — すべての機能で 1 つの型を使用します
  • WeakRefVirtualProxy<T> を使用して、型ごとの定型コードを増やさずに保持サイクルを解消します
  • SceneDelegate にコンポジションルートパターンを適用し、インフラストラクチャを @MainActor サービスオブジェクトに切り出します(examples/swift/composition_root.md を参照)

参考:Swift 固有のパターンについては examples/swift/ を参照してください。

汎用/言語非依存
  • SOLID 原則をあらゆる場面に適用します
  • 言語に応じてインターフェース/トレイト/プロトコルを使用します
  • 言語の機能に合わせてパターンを適応させます
  • クリーンアーキテクチャのレイヤーを維持します

参考:言語に依存しない例については examples/generic/ を参照してください。


Swift の並行処理 (Swift/iOS のみ)

このセクションの概念(非同期 I/O 境界、メインスレッドの分離、値型のスレッド安全性、汎用プレゼンター、メモリーセーフなプロキシ)は、あらゆる場面に適用できます。以下の構文と API は Swift 固有です。詳細な実装については references/concurrency_patterns.md を参照してください。

非同期プロトコル境界

ネットワークプロトコルには async throws を、ストアプロトコルには同期の throws を使用します。

// Network boundary — async because IO is inherently async
public protocol HTTPClient {
    func get(from url: URL) async throws -> (Data, HTTPURLResponse)
}

// Cache/store boundary — sync because the store runs on its own queue (bridged via Scheduler)
// Apply this pattern to any persistent store: CoreData, SQLite, in-memory, Realm, etc.
public protocol ItemStore {
    func delete() throws
    func insert(_ items: [LocalItem], timestamp: Date) throws
    func retrieve() throws -> CachedItems?
}
// Essential Feed uses FeedStore with identical structure for FeedImage caching
@MainActor による分離

プレゼンテーション層の型は @MainActor です — 手動でスレッドをディスパッチする必要はありません。

@MainActor public protocol ResourceView {
    associatedtype ResourceViewModel
    func display(_ viewModel: ResourceViewModel)
}

@MainActor public final class LoadResourcePresenter<Resource, View: ResourceView> {
    public typealias Mapper = (Resource) throws -> View.ResourceViewModel
    public init(resourceView: View, loadingView: ResourceLoadingView,
                errorView: ResourceErrorView, mapper: @escaping Mapper)
    public init(resourceView: View, loadingView: ResourceLoadingView,
                errorView: ResourceErrorView) where Resource == View.ResourceViewModel
    public func didStartLoading()
    public func didFinishLoading(with resource: Resource)
    public func didFinishLoading(with error: Error)
}
Sendable 値型

アクター境界を越えるドメインエンティティには Sendable が必要です。保存されるすべてのプロパティ自体も Sendable でなければならず、コンパイラーがこれを検証します。

// Generic pattern — apply to any domain entity
public struct Item: Hashable, Sendable {
    public let id: UUID
    public let title: String
    // String, UUID, URL are all Sendable — compiler is satisfied
}

// Concrete example from Essential Feed:
// public struct FeedImage: Hashable, Sendable { ... }
WeakRefVirtualProxy

条件付き適合を備えた単一の汎用型を通じて、保持サイクルを防ぎます。

final class WeakRefVirtualProxy<T: AnyObject> {
    private weak var object: T?
    init(_ object: T) { self.object = object }
}

extension WeakRefVirtualProxy: ResourceErrorView where T: ResourceErrorView {
    func display(_ viewModel: ResourceErrorViewModel) { object?.display(viewModel) }
}

extension WeakRefVirtualProxy: ResourceLoadingView where T: ResourceLoadingView {
    func display(_ viewModel: ResourceLoadingViewModel) { object?.display(viewModel) }
}
Scheduler プロトコル

ストアを非同期にすることなく、同期の FeedStore を非同期コンテキストに橋渡しします。

protocol Scheduler {
    @MainActor
    func schedule<T>(_ action: @escaping @Sendable () throws -> T) async rethrows -> T
}

参考:実装の全体と移行ガイドについては references/concurrency_patterns.md を参照してください。


要件エンジニアリングとの統合

  1. 要件 → ユースケース → BDD シナリオから始めます
  2. このスキルを適用 → アーキテクチャ → コンポーネント設計
  3. 実装 → アーキテクチャパターンに従います
  4. テスト → 定義したテスト戦略を使用します

参考資料

references/ 内:

  • clean_architecture.md - クリーンアーキテクチャのレイヤーとルール
  • solid_principles.md - 例を交えた SOLID の詳細な説明
  • design_patterns.md - 一般的なパターン(アダプター、デコレーター、コンポジット、ヌルオブジェクトなど)
  • null_object_pattern.md - テスト例を交えたヌルオブジェクトパターン
  • command_query_separation.md - キャッシュ設計における CQS 原則
  • dependency_management.md - DI パターンと戦略
  • testing_strategies.md - テストパターンとベストプラクティス
  • modular_design.md - モジュールの構成と境界
  • concurrency_patterns.md - Swift の並行処理:async/await、@MainActor、Sendable、Scheduler

examples/ 内:

  • swift/ - Essential Feed(Swift 6)の実際の実装
  • generic/ - 言語に依存しない例

出力形式

このスキルを適用する際は、次の内容を提示してください。

  1. コンポーネント分析 - 特定したコンポーネントとその責務
  2. SOLID のレビュー - 適用した原則とその根拠
  3. アーキテクチャ図 - レイヤーと依存関係(Mermaid)
  4. テスト戦略 - テスト構成とカバレッジ計画
  5. ADR - 文書化した主要な意思決定
  6. 実装ガイド - リファクタリングまたは実装の段階的な計画

クレジット

Essential Developer の実績あるアーキテクチャ手法に基づいています。


バージョン履歴

  • 2.1.0 - 複数プロジェクトでの利用に向けて汎用化:ドメインに依存しない SOLID の例を追加し、Swift 固有のガイダンスを分離し、スコープラベルを追加し、汎用 ADR テンプレートを作成して、汎用アーキテクチャの例を更新
  • 2.0.0 - Swift 6 への移行:async/await、@MainActor、Sendable、LoadResourcePresenter、Scheduler、WeakRefVirtualProxy
  • 1.0.0 - 5 ステップのプロセスで初回リリース

コメント