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会:
- 将事件添加到未发布事件列表
- 立即调用对应的事件处理器方法更新聚合状态
这种设计确保了业务逻辑与状态变更的分离,使代码更易于理解和维护。
聚合根的持久化
从事件存储加载
要重建聚合根的状态,需要从事件存储中读取所有相关事件并按顺序应用:
stream_name = "Order$123"
order = AggregateRoot::Repository.new.load(Order.new, stream_name)
Repository的load方法会:
- 从指定流(stream)中读取所有事件
- 在新创建的Order实例上按顺序重放这些事件
- 返回已恢复状态的聚合根对象
保存变更到事件存储
当聚合根状态变更后,需要将新产生的事件持久化:
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),它接收两个参数:聚合根实例和要处理的事件。
最佳实践
- 保持聚合根精简:聚合根应该只包含核心业务逻辑和最小状态
- 事件设计:事件应该用过去时动词命名,表示已发生的事实
- 幂等处理:确保事件处理器可以安全地多次处理同一事件
- 合理划分边界:根据业务一致性需求确定聚合根的粒度
通过RailsEventStore的AggregateRoot模块,开发者可以轻松地在Rails应用中实现事件溯源架构,构建出更灵活、可追溯和可扩展的业务系统。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考