iT邦幫忙

2023 iThome 鐵人賽

DAY 5
0
Software Development

當rust 遇上 cqrs & es系列 第 5

D5 實現基本資料結構(2)

  • 分享至 

  • xImage
  •  

先前做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());
        }
    }
}

上一篇
D4 測試command to mem store
下一篇
D6 測試event apply & rollback
系列文
當rust 遇上 cqrs & es30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言