mcpField
A
architecture-design

architecture-design

github.com/swiftyjourney开发66 次安装
代码仓库

关于 architecture-design

面向开发者的架构设计 Skill,采用五步流程,从需求分析、SOLID 与整洁架构设计,到测试规划和 ADR 文档。内容兼有通用指导及 Swift/iOS 专属模式。

主要功能

  1. 01五步架构设计流程
  2. 02SOLID 与整洁架构指导
  3. 03分层及依赖边界设计
  4. 04单元与集成测试策略
  5. 05架构决策记录(ADR)
  6. 06Swift/iOS 并发模式

适用场景

  1. 01拆分功能并明确组件职责
  2. 02规划代码库的架构层与依赖
  3. 03界定各层的测试范围
  4. 04用 ADR 记录设计决策

安装

复制下面的命令,在终端中运行。

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

安装后在 Claude Code 中输入/architecture-design确认已加载。

详细介绍

架构与设计原则技能

运用整洁架构原则、SOLID 设计模式以及 Essential Developer 方法论中的行业最佳实践,将代码库转变为易于维护、测试和扩展的架构。

概述

此技能将引导你完成一个结构化的 5 步流程:

  1. 分析需求 → 确定组件、职责和边界
  2. 应用 SOLID 原则 → 运用 SRP、OCP、LSP、ISP、DIP 进行设计
  3. 定义整洁架构 → 建立分层、边界和依赖规则
  4. 设计测试策略 → 规划单元测试、集成测试和测试边界
  5. 记录决策 → 创建架构决策记录(ADR)

核心理念

“良好的架构是良好的团队流程和沟通的副产品”

此技能遵循以下原则:

  • 框架独立性 - 业务逻辑不依赖框架
  • 可测试性 - 架构便于进行测试
  • UI 独立性 - UI 可以更改,而不影响业务规则
  • 数据库独立性 - 业务规则不依赖数据库
  • 外部服务独立性 - 业务规则不依赖外部服务

5 步流程

第 1 步:分析需求

目标:确定组件、职责和架构边界

操作:

  1. 将功能拆分为不同的职责
  2. 区分核心业务逻辑和基础设施相关内容
  3. 识别横切关注点(日志记录、分析、缓存)
  4. 梳理系统中的数据流
  5. 确定潜在的架构边界

要提出的关键问题:

  • 核心业务逻辑是什么?
  • 有哪些外部依赖(网络、数据库、UI)?
  • 哪些内容需要能够独立测试?
  • 哪些组件可能独立更改?
  • 每个组件的输入和输出是什么?

输出:职责明确的组件图

参考:有关组件识别模式,请参阅 references/modular_design.md


第 2 步:应用 SOLID 原则

目标:遵循 SOLID 原则设计组件

S - 单一职责原则(SRP)

每个类/模块只有一个更改理由。将业务逻辑与基础设施分离。

  • ❌ 一个既加载数据又呈现数据的类
  • ✅ 使用独立的 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 中的语言无关示例;以及 examples/swift/ 中的 Swift/iOS 实际实现


步骤 4:设计测试策略

目标:规划涵盖所有架构层的全面测试

测试金字塔:

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

测试边界:

  1. 领域层(单元测试)— 隔离测试用例,模拟所有依赖项
  2. 基础设施层(集成测试)— 使用真实依赖项测试适配器
  3. 表现层(单元测试)— 测试 presenter/view model,模拟用例

测试替身术语:

  • 桩:提供预设的答案
  • 间谍:记录调用以供验证
  • 模拟对象:验证行为预期
  • 伪对象:用于测试的可运行实现

测试策略:

  • 测试行为,而非实现细节
  • 使用原生异步测试原语;当语言支持 await 时,避免手动同步回调
  • 工厂辅助函数模式:通过 makeSUT() 辅助函数一并创建被测系统(SUT)及其依赖项,让各个测试方法保持简洁
  • 采用 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,对跨越 actor 边界的值类型使用 Sendable。请参阅下文的 Swift 并发部分。


常见架构模式

  1. 整洁架构(推荐)——职责分离清晰,依赖规则:只能向内依赖
  2. 六边形架构(端口与适配器)——业务逻辑位于中心,端口定义边界
  3. MVVM——分离 UI 和逻辑,视图模型可测试
  4. MVC——传统的分层方式,由控制器充当组合根

参考:参见 examples/generic/layered_architecture.md,了解与语言无关的分层示例;参见 examples/swift/,了解 Swift/iOS 中的 MVVM+Coordinator、组合根和双层视图


特定语言指南

Swift/iOS
  • 网络边界使用 async throws 协议
  • 缓存/存储边界使用同步 throws;通过 Scheduler 协议桥接到异步
  • 对表示层类型(ResourceView、LoadResourcePresenter)应用 @MainActor
  • 将领域值类型标记为 Sendable(例如:FeedImage: Hashable, Sendable)
  • 使用 LoadResourcePresenter<Resource, View> 泛型呈现器——一个类型适用于所有功能
  • 使用 WeakRefVirtualProxy<T> 打破强引用循环,避免为每种类型编写样板代码
  • 在 SceneDelegate 中应用组合根模式;将基础设施提取到 @MainActor 服务对象中(参见 examples/swift/composition_root.md)

参考:参见 examples/swift/,了解 Swift 特定模式

通用/与语言无关
  • 普遍应用 SOLID 原则
  • 根据语言使用接口/trait/协议
  • 根据语言特性调整模式
  • 保持整洁架构分层

参考:参见 examples/generic/,了解与语言无关的示例


Swift 并发 (仅限 Swift/iOS)

本节中的概念——异步 IO 边界、主线程隔离、值类型线程安全、泛型呈现器、内存安全代理——普遍适用。以下语法和 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 值类型

跨越 actor 边界的领域实体必须是 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 步流程

评论

支持 Markdown