Swift 微服务架构师
若后端已采用其他方式构建,则保留其现有结构和约定,除非用户要求更改;仅当后端缺少某部分时,才从本技能中获取相应的模式。
X 代表目标名称中的服务名(如 LoansCore),以及类型名称中的服务名(如 BorrowBookUseCase、LoanService);<Org> 和 <org> 代表组织名。以下是 Bookshelf(一个小型借阅图书馆)的示例:
| 包 | 包含内容 |
|---|---|
bookshelf-identity | 账户、密码、会话;签发所有访问令牌;Valkey 中的重置码;内部供其他服务使用的成员信息 |
bookshelf-catalog | 图书:任何人可读取,管理员可编辑;Loans 服务内部通过它查询图书 |
bookshelf-loans | 成员的借阅记录,多租户表;一个工作流将逾期借阅标记为已逾期并发送通知 |
bookshelf-gateway | 公共 HTTP API,将请求转发给各服务的 gRPC 契约 |
bookshelf-core | BookshelfAuthentication、BookshelfPersistence、BookshelfTransport、BookshelfTesting |
bookshelf-protos | IdentityProtos、CatalogProtos、LoansProtos |
架构形状
客户端 ── HTTPS ──► 边界(TLS 终结) ── HTTP ──► 网关(Hummingbird, OpenAPI)
网关 ── gRPC, mTLS, 调用者的 JWT ──► 每个服务的 XService
服务、工作进程 ── gRPC, mTLS ──► 另一个服务的 XInternalService
服务、工作进程 ── mTLS ──► Temporal 服务 ──► 自己的 Postgres 数据库、Valkey
每个服务及网关都依赖于 <org>-core、<org>-protos 以及 swift-microservices 库(栈)。
服务的包结构
<org>-x
├── XCore 领域模型、错误、仓库协议和命令、端口、用例
├── XPostgres 每个数据库角色的作用域、仓库、语句、迁移
├── XGRPC XService 和 XInternalService、Protobuf 转换、用于 Core 端口的 gRPC 客户端
├── XTemporal 工作流、活动容器、用于 Core 工作流端口的 Temporal 客户端
├── XValkey 用于短生命周期值的仓库
├── X<Provider> 封装 Core 端口后的第三方 API(LoansResend)
└── X 可执行文件:命令、配置默认值、迁移、组合根
服务仅包含其所需的目标,每个目标仅导入其所在行列出的模块。硬性规则:
- XCore 不导入任何驱动、传输层、契约、工作流引擎、配置或生命周期模块(Core 可以链接的内容)。
- 适配器目标之间永不相互导入;可执行文件通过 Core 的协议将一个适配器传递给另一个。
- 生成的契约类型永不离开 XGRPC、网关的 HTTP 目标和可执行文件。
- 只有可执行文件读取配置、构建客户端并运行
ServiceGroup。 - 没有服务读取另一个服务的数据;数据只能通过契约在服务间传递。
职责归属
| 关注点 | 负责人 |
|---|---|
| 业务规则、授权决策、错误的含义 | 用例(位于 XCore);适配器仅进行转换和委托 |
| 服务知晓的另一服务的数据模型 | XCore 中 Ports/ 下的模型,位于返回该模型的端口旁 |
| Protobuf ↔ 领域、HTTP ↔ protobuf | XGRPC 的 Protobuf/ 转换;网关的 Schemas/ |
| 进程间消息;公共 API | <org>-protos;网关的 openapi.yaml |
| 调用者身份 | 网关验证并转发令牌;每个服务再次验证 |
| 哪些进程可以调用内部 RPC | 监听器上的 mTLS:环境中授权机构签名的所有进程 |
| 租户隔离 | 行级安全性;<Org>Persistence 将调用者绑定到每个事务,每个受众的用例以其数据库角色运行 |
| 持久化、延迟或多服务工作 | XTemporal 中的 Temporal 工作流 |
| 每个服务都相同需要的部分 | <org>-core:身份、租户绑定、传输读取器 |
代码风格
代码读起来应像其所实现的业务需求,以 Apple 的 WWDC 示例代码为标杆(风格)。用例和适配器按以下方式映射错误:
do {
… // 工作代码,按需求陈述的顺序
} catch let error as BorrowBookUseCaseError {
throw error
} catch CatalogClientError.unavailable {
logger.warning("借阅失败:目录不可用")
throw .catalogUnavailable
} catch {
logger.error("借阅失败:未知错误", metadata: ["error": "\(String(reflecting: error))"])
throw .unknown
}
- 一个
do块包含直至return的整个工作;每个catch结束自身路径:不要在do之前声明任何供catch填充的变量。 - 构建,再调用:调用所接收的值应是一个命名的
let,绝不允许在参数中直接进行转换、try或await(构建,再调用)。 - 用例、工作流或活动中无辅助方法,无
Result周转、引导标志、try?、catch中的嵌套do或强制解包。 - Swift 6 模式,启用
ExistentialAny、MemberImportVisibility、InternalImportsByDefault和NonisolatedNonsendingByDefault(Swift 设置);在服务的目标之间使用package访问权限。 - 跨越边界的所有内容都必须是
Sendable;不得使用Task {}或Task.detached,仅当并发允许时才使用未检查的并发。
注意事项
- 如果某个组件未包含在
ServiceGroup中,它将永远不会运行:未运行的GRPCClient或PostgresClient永远不会连接,因此其调用将一直等待直到超时。 - 以数据库所有者身份连接的进程会绕过行级安全性,因此每个进程应以其角色连接,只有
serve --migrate-database以所有者身份运行。 - Temporal 仅在运行时检查
Codable:每个工作流和活动负载,以及其中包含的每个类型,都必须是Codable, Sendable(工作流)。 - 工作流代码会被重放:工作流中的
Date.now、UUID()、随机数、Task.sleep或任何 I/O 都会破坏它(确定性)。 - 在自有代码中使用简单的或有作用域的
import Foundation会在 Linux 上链接完整的 Foundation;请使用条件导入FoundationEssentials。
检查清单
将任务的检查清单复制到回复中,并逐项标记完成;每个标明的 .md 文件均位于 references/ 中。
规划后端
- [ ] 每个服务拥有的能力及数据;每项能力对应一个服务(system.md#将系统拆分为服务)
- [ ] 命名每个服务;对照图中的所有模块检查其目标名称
- [ ] 每个服务:按受众划分的 XService RPC、内部使用的 XInternalService RPC 及调用者(无调用循环)、表(租户或非租户)、工作流、短生命周期状态
- [ ] 每个服务:其目标、命令和数据库角色
- [ ] <org>-core 的产品、<org>-protos 的契约、网关按层级划分的路由
- [ ] 构建顺序:<org>-core 和 <org>-protos、令牌签发者、每个服务在其调用者之前、网关
- [ ] 根据 system.md#计划 编写计划;在任何代码编写之前对照硬性规则检查
添加服务
- [ ] <org>-protos:包含 XService 的 XProtos,如果进程需要则包含 XInternalService
- [ ] 根据 packages.md#packageswift-template 创建包 <org>-x,仅包含计划中命名的目标
- [ ] XCore:模型、错误、仓库、端口、每个 RPC 一个用例(use-cases.md)
- [ ] XPostgres:角色、表、行级安全、语句、每个角色一个作用域(persistence.md)
- [ ] XGRPC:基于用例的 proto 服务、转换、端口客户端(grpc.md)
- [ ] 根据需要添加 XTemporal、XValkey、X<Provider>
- [ ] X:命令、InMemoryProvider 默认值、迁移、Serve 和包含工作进程的 Run(composition.md)
- [ ] 网关:公开 RPC 的路由(gateway.md)
- [ ] 每个模块基于 mock 的测试目标(testing.md)
- [ ] 其 CI 工作流、Containerfile 和 Dependabot(delivery.md)
- [ ] swift build 和 swift test;修复并重复直到无警告通过
添加操作
- [ ] 契约:在其受众下的 XService 或 XInternalService 中添加 RPC
- [ ] XCore:用例的五个文件;其作用域仅列出它涉及的内容(use-cases.md)
- [ ] XPostgres:它可能以某些角色运行的作用域需采用该作用域协议
- [ ] XGRPC:处理器和输入的转换,每个错误情况映射到位
- [ ] 可执行文件:用例在其角色的数据库上运行,传递给服务
- [ ] 网关:如果是公共路由,则在其层级中添加路由
- [ ] 测试:基于 mock 验证用例的输出和处理器的状态码
- [ ] swift build 和 swift test;修复并重复直到无警告通过
添加持久化流程
- [ ] XCore:XWorkflowClient 端口;其活动执行的用例,由工作进程作用域采纳
- [ ] XTemporal:工作流、其活动、TemporalXWorkflowClient、命名的 ActivityOptions(workflows.md)
- [ ] 启动它的用例,在其事务提交后,使用确定性 ID
- [ ] 可执行文件:Serve 中的 Temporal 客户端;工作进程组及其 Run 根
- [ ] 测试:基于时间跳跃服务器的工作流,基于 mock 的活动
- [ ] swift build 和 swift test;修复并重复直到无警告通过
设置 CI/CD
- [ ] 包的类别:公共库、系统库或服务(delivery.md#包类别)
- [ ] 其 CI 工作流,引用 swift-microservices/ci 的发布版本提交;dependabot.yml;.license_header_template 和 .licenseignore;机密
- [ ] 如果是库:semver 标签和 .github/release.yml;如果是服务:其 Containerfile
- [ ] 使用 CI 的标记运行 swift build 和 swift test;修复并重复直到通过
审查后端
- [ ] 运行边界检查(packages.md#检查边界)
- [ ] 每个用例:五个文件、类型化抛出、每个 case 只有一个 catch、无辅助方法、正确数据库角色
- [ ] 每个适配器仅做转换和委托;每个错误情况都已映射
- [ ] 每个根:仅在此处读取配置,所有组件按依赖顺序放入 ServiceGroup
- [ ] Swift 设置、FoundationEssentials 导入、包访问权限、命名规范(conventions.md)
- [ ] 严重级别排序的发现:阻塞级(租户隔离、授权、凭据、构建)、主要级(架构形状)、次要级(命名、布局、日志记录),每个发现引用文件及行号或空 grep 结果,附带规则和修复方案;然后是用户决策。仅在被要求时修改代码
- [ ] swift build 和 swift test;报告每个结果,或无法运行的原因
参考资料
根据需要阅读相应的参考资料:
| 参考资料 | 内容 |
|---|---|
| system.md | 拆分为服务、契约、服务间通信、共享包、计划 |
| packages.md | 目标及其导入、Package.swift、Swift 设置、特性与 Foundation、布局、边界检查 |
| stack.md | 库、每个目标链接哪些库、Foundation 链接、选择版本 |
| use-cases.md | 模型、错误、仓库、端口、用例的五个文件和主体、事务、授权 |
| persistence.md | 每个数据库角色的作用域、行级安全、语句、迁移、Valkey |
| grpc.md | 契约、proto 服务、处理器、转换、分页、状态码、客户端、传输 |
| gateway.md | OpenAPI 类型、请求上下文和层级、控制器、Webhook、错误、上游客户端 |
| workflows.md | 启动工作流、负载、确定性、活动和选项、版本、工作进程 |
| composition.md | 命令、组合根、配置、日志记录、ServiceGroup |
| security.md | 信任模型、mTLS、用户令牌、授权层、机密、Webhook |
| style.md | 代码风格:错误处理、并发、导入、Foundation、类型、格式化 |
| testing.md | 测试目标、每个目标验证的内容、mock |
| delivery.md | 按包类别的 CI 工作流、发布、部署、Containerfile、Dependabot |
| conventions.md | 命名包、目标、类型、文件、键或文件夹 |
相关技能
对于架构形状内部的代码实现,当这些技能已安装时加载;若本技能已规定形状,则遵循本技能:
- Actor、任务组、取消、
Sendable诊断、Swift 6 迁移:swift-concurrency - 参数标签、方法名、文档注释:
swift-api-design-guidelines - 用例、处理器和工作流的测试:
swift-testing-expert