先建立讀者 DTO
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct ReaderDto {
pub reader_id: String,
pub name: String,
pub books_borrowed: u32,
pub books_borrows: Vec<ReaderBorrows>,
pub next_due_date: Option<DateTime<Utc>>,
}
impl ReaderDto {
pub fn next_due_date(&self) -> Option<DateTime<Utc>> {
self.books_borrows
.iter()
.map(|record| record.due_date)
.min()
}
}
借閱資料
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct ReaderBorrows {
pub book_id: String,
pub book_name: String,
pub borrowed_date: DateTime<Utc>,
pub due_date: DateTime<Utc>,
}
讀者Query
#[derive(Debug, Clone)]
pub struct ReaderQuery {
pub db: Surreal<Db>,
}
impl ReaderQuery {
pub async fn insert_reader(&self, reader_dto: &ReaderDto) {
let _res : Option<ReaderDto> = self.db
.create(("readers", reader_dto.reader_id.clone()))
.content(reader_dto)
.await.unwrap();
}
pub async fn get_reader(&self, id: String) -> ReaderDto {
let result: Vec<ReaderDto> = self.db
.query("SELECT * FROM readers WHERE reader_id = $id")
.bind(("id", id))
.await.unwrap()
.take(0).unwrap();
result.first().unwrap().clone()
}
}
實作CQRS的 Query
#[async_trait]
impl Query<Reader> for ReaderQuery {
async fn dispatch(&self, aggregate_id: &str, events: &[EventEnvelope<Reader>]) {
for event in events {
match &event.payload {
ReaderEvent::ReaderCreated { name, id } => {
self.insert_reader(&ReaderDto {
reader_id: aggregate_id.to_string(),
name: name.to_string(),
books_borrowed: 0,
books_borrows: vec![],
next_due_date: None,
}).await;
}
ReaderEvent::BookBorrowed(borrow) => {
let mut reader = self.get_reader(aggregate_id.to_string()).await;
reader.books_borrowed += 1;
let book: Vec<BookDto> = self.db
.query("SELECT * FROM books WHERE book_id = $id")
.bind(("id", borrow.book_id.to_string()))
.await.unwrap()
.take(0).unwrap();
let book = book.first().unwrap();
reader.books_borrows.push(ReaderBorrows {
book_id: borrow.book_id.to_string(),
book_name: book.title.to_string(),
borrowed_date: borrow.borrowed_date,
due_date: borrow.due_date,
});
reader.next_due_date = reader.next_due_date();
let _res: Option<ReaderDto> = self.db
.update(("readers", aggregate_id))
.content(&reader)
.await.unwrap();
}
ReaderEvent::BookReturned(borrow) => {
let mut reader = self.get_reader(aggregate_id.to_string()).await;
reader.books_borrowed -= 1;
reader.books_borrows.retain(|record| record.book_id != borrow.book_id.to_string());
reader.next_due_date = reader.next_due_date();
let _res: Option<ReaderDto> = self.db
.update(("readers", aggregate_id))
.content(&reader)
.await.unwrap();
}
}
}
}
}