RailsEventStore核心概念:事件溯源(Event Sourcing)实践指南

RailsEventStore核心概念:事件溯源(Event Sourcing)实践指南

什么是事件溯源

事件溯源(Event Sourcing)是一种架构模式,它将应用程序状态的变化记录为一系列不可变的事件。与传统CRUD模式不同,事件溯源不直接修改当前状态,而是通过存储和重放事件来重建状态。

RailsEventStore提供了在Ruby on Rails应用中实现事件溯源的完整工具集,其中AggregateRoot模块是构建领域模型的核心组件。

基础配置

在开始使用AggregateRoot之前,需要配置默认的事件存储客户端:

AggregateRoot.configure do |config|
  config.default_event_store = RailsEventStore::Client.new
  # 或者使用Rails配置中的事件存储
  # config.default_event_store = Rails.configuration.event_store
end

这个配置会作为AggregateRoot::Repository的默认事件存储,除非在构造Repository时显式指定其他事件存储。

领域模型构建

创建聚合根

聚合根是领域模型中的核心对象,它负责维护业务规则和一致性边界。创建一个聚合根只需包含AggregateRoot模块:

class Order
  include AggregateRoot
  
  # 业务逻辑将在这里实现
end

定义领域事件

领域事件表示系统中发生的重要业务事实。在RailsEventStore中,事件是简单的Ruby类:

class OrderSubmitted < RailsEventStore::Event; end
class OrderExpired < RailsEventStore::Event; end

每个事件可以携带相关数据,这些数据会被存储在事件的data属性中。

实现业务逻辑

状态变更与事件应用

聚合根通过应用(apply)事件来改变其内部状态。下面是一个完整的订单聚合根实现示例:

class Order
  include AggregateRoot
  
  # 自定义业务异常
  class HasBeenAlreadySubmitted < StandardError; end
  class HasExpired < StandardError; end

  def initialize
    @state = :new  # 初始状态
  end

  # 提交订单业务方法
  def submit
    # 业务规则校验
    raise HasBeenAlreadySubmitted if state == :submitted
    raise HasExpired if state == :expired
    
    # 应用领域事件
    apply OrderSubmitted.new(data: {delivery_date: Time.now + 24.hours})
  end

  # 订单过期业务方法
  def expire
    apply OrderExpired.new
  end

  # 事件处理器 - OrderSubmitted
  on OrderSubmitted do |event|
    @state = :submitted
    @delivery_date = event.data.fetch(:delivery_date)
  end

  # 事件处理器 - OrderExpired
  on OrderExpired do |event|
    @state = :expired
  end

  private
  attr_reader :state
end

事件处理器的工作机制

当调用apply方法时,AggregateRoot会:

  1. 将事件添加到未发布事件列表
  2. 立即调用对应的事件处理器方法更新聚合状态

这种设计确保了业务逻辑与状态变更的分离,使代码更易于理解和维护。

聚合根的持久化

从事件存储加载

要重建聚合根的状态,需要从事件存储中读取所有相关事件并按顺序应用:

stream_name = "Order$123"
order = AggregateRoot::Repository.new.load(Order.new, stream_name)

Repository的load方法会:

  1. 从指定流(stream)中读取所有事件
  2. 在新创建的Order实例上按顺序重放这些事件
  3. 返回已恢复状态的聚合根对象

保存变更到事件存储

当聚合根状态变更后,需要将新产生的事件持久化:

stream_name = "Order$123"
repository = AggregateRoot::Repository.new

# 加载聚合根
order = repository.load(Order.new, stream_name)

# 执行业务操作(会产生新事件)
order.submit

# 保存变更
repository.store(order, stream_name)

store方法会获取聚合根的所有未发布事件,并将它们持久化到事件存储中。

简化工作流

使用with_aggregate方法

Repository提供了with_aggregate方法来简化加载-操作-保存的常见模式:

repository = AggregateRoot::Repository.new
repository.with_aggregate(Order.new, "Order$123") do |order|
  order.submit
end

自定义仓储类

为了更好的封装,可以为特定聚合根创建专门的仓储类:

class OrderRepository
  def initialize(event_store = Rails.configuration.event_store)
    @repository = AggregateRoot::Repository.new(event_store)
  end

  def with_order(order_id, &block)
    stream_name = "Order$#{order_id}"
    repository.with_aggregate(Order.new, stream_name, &block)
  end

  private
  attr_reader :repository
end

使用示例:

repository = OrderRepository.new
repository.with_order(123) do |order|
  order.submit
end

高级定制

自定义事件应用策略

默认情况下,AggregateRoot使用基于事件类名的约定来查找事件处理方法。但你可以通过覆盖apply_strategy方法来自定义这一行为:

class Order
  include AggregateRoot
  
  # ... 其他代码 ...

  private
  
  def apply_strategy
    ->(aggregate, event) do
      case event
      when OrderExpired
        order_has_expired(event)
      when OrderSubmitted
        order_has_been_submitted(event)
      else
        raise "Unknown event type: #{event.class}"
      end
    end
  end

  def order_has_been_submitted(event)
    @state = :submitted
    @delivery_date = event.data.fetch(:delivery_date)
  end

  def order_has_expired(event)
    @state = :expired
  end
end

apply_strategy必须返回一个可调用对象(如Proc/lambda),它接收两个参数:聚合根实例和要处理的事件。

最佳实践

  1. 保持聚合根精简:聚合根应该只包含核心业务逻辑和最小状态
  2. 事件设计:事件应该用过去时动词命名,表示已发生的事实
  3. 幂等处理:确保事件处理器可以安全地多次处理同一事件
  4. 合理划分边界:根据业务一致性需求确定聚合根的粒度

通过RailsEventStore的AggregateRoot模块,开发者可以轻松地在Rails应用中实现事件溯源架构,构建出更灵活、可追溯和可扩展的业务系统。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霍忻念

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值