atomr_patterns/ddd/command.rs
1//! [`Command`] — an intent to change one aggregate's state.
2
3use std::hash::Hash;
4
5/// A command targets exactly one [`crate::AggregateRoot`] (the
6/// transactional consistency boundary). The framework needs to know
7/// *which* aggregate before it can route the command, so every command
8/// must surface its target id.
9pub trait Command: Send + 'static {
10 /// Identity type of the aggregate this command targets.
11 type AggregateId: Clone + Eq + Hash + Send + Sync + 'static;
12
13 /// Routing key — which aggregate instance should handle this command.
14 fn aggregate_id(&self) -> Self::AggregateId;
15
16 /// Optional sequence the caller expects the aggregate to be at
17 /// when this command runs. When `Some(n)`, the gateway compares
18 /// the entity's current sequence number to `n` and returns
19 /// [`crate::PatternError::ConcurrencyConflict`] on mismatch — i.e.
20 /// optimistic concurrency control. Default: `None` (no check).
21 fn expected_version(&self) -> Option<u64> {
22 None
23 }
24
25 /// Optional idempotency key. When the gateway has been configured
26 /// with [`crate::cqrs::CqrsBuilder::dedupe_window`] (non-zero),
27 /// commands with the same `command_id` for the same aggregate
28 /// return the *previous* result without re-running the handler.
29 /// Default: `None` (no dedupe).
30 fn command_id(&self) -> Option<&str> {
31 None
32 }
33}