Skip to main content

ddd_toolkit_core/
event_store.rs

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