4个版本
使用旧的Rust 2015
0.1.3 | 2020年2月20日 |
---|---|
0.1.2 | 2020年2月4日 |
0.1.1 | 2018年5月23日 |
0.1.0 | 2018年5月1日 |
#51 在 #aggregate
571 每月下载
在 7 个crate中使用 (3直接)
9KB
173 行
事件溯源
Rust的事件溯源库
事件溯源的一个优点是,在大多数情况下,采用此模式不需要太多代码。然而,仍有一些样板代码需要编写,以及确保事件、命令和聚合体都能各自履行其职责而不共享关注点的纪律。
要记住的基本工作流程是,将命令应用于聚合体,然后聚合体发出一个或多个事件。聚合体的业务逻辑还负责从上一个状态和新的事件组合中返回一个新状态。用数学公式表示,这看起来像
f(state1, event) = state2
有一些事件溯源库允许,甚至鼓励在内存中修改状态。我更喜欢一种更函数式的方法,这个库的设计也反映了这一点。它鼓励你为聚合体的业务逻辑编写可预测的单元测试,这些测试可以在不关心如何接收事件或如何将它们持久化存储的情况下独立执行。
首先,你创建一个未装饰的枚举用于你的Command类型
enum LocationCommand {
UpdateLocation { lat: f32, long: f32, alt: f32 },
}
接下来,你创建一个枚举用于你的事件,并使用derive宏为你编写一些样板代码。注意,命令变体是命令式语句,而事件变体是过去时态的动词短语。虽然这仅是惯例,而不是通过代码强制执行,但这是一个值得采纳的好做法。
#[derive(Serialize, Deserialize, Debug, Clone, Event)]
#[event_type_version("1.0")]
#[event_source("events://github.com/pholactery/eventsourcing/samples/location")]
enum LocationEvent {
LocationUpdated { lat: f32, long: f32, alt: f32 },
}
然后,我们定义一个表示聚合体将要使用的状态的类型。有了这个,我们就在聚合体中编写所有业务逻辑,这是我们的事件溯源系统的核心。
#[derive(Debug, Clone)]
struct LocationData {
lat: f32,
long: f32,
alt: f32,
generation: u64,
}
impl AggregateState for LocationData {
fn generation(&self) -> u64 {
self.generation
}
}
struct Location;
impl Aggregate for Location {
type Event = LocationEvent;
type Command = LocationCommand;
type State = LocationData;
fn apply_event(state: &Self::State, evt: &Self::Event) -> Result<Self::State> {
// TODO: validate event
let ld = match evt {
LocationEvent::LocationUpdated { lat, long, alt } => LocationData {
lat: *lat,
long: *long,
alt: *alt,
generation: state.generation + 1,
},
};
Ok(ld)
}
fn handle_command(_state: &Self::State, cmd: &Self::Command) -> Result<Vec<Self::Event>> {
// TODO: add code to validate state and command
// if validation passes...
Ok(vec![LocationEvent::LocationUpdated { lat: 10.0, long: 10.0, alt: 10.0 }])
}
}
有关使用示例的更多示例,请查看github上的示例目录。
lib.rs
:
EventSourcing Derive
针对 eventsourcing 库的定制派生宏实现
依赖项
~2MB
~47K SLoC