CQRS与事件溯源在Swift中的应用:复杂项目的架构

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么传统架构无法应对现代挑战?

\n\n

您是否曾遇到过这样的情况:在Swift应用中简单地添加一个新功能却变成了一场噩梦?Bug层出不穷,数据丢失,逻辑膨胀到难以置信的程度?熟悉吗?欢迎来到一个CQRS和事件溯源成为您最好朋友的世界。

\n\n

这些模式不仅仅是时髦的词汇。它们是一种思维方式,从根本上改变了构建Swift应用的方法。今天,我们将通过实际案例来剖析它们。没有废话,没有晦涩的理论——只有实践。

\n\n

什么是CQRS?分而治之

\n\n

CQRS(命令查询职责分离)是一个原则,它告诉我们:“别再在同一个类中混用读和写操作”。听起来简单,但影响深远。

\n\n

在经典的CRUD(创建、读取、更新、删除)架构中,您对所有操作使用同一个模型。例如,User模型。它既用于保存,也用于读取和更新。但如果读取需要某些字段,而写入需要其他字段呢?于是开始出现各种权宜之计:可选字段、标志位、映射……

\n\n

CQRS建议分离职责:

\n
    \n
  • 命令(Commands)——改变状态。它们修改数据,但不返回任何内容。
  • \n
  • 查询(Queries)——读取数据。它们不改变状态,只返回结果。
  • \n
\n\n

在Swift中,这看起来可能是这样的:

\n\n
// 命令——仅用于修改\nstruct CreateUserCommand {\n    let id: UUID\n    let name: String\n    let email: String\n}\n\n// 查询——仅用于读取\nstruct GetUserQuery {\n    let userId: UUID\n}\n\n// 命令处理器\nclass CreateUserCommandHandler {\n    func handle(_ command: CreateUserCommand) {\n        // 创建用户的逻辑\n        print("用户已创建:\\(command.name)")\n    }\n}\n\n// 查询处理器\nclass GetUserQueryHandler {\n    func handle(_ query: GetUserQuery) -> User? {\n        // 获取用户的逻辑\n        return User(id: query.userId, name: "阿列克谢", email: "alex@example.com")\n    }\n}
\n\n

看到了吗?没有任何混淆。每个类只做一件事。这简化了测试、维护和扩展。

\n\n

事件溯源:存储历史,而非状态

\n\n

现在我们来看事件溯源——一种模式,它不存储对象的当前状态,而是存储导致该状态的所有事件序列。

\n\n

想象一个银行账户。与其存储余额(1000卢布),不如存储事件列表:“开户”、“存入500”、“取出200”、“存入700”。要了解当前余额,您只需重放所有事件。这带来了难以置信的可能性:审计、回滚更改、数据分析。

\n\n

在Swift中实现这一点:

\n\n
// 事件\nprotocol Event {}\n\nstruct AccountCreated: Event {\n    let accountId: UUID\n    let initialBalance: Double\n}\n\nstruct MoneyDeposited: Event {\n    let accountId: UUID\n    let amount: Double\n}\n\nstruct MoneyWithdrawn: Event {\n    let accountId: UUID\n    let amount: Double\n}\n\n// 聚合——从事件中恢复状态\nclass BankAccount {\n    private(set) var balance: Double = 0\n    private(set) var isActive: Bool = false\n    private(set) var accountId: UUID?\n\n    func apply(_ event: Event) {\n        switch event {\n        case let created as AccountCreated:\n            accountId = created.accountId\n            balance = created.initialBalance\n            isActive = true\n        case let deposited as MoneyDeposited:\n            balance += deposited.amount\n        case let withdrawn as MoneyWithdrawn:\n            balance -= withdrawn.amount\n        default:\n            break\n        }\n    }\n\n    // 从事件历史中恢复\n    static func load(from history: [Event]) -> BankAccount {\n        l                

博客

书籍推荐