Skip to main content

ddd_toolkit_core/
event_store.rs

1use crate::{AggregateRoot, Entity};
2
3/// **English:**<br>
4/// Zero-based event position; the first event in a stream has revision `0`.
5///
6/// **日本語:**<br>
7/// ゼロ始まりのイベント位置です。ストリームの最初のイベントはリビジョン `0` です。
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct StreamRevision(u64);
10
11impl StreamRevision {
12    /// **English:**<br>
13    /// Wraps `value` without checking whether a corresponding stream exists.
14    ///
15    /// **日本語:**<br>
16    /// 対応するストリームの存在を確認せず `value` をラップします。
17    pub const fn new(value: u64) -> Self {
18        Self(value)
19    }
20
21    /// **English:**<br>
22    /// Returns the zero-based event position.
23    ///
24    /// **日本語:**<br>
25    /// ゼロ始まりのイベント位置を返します。
26    pub const fn value(self) -> u64 {
27        self.0
28    }
29
30    /// **English:**<br>
31    /// Returns the next revision, or `None` at `u64::MAX`.
32    ///
33    /// **日本語:**<br>
34    /// 次のリビジョンを返します。`u64::MAX` では `None` を返します。
35    pub const fn next(self) -> Option<Self> {
36        match self.0.checked_add(1) {
37            Some(value) => Some(Self(value)),
38            None => None,
39        }
40    }
41}
42
43/// **English:**<br>
44/// Absence of a stream, distinct from an existing stream at revision `0`.
45///
46/// **日本語:**<br>
47/// ストリームの不在です。リビジョン `0` の既存ストリームとは区別されます。
48#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
49pub struct NoStream;
50
51/// **English:**<br>
52/// Stream state required by a caller when appending events.
53///
54/// **日本語:**<br>
55/// イベント追加時に呼び出し側が要求するストリーム状態です。
56#[derive(Clone, Copy, Debug, Eq, PartialEq)]
57pub enum ExpectedRevision {
58    /// **English:**<br>
59    /// Requires that no stream exists for the identifier.
60    ///
61    /// **日本語:**<br>
62    /// 識別子に対応するストリームの不在を要求します。
63    NoStream(NoStream),
64    /// **English:**<br>
65    /// Requires a stream ending at this revision.
66    ///
67    /// **日本語:**<br>
68    /// このリビジョンで終わるストリームを要求します。
69    Revision(StreamRevision),
70}
71
72/// **English:**<br>
73/// Stream state observed by an adapter during a revision conflict.
74///
75/// **日本語:**<br>
76/// リビジョン競合時にアダプターが観測したストリーム状態です。
77#[derive(Clone, Copy, Debug, Eq, PartialEq)]
78pub enum CurrentRevision {
79    /// **English:**<br>
80    /// No stream exists for the identifier.
81    ///
82    /// **日本語:**<br>
83    /// 識別子に対応するストリームが存在しません。
84    NoStream(NoStream),
85    /// **English:**<br>
86    /// The existing stream ends at this revision.
87    ///
88    /// **日本語:**<br>
89    /// 既存ストリームの最終リビジョンです。
90    Revision(StreamRevision),
91}
92
93/// **English:**<br>
94/// Non-empty, complete history in replay order starting at revision `0`.
95/// Construction checks event count against the final revision, not event contents or identity.
96///
97/// **日本語:**<br>
98/// リビジョン `0` から始まる再生順の空でない完全な履歴です。
99/// 構築時にはイベント数と最終リビジョンを照合し、内容や識別子は検証しません。
100///
101/// # Example / 使用例
102///
103/// ```rust
104/// use ddd_toolkit_core::{StreamHistory, StreamRevision};
105/// let history = StreamHistory::try_new(vec!["created", "updated"], StreamRevision::new(1))?;
106/// assert_eq!(history.events_after(StreamRevision::new(0)), Some(&["updated"][..]));
107/// assert_eq!(history.events_after(StreamRevision::new(1)), Some(&[][..]));
108/// # Ok::<(), ddd_toolkit_core::StreamHistoryError>(())
109/// ```
110#[derive(Debug, Eq, PartialEq)]
111pub struct StreamHistory<E> {
112    events: Vec<E>,
113    revision: StreamRevision,
114}
115
116impl<E> StreamHistory<E> {
117    /// **English:**<br>
118    /// Takes ownership of events and checks that `revision` equals their count minus one.
119    /// Returns [`StreamHistoryError::Empty`] for no events, [`StreamHistoryError::TooManyEvents`]
120    /// if the count cannot fit in `u64`, or [`StreamHistoryError::RevisionMismatch`] for a mismatch.
121    ///
122    /// **日本語:**<br>
123    /// イベントを所有し、`revision` がイベント数から 1 を引いた値か確認します。
124    /// 空なら [`StreamHistoryError::Empty`]、件数が `u64` に収まらなければ
125    /// [`StreamHistoryError::TooManyEvents`]、不一致なら [`StreamHistoryError::RevisionMismatch`] を返します。
126    pub fn try_new(events: Vec<E>, revision: StreamRevision) -> Result<Self, StreamHistoryError> {
127        if events.is_empty() {
128            return Err(StreamHistoryError::Empty);
129        }
130
131        let event_count =
132            u64::try_from(events.len()).map_err(|_| StreamHistoryError::TooManyEvents)?;
133        let expected = StreamRevision::new(event_count - 1);
134        if revision != expected {
135            return Err(StreamHistoryError::RevisionMismatch {
136                expected,
137                actual: revision,
138            });
139        }
140
141        Ok(Self { events, revision })
142    }
143
144    /// **English:**<br>
145    /// Borrows all events in construction order.
146    ///
147    /// **日本語:**<br>
148    /// 構築時の順序ですべてのイベントを借用します。
149    pub fn events(&self) -> &[E] {
150        &self.events
151    }
152
153    /// **English:**<br>
154    /// Returns the final event's revision.
155    ///
156    /// **日本語:**<br>
157    /// 最後のイベントのリビジョンを返します。
158    pub fn revision(&self) -> StreamRevision {
159        self.revision
160    }
161
162    /// **English:**<br>
163    /// Consumes the history and returns its events without cloning.
164    ///
165    /// **日本語:**<br>
166    /// 履歴を消費し、複製せずにイベントを返します。
167    pub fn into_events(self) -> Vec<E> {
168        self.events
169    }
170
171    /// **English:**<br>
172    /// Borrows events strictly after `revision`; the final revision yields an empty slice.
173    /// Returns `None` for a future revision or if the next index cannot fit in `usize`.
174    ///
175    /// **日本語:**<br>
176    /// `revision` より後のイベントを借用します。最終リビジョンなら空スライスです。
177    /// 未来のリビジョンや、次の添字が `usize` に収まらない場合は `None` を返します。
178    pub fn events_after(&self, revision: StreamRevision) -> Option<&[E]> {
179        if revision > self.revision {
180            return None;
181        }
182
183        let start = usize::try_from(revision.value()).ok()?.checked_add(1)?;
184        Some(&self.events[start..])
185    }
186}
187
188/// **English:**<br>
189/// Reason that event data cannot form a complete [`StreamHistory`].
190///
191/// **日本語:**<br>
192/// イベントデータから完全な [`StreamHistory`] を構築できない理由です。
193#[derive(Clone, Copy, Debug, Eq, PartialEq)]
194pub enum StreamHistoryError {
195    /// **English:**<br>
196    /// The event vector is empty.
197    ///
198    /// **日本語:**<br>
199    /// イベントのベクターが空です。
200    Empty,
201    /// **English:**<br>
202    /// The event count exceeds the range of `u64`.
203    ///
204    /// **日本語:**<br>
205    /// イベント数が `u64` の範囲を超えています。
206    TooManyEvents,
207    /// **English:**<br>
208    /// The final revision differs from the event count minus one.
209    ///
210    /// **日本語:**<br>
211    /// 最終リビジョンがイベント数から 1 を引いた値と異なります。
212    RevisionMismatch {
213        /// **English:**<br>
214        /// Final revision calculated from the event count.
215        ///
216        /// **日本語:**<br>
217        /// イベント数から算出した最終リビジョンです。
218        expected: StreamRevision,
219        /// **English:**<br>
220        /// Final revision supplied by the caller.
221        ///
222        /// **日本語:**<br>
223        /// 呼び出し側が指定した最終リビジョンです。
224        actual: StreamRevision,
225    },
226}
227
228/// **English:**<br>
229/// Stream lookup result, separating absence from complete event history.
230///
231/// **日本語:**<br>
232/// ストリームの検索結果です。不在と完全なイベント履歴を区別します。
233#[derive(Debug, Eq, PartialEq)]
234pub enum LoadResult<E> {
235    /// **English:**<br>
236    /// The identifier has no stored stream.
237    ///
238    /// **日本語:**<br>
239    /// 識別子に対応する保存済みストリームがありません。
240    NoStream(NoStream),
241    /// **English:**<br>
242    /// Complete, non-empty history of the stored stream.
243    ///
244    /// **日本語:**<br>
245    /// 保存済みストリームの空でない完全な履歴です。
246    Found(StreamHistory<E>),
247}
248
249/// **English:**<br>
250/// Stream position reported after a successful append.
251///
252/// **日本語:**<br>
253/// 追加成功後に報告されるストリーム位置です。
254#[derive(Clone, Copy, Debug, Eq, PartialEq)]
255pub struct AppendResult {
256    /// **English:**<br>
257    /// Revision of the final event after appending the batch.
258    ///
259    /// **日本語:**<br>
260    /// バッチ追加後の最後のイベントのリビジョンです。
261    pub resulting_revision: StreamRevision,
262}
263
264/// **English:**<br>
265/// Requested and observed stream states that prevented an append.
266///
267/// **日本語:**<br>
268/// 追加を妨げた、要求されたストリーム状態と観測された状態です。
269#[derive(Clone, Copy, Debug, Eq, PartialEq)]
270pub struct RevisionConflict {
271    /// **English:**<br>
272    /// Stream state requested by the caller.
273    ///
274    /// **日本語:**<br>
275    /// 呼び出し側が要求したストリーム状態です。
276    pub expected: ExpectedRevision,
277    /// **English:**<br>
278    /// Stream state observed by the adapter.
279    ///
280    /// **日本語:**<br>
281    /// アダプターが観測したストリーム状態です。
282    pub current: CurrentRevision,
283}
284
285/// **English:**<br>
286/// Append failure; `E` carries an adapter-specific storage error.
287///
288/// **日本語:**<br>
289/// 追加の失敗です。`E` はアダプター固有のストレージエラーです。
290#[derive(Debug, Eq, PartialEq)]
291pub enum AppendError<E> {
292    /// **English:**<br>
293    /// No events were supplied.
294    ///
295    /// **日本語:**<br>
296    /// イベントが指定されていません。
297    EmptyBatch,
298    /// **English:**<br>
299    /// Expected and observed stream states differ.
300    ///
301    /// **日本語:**<br>
302    /// 期待したストリーム状態と観測された状態が異なります。
303    Conflict(RevisionConflict),
304    /// **English:**<br>
305    /// Storage adapter failure.
306    ///
307    /// **日本語:**<br>
308    /// ストレージアダプターの障害です。
309    Adapter(E),
310    /// **English:**<br>
311    /// The batch would exceed the revision range.
312    ///
313    /// **日本語:**<br>
314    /// バッチを追加するとリビジョンの範囲を超えます。
315    RevisionExhausted {
316        /// **English:**<br>
317        /// Stream revision before the attempted append.
318        ///
319        /// **日本語:**<br>
320        /// 追加を試みる前のストリームリビジョンです。
321        current: StreamRevision,
322    },
323}
324
325/// **English:**<br>
326/// Synchronous persistence boundary for aggregate event streams.
327/// Implementations define atomicity, durability, concurrent-writer coordination, and panic
328/// conditions; this trait supplies no storage or locking implementation.
329///
330/// **日本語:**<br>
331/// 集約イベントストリームの同期永続化境界です。
332/// 原子性、永続性、並行書き込みの調整、パニック条件は実装が定めます。
333/// このトレイト自体はストレージやロックを実装しません。
334pub trait EventStore {
335    /// **English:**<br>
336    /// Aggregate defining the identifier and event types to store.
337    ///
338    /// **日本語:**<br>
339    /// 保存する識別子とイベントの型を定める集約です。
340    type Aggregate: AggregateRoot;
341    /// **English:**<br>
342    /// Adapter-specific storage failure.
343    ///
344    /// **日本語:**<br>
345    /// アダプター固有のストレージ障害です。
346    type Error;
347
348    /// **English:**<br>
349    /// Loads the complete history for `id`, or reports an absent stream.
350    /// Storage failures are returned as `Self::Error`, not as stream absence.
351    ///
352    /// **日本語:**<br>
353    /// `id` の完全な履歴を読み込むか、ストリームの不在を報告します。
354    /// ストレージ障害は不在として扱わず、`Self::Error` として返します。
355    fn load(
356        &self,
357        id: &<Self::Aggregate as Entity>::Id,
358    ) -> Result<LoadResult<<Self::Aggregate as AggregateRoot>::Event>, Self::Error>;
359
360    /// **English:**<br>
361    /// Consumes a batch to append in vector order under the `expected` stream state.
362    /// Returns the final revision or an [`AppendError`] for an empty batch, conflict, revision
363    /// exhaustion, or adapter failure. The batch is not returned on failure; consult the adapter
364    /// contract for partial-write and retry behavior.
365    ///
366    /// **日本語:**<br>
367    /// バッチを消費し、`expected` のストリーム状態を条件にベクター順で追加します。
368    /// 最終リビジョン、または空バッチ・競合・リビジョン枯渇・アダプター障害を示す
369    /// [`AppendError`] を返します。失敗時にバッチは返されません。部分書き込みや再試行の
370    /// 挙動はアダプターの契約を確認してください。
371    fn append(
372        &mut self,
373        id: &<Self::Aggregate as Entity>::Id,
374        expected: ExpectedRevision,
375        events: Vec<<Self::Aggregate as AggregateRoot>::Event>,
376    ) -> Result<AppendResult, AppendError<Self::Error>>;
377}