getting_started/
getting_started.rs1use ddd_toolkit::{AggregateRoot as _, DomainEventPublisher, Repository, dispatch_events};
5use std::{collections::HashMap, convert::Infallible};
6
7#[derive(Debug, Clone, PartialEq, ddd_toolkit::DomainEvent)]
11struct OrderConfirmed {
12 order_id: u64,
13 total_yen: u32,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq)]
17enum OrderError {
18 ZeroPrice,
19 TotalOverflow,
20 EmptyOrder,
21 AlreadyConfirmed,
22}
23
24#[derive(Clone, ddd_toolkit::Entity, ddd_toolkit::AggregateRoot)]
29struct Order {
30 #[entity(id)]
31 id: u64,
32 total_yen: u32,
33 confirmed: bool,
34 #[aggregate(events)]
35 events: Vec<OrderConfirmed>,
36}
37impl Order {
38 fn draft(id: u64) -> Self {
39 Self {
40 id,
41 total_yen: 0,
42 confirmed: false,
43 events: vec![],
44 }
45 }
46
47 fn add_item(&mut self, price_yen: u32) -> Result<(), OrderError> {
50 if self.confirmed {
51 return Err(OrderError::AlreadyConfirmed);
52 }
53 if price_yen == 0 {
54 return Err(OrderError::ZeroPrice);
55 }
56 self.total_yen = self
57 .total_yen
58 .checked_add(price_yen)
59 .ok_or(OrderError::TotalOverflow)?;
60 Ok(())
61 }
62
63 fn confirm(&mut self) -> Result<(), OrderError> {
64 if self.confirmed {
65 return Err(OrderError::AlreadyConfirmed);
66 }
67 if self.total_yen == 0 {
68 return Err(OrderError::EmptyOrder);
69 }
70 self.confirmed = true;
71 self.record_event(OrderConfirmed {
74 order_id: self.id,
75 total_yen: self.total_yen,
76 });
77 Ok(())
78 }
79}
80
81fn main() {
85 let mut order = Order::draft(1);
86 assert_eq!(order.confirm(), Err(OrderError::EmptyOrder));
87 assert_eq!(order.add_item(0), Err(OrderError::ZeroPrice));
88 order.add_item(1200).unwrap();
89 order.add_item(3000).unwrap();
90 order.confirm().unwrap();
91 assert_eq!(order.add_item(100), Err(OrderError::AlreadyConfirmed));
92 assert_eq!(order.confirm(), Err(OrderError::AlreadyConfirmed));
93 assert_eq!(order.total_yen, 4200);
94 assert_eq!(order.events.len(), 1); let mut orders = MemoryOrders::default();
99 orders.save(&order).unwrap();
100 let saved = orders.find_by_id(&1).unwrap().unwrap();
101 assert!(saved.confirmed);
102 assert_eq!(saved.total_yen, 4200);
103 assert!(saved.events.is_empty()); assert_eq!(order.events.len(), 1); let mut notifications = Notifications::default();
107 dispatch_events(&mut order, &mut notifications).unwrap();
108 assert_eq!(
109 notifications.sent,
110 vec![OrderConfirmed {
111 order_id: 1,
112 total_yen: 4200
113 }]
114 );
115 assert!(order.events.is_empty());
116 println!("getting_started: order #1 confirmed; saved total=4200 yen; notifications=1");
117}
118
119#[derive(Default)]
125struct MemoryOrders(HashMap<u64, Order>);
126impl Repository for MemoryOrders {
127 type Aggregate = Order;
128 type Error = Infallible;
129 fn find_by_id(&self, id: &u64) -> Result<Option<Order>, Infallible> {
130 Ok(self.0.get(id).cloned())
131 }
132 fn save(&mut self, order: &Order) -> Result<(), Infallible> {
133 let mut saved = order.clone();
134 saved.pull_events();
135 self.0.insert(order.id, saved);
136 Ok(())
137 }
138 fn delete(&mut self, id: &u64) -> Result<(), Infallible> {
139 self.0.remove(id);
140 Ok(())
141 }
142 fn exists(&self, id: &u64) -> Result<bool, Infallible> {
143 Ok(self.0.contains_key(id))
144 }
145}
146
147#[derive(Default)]
150struct Notifications {
151 sent: Vec<OrderConfirmed>,
152}
153impl DomainEventPublisher for Notifications {
154 type Event = OrderConfirmed;
155 type Error = Infallible;
156 fn publish(&mut self, event: &OrderConfirmed) -> Result<(), Infallible> {
157 self.sent.push(event.clone());
158 Ok(())
159 }
160}