初步分析要建立的資料結構:
實作讀者:
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Reader {
pub id: Uuid,
pub name: String,
pub borrowed_books: Vec<Uuid>,
}
實作命令:
pub enum ReaderCommand {
CreateReader { name: String },
}
實作領域事件:
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum ReaderEvent {
ReaderCreated { name: String, id: Uuid },
}
實作錯誤:
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[error("{0}")]
pub struct ReaderError(pub String);
讀者事件綁定領域事件:
impl DomainEvent for ReaderEvent {
fn event_type(&self) -> String {
"ReaderEvent".to_string()
}
fn event_version(&self) -> String {
"0.1.0".to_string()
}
}
讀者綁定聚合:
#[async_trait]
impl Aggregate for Reader {
type Command = ReaderCommand;
type Event = ReaderEvent;
type Error = ReaderError;
fn aggregate_type() -> String {
"Reader".to_string()
}
async fn handle(&self, command: Self::Command) -> Result<Vec<Self::Event>, Self::Error> {
match command {
ReaderCommand::CreateReader { name } => {
let event = ReaderEvent::ReaderCreated { name, id: self.id };
Ok(vec![event])
}
}
}
fn apply(&mut self, event: Self::Event) {
match event {
ReaderEvent::ReaderCreated { name, id } => {
self.name = name;
self.id = id;
}
}
}
}
進行測試:
#[cfg(test)]
mod test {
use super::*;
#[tokio::test]
async fn test_user_register() {
let mut user = Reader::default();
let command = ReaderCommand::CreateReader {
name: "John Smith".to_string(),
};
let events = user.handle(command).await.unwrap();
assert_eq!(events.len(), 1);
let event = &events[0];
assert_eq!(
event,
&ReaderEvent::ReaderCreated {
name: "John Smith".to_string(),
id: user.id
}
);
user.apply(event.clone());
assert_eq!(user.name, "John Smith");
}
}
試跑一下測試:
Finished test [unoptimized + debuginfo] target(s) in 0.02s
Running unittests src/lib.rs (target/debug/deps/book_lib-0828773fb66fe890)
running 1 test
test domain::reader::test::test_user_register ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s