iT邦幫忙

2023 iThome 鐵人賽

DAY 3
0
Software Development

當rust 遇上 cqrs & es系列 第 3

D3 實現基本資料結構(1)

  • 分享至 

  • xImage
  •  

初步分析要建立的資料結構:

  • 2個主要的aggregate是「讀者」和「書籍」
  • 5個領域事件為
    • 「讀者」:「讀者已註冊」
    • 「書籍」:「書籍已建檔」「書籍已入庫」
    • 「借閱服務」:「書籍已借出」「書籍已歸還」
  • 對應的命令
    • 「讀者」:「讀者註冊」
    • 「書籍」:「書籍建檔」「書籍入庫」
    • 「借閱服務」:「書籍借出」「書籍歸還」
  • 查詢
    • 書籍資訊及庫存
    • 讀書資訊及借閱紀錄

實作 Aggregate Reader

實作讀者:

#[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

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

尚未有邦友留言

立即登入留言