先前做Reader結構比較簡單,現在嘗試實作Book:
書籍
pub struct Book {
pub id: String,
pub title: String,
pub isbn10: String,
pub description: String,
pub copies: u32, // 館藏數量
pub lending_records: Vec<LendingRecord>, // 出借中資料
pub lent_history: Vec<LentRecord>, // 歷史借閱紀錄
}
借出清單
pub struct LendingRecord {
pub reader_id: String,
pub lent_date: DateTime<Utc>,
pub due_date: DateTime<Utc>,
}
impl PartialEq<Self> for LendingRecord {
fn eq(&self, other: &Self) -> bool {
return self.reader_id == other.reader_id && self.lent_date == other.lent_date;
}
}
歷史借閱紀錄
pub struct LentRecord {
pub reader_id: String,
pub lent_date: DateTime<Utc>,
pub due_date: DateTime<Utc>,
pub returned_date: Option<DateTime<Utc>>,
}
impl PartialEq<Self> for LentRecord {
fn eq(&self, other: &Self) -> bool {
return self.reader_id == other.reader_id && self.lent_date == other.lent_date;
}
}
指令
pub enum BookCommand {
CreateBook { // 建檔
id: String,
title: String,
isbn10: String,
description: String,
},
IngestBook { // 入庫
id: String,
copies: u32,
},
LendBook(LendingRecord), // 借出
ReturnBook(LentRecord), // 歸還
}
Aggregate: 指令處理
async fn handle(&self, command: Self::Command) -> Result<Vec<Self::Event>, Self::Error> {
match command {
// todo: 檢查書籍是否已存在
BookCommand::CreateBook {
id,
title,
isbn10,
description,
} => {
let event = BookEvent::BookCreated {
id,
title,
isbn10,
description,
};
Ok(vec![event])
}
BookCommand::IngestBook { id, copies } => {
let event = BookEvent::BookIngested { id, copies };
Ok(vec![event])
}
BookCommand::LendBook(lending) => {
if self.copies == 0 {
return Err(BookError("書籍已無庫存".to_string()));
}
if self.lending_records.contains(&lending) {
return Err(BookError("讀者已借出同一本書".to_string()));
}
let event = BookEvent::BookLent(lending);
Ok(vec![event])
}
BookCommand::ReturnBook(lent_record) => {
let event = BookEvent::BookReturned(lent_record);
Ok(vec![event])
}
}
}
Aggregate: 事件套用
fn apply(&mut self, event: Self::Event) {
match event {
BookEvent::BookCreated {
id,
title,
isbn10,
description,
} => {
self.id = id;
self.title = title;
self.isbn10 = isbn10;
self.description = description;
}
BookEvent::BookIngested { copies, .. } => {
self.copies += copies;
}
BookEvent::BookLent(lent_record) => {
self.copies -= 1;
self.lending_records.push(lent_record);
}
BookEvent::BookReturned(lent_record) => {
self.copies += 1;
self.lending_records.retain(|record|
record.reader_id != lent_record.reader_id
&& record.lent_date != lent_record.lent_date);
self.lent_history.push(lent_record);
}
}
}
這次多套一個,還原上一步,在事件走訪的時候,使其還原該事件:
fn rollback(&mut self, event: Self::Event) {
match event {
BookEvent::BookCreated { .. } => {
self.id = String::new();
self.title = String::new();
self.isbn10 = String::new();
self.description = String::new();
}
BookEvent::BookIngested { copies, .. } => {
self.copies -= copies;
}
BookEvent::BookLent(lending) => {
self.copies += 1;
self.lending_records.retain(|record| record == &lending);
}
BookEvent::BookReturned(lent_record) => {
self.copies -= 1;
self.lent_history.retain(|record| *record == lent_record);
self.lending_records.push(lent_record.into());
}
}
}