Skip to main content

ddd_toolkit_core/
projection_reliability.rs

1#![allow(clippy::type_complexity)]
2use crate::DomainEvent;
3use std::future::Future;
4use std::pin::Pin;
5
6/// **English:** Owned cursor identifying progress in a projection's input stream.
7///
8/// The cursor type and meaning belong to the application. This container performs no ordering,
9/// monotonicity, or validity checks and does not store progress by itself.
10///
11/// **日本語:** プロジェクションの入力ストリームにおける進捗を示す、所有されたカーソルです。
12///
13/// カーソルの型と意味はアプリケーションが定めます。このコンテナーは順序、単調性、妥当性を
14/// 検証せず、単体では進捗を保存しません。
15///
16/// **English:** Borrow a cursor for inspection, then recover its ownership without cloning.
17///
18/// **日本語:** カーソルを借用して確認した後、複製せずに所有権を取り出せます。
19///
20/// ```rust
21/// use ddd_toolkit_core::ProjectionCheckpoint;
22///
23/// let checkpoint = ProjectionCheckpoint::new(String::from("partition-2:17"));
24/// assert_eq!(checkpoint.cursor(), "partition-2:17");
25/// let cursor = checkpoint.into_cursor();
26/// assert_eq!(cursor, "partition-2:17");
27/// ```
28#[derive(Clone, Debug, Eq, Hash, PartialEq)]
29pub struct ProjectionCheckpoint<C> {
30    cursor: C,
31}
32
33impl<C> ProjectionCheckpoint<C> {
34    /// **English:** Wraps `cursor` without validating its position or comparing previous progress.
35    ///
36    /// **日本語:** 位置の検証や以前の進捗との比較をせずに `cursor` を包みます。
37    pub fn new(cursor: C) -> Self {
38        Self { cursor }
39    }
40
41    /// **English:** Borrows the stored cursor without advancing it.
42    ///
43    /// **日本語:** 進めることなく、保存されたカーソルを借用します。
44    pub fn cursor(&self) -> &C {
45        &self.cursor
46    }
47
48    /// **English:** Consumes the checkpoint and returns its cursor by value.
49    ///
50    /// **日本語:** チェックポイントを消費し、カーソルを値として返します。
51    pub fn into_cursor(self) -> C {
52        self.cursor
53    }
54}
55
56/// **English:** Successful outcome of [`IdempotentProjection::apply_once`] or
57/// [`AsyncIdempotentProjection::apply_once`], distinguishing application from a recognized duplicate.
58///
59/// **日本語:** [`IdempotentProjection::apply_once`] または [`AsyncIdempotentProjection::apply_once`] の
60/// 成功結果であり、適用と認識済みの重複を区別します。
61#[derive(Clone, Copy, Debug, Eq, PartialEq)]
62pub enum ProjectionApplication {
63    /// **English:** The implementation reports that this call applied the event.
64    ///
65    /// **日本語:** この呼び出しでイベントを適用したことを実装が報告しています。
66    Applied,
67
68    /// **English:** The implementation recognized an already-applied input and skipped reapplication.
69    ///
70    /// **日本語:** 適用済みの入力を実装が認識し、再適用を省略しました。
71    AlreadyApplied,
72}
73
74/// **English:** Synchronous persistence port for a projection's checkpoint.
75///
76/// With no projection identifier parameter, the store instance determines the checkpoint scope.
77/// Implementations define replacement, durability, concurrency, panic conditions, and whether
78/// saving progress is atomic with read-model updates. This trait supplies no monotonicity check.
79///
80/// **日本語:** プロジェクションのチェックポイントを同期永続化するポートです。
81///
82/// プロジェクション識別子の引数がないため、保存対象の範囲はストアのインスタンスが定めます。
83/// 置換、永続性、並行処理、パニック条件、読み取りモデル更新との原子的な進捗保存は
84/// 実装が定めます。このトレイトは単調性の検証を提供しません。
85pub trait ProjectionCheckpointStore {
86    /// **English:** Application-defined position representation persisted by this store.
87    ///
88    /// **日本語:** このストアが永続化する、アプリケーション定義の位置表現です。
89    type Cursor;
90    /// **English:** Checkpoint storage failure reported by the adapter.
91    ///
92    /// **日本語:** アダプターが報告するチェックポイント保存先の障害です。
93    type Error;
94
95    /// **English:** Returns stored progress, `Ok(None)` for absence, or a storage error.
96    /// Absence is distinct from failing to read the checkpoint.
97    ///
98    /// **日本語:** 保存済み進捗、不在を表す `Ok(None)`、またはストレージエラーを返します。
99    /// 不在とチェックポイントの読み込み失敗は区別します。
100    fn load(&self) -> Result<Option<ProjectionCheckpoint<Self::Cursor>>, Self::Error>;
101
102    /// **English:** Consumes `checkpoint` to persist progress, returning `Ok(())` or a storage error.
103    /// The value is not returned on failure; retry and partial-write behavior depend on the adapter.
104    ///
105    /// **日本語:** `checkpoint` を消費して進捗を保存し、`Ok(())` またはストレージエラーを返します。
106    /// 失敗時に値は返されません。再試行と部分書き込みの挙動はアダプターに依存します。
107    fn save(&mut self, checkpoint: ProjectionCheckpoint<Self::Cursor>) -> Result<(), Self::Error>;
108}
109
110/// **English:** Opt-in projection contract distinguishing new application from duplicate input.
111///
112/// This trait is independent of [`crate::Projection`] and [`ProjectionCheckpointStore`].
113/// Implementations supply duplicate detection, input identity, read-model changes, and any
114/// atomic checkpoint updates or locking. No deduplication algorithm is implemented here;
115/// adapters must define failure, retry, and panic behavior for their persistence boundary.
116///
117/// **日本語:** 新規適用と重複入力を区別する、任意のプロジェクション契約です。
118///
119/// このトレイトは [`crate::Projection`] と [`ProjectionCheckpointStore`] から独立しています。
120/// 重複検出、入力の識別、読み取りモデルの変更、原子的なチェックポイント更新やロックは
121/// 実装が提供します。ここでは重複排除アルゴリズムを実装しません。永続化境界における
122/// 失敗、再試行、パニック時の挙動はアダプターが定める必要があります。
123pub trait IdempotentProjection {
124    /// **English:** Domain event inspected for application to the read model.
125    ///
126    /// **日本語:** 読み取りモデルへの適用を判断するドメインイベントです。
127    type Event: DomainEvent;
128    /// **English:** Position type used to associate input with projection progress.
129    ///
130    /// **日本語:** 入力とプロジェクションの進捗を関連付ける位置の型です。
131    type Cursor;
132    /// **English:** Error raised while detecting duplicates or applying an event.
133    ///
134    /// **日本語:** 重複の検出やイベント適用中に生じるエラーです。
135    type Error;
136
137    /// **English:** Borrows `event` and `checkpoint` and reports application or a recognized duplicate.
138    /// An error does not specify whether partial updates occurred. This call does not automatically
139    /// invoke a separate checkpoint store; checkpoint meaning and persistence belong to the implementation.
140    ///
141    /// **日本語:** `event` と `checkpoint` を借用し、適用または認識済みの重複を報告します。
142    /// エラーだけでは部分更新の有無は決まりません。別のチェックポイントストアを自動では
143    /// 呼び出さず、チェックポイントの意味と永続化は実装が定めます。
144    fn apply_once(
145        &mut self,
146        event: &Self::Event,
147        checkpoint: &ProjectionCheckpoint<Self::Cursor>,
148    ) -> Result<ProjectionApplication, Self::Error>;
149}
150
151/// **English:** Checkpoint persistence port returning boxed `Send` futures tied to the store borrow.
152///
153/// The store instance determines the projection scope, as in [`ProjectionCheckpointStore`].
154/// The store must be `Send + Sync`, the cursor `Send + Sync`, and the error `Send`; these bounds
155/// do not provide locking, monotonic progress checks, or atomic read-model updates. Implementations
156/// define durability, concurrent replacement, partial failures, and panic conditions.
157///
158/// Dropping a returned future ends that operation's polling, but does not promise rollback or that
159/// no I/O occurred. Implementations may perform work before returning a future and must specify
160/// cancellation and retry behavior. This trait supplies no executor or transaction coordinator.
161///
162/// **日本語:** ストアの借用期間に結び付いた、ボックス化された `Send` Future を返す進捗永続化ポートです。
163///
164/// [`ProjectionCheckpointStore`] と同様に、プロジェクションの範囲はストアのインスタンスが定めます。
165/// ストアには `Send + Sync`、カーソルには `Send + Sync`、エラーには `Send` が必要ですが、
166/// これらの境界はロック、単調な進捗の検査、読み取りモデルの原子的更新を提供しません。
167/// 永続性、並行する置換、部分失敗、パニック条件は実装が定めます。
168///
169/// 返された Future の破棄はその操作のポーリングを終了しますが、ロールバックや I/O が未実行であることを
170/// 保証しません。実装は Future を返す前にも処理でき、キャンセルと再試行の挙動を定める必要があります。
171/// このトレイトは実行器やトランザクション調整器を提供しません。
172///
173/// **English:** Await a write and then read its result; the adapter determines read-after-write consistency.
174/// This helper returns `None` if the subsequent load reports no stored checkpoint.
175///
176/// **日本語:** 書き込みを待ってから結果を読み込みます。書き込み後の読み取り整合性はアダプターが定めます。
177/// 後続の読み込みがチェックポイントの不在を報告した場合、このヘルパーは `None` を返します。
178///
179/// ```rust
180/// use ddd_toolkit_core::{AsyncProjectionCheckpointStore, ProjectionCheckpoint};
181///
182/// async fn save_then_load<S: AsyncProjectionCheckpointStore>(
183///     store: &mut S,
184///     cursor: S::Cursor,
185/// ) -> Result<Option<S::Cursor>, S::Error> {
186///     store.save(ProjectionCheckpoint::new(cursor)).await?;
187///     Ok(store.load().await?.map(ProjectionCheckpoint::into_cursor))
188/// }
189/// ```
190pub trait AsyncProjectionCheckpointStore: Send + Sync {
191    /// **English:** Owned progress representation transferable and shareable across threads.
192    /// No ordering or clone bound is required.
193    ///
194    /// **日本語:** スレッド間で移動・共有できる、所有された進捗表現です。
195    /// 順序や複製の境界は要求しません。
196    type Cursor: Send + Sync;
197    /// **English:** Storage failure transferable with the future's result; no error trait bound is imposed.
198    ///
199    /// **日本語:** Future の結果とともに移動できるストレージの失敗です。エラートレイトの境界は課しません。
200    type Error: Send;
201
202    /// **English:** Borrows the store for `'a` and returns a future yielding owned stored progress.
203    /// `Ok(None)` means absence, while `Err` reports a read failure. Shared access permits multiple
204    /// load borrows but does not define snapshot isolation or freshness. Drop effects are adapter-defined.
205    ///
206    /// **日本語:** ストアを `'a` の間借用し、所有された保存済み進捗を返す Future を返します。
207    /// `Ok(None)` は不在を表し、`Err` は読み込み失敗を表します。共有参照で複数の読み込みを借用できますが、
208    /// スナップショット分離や鮮度は定めません。破棄による効果はアダプターが定めます。
209    fn load<'a>(
210        &'a self,
211    ) -> Pin<
212        Box<
213            dyn Future<Output = Result<Option<ProjectionCheckpoint<Self::Cursor>>, Self::Error>>
214                + Send
215                + 'a,
216        >,
217    >;
218
219    /// **English:** Takes ownership of `checkpoint` and exclusively borrows the store for `'a`.
220    /// The returned future reports `Ok(())` or a write failure, without returning the checkpoint
221    /// on error or cancellation. No rollback, retry, or atomic coordination with a projection is added.
222    ///
223    /// **日本語:** `checkpoint` の所有権を受け取り、ストアを `'a` の間排他的に借用します。
224    /// 返す Future は `Ok(())` または書き込み失敗を報告し、エラーやキャンセル時にチェックポイントを
225    /// 返しません。ロールバック、再試行、プロジェクションとの原子的な連携は追加しません。
226    fn save<'a>(
227        &'a mut self,
228        checkpoint: ProjectionCheckpoint<Self::Cursor>,
229    ) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send + 'a>>;
230}
231
232/// **English:** Asynchronous duplicate-aware projection independent of [`crate::AsyncProjection`]
233/// and [`AsyncProjectionCheckpointStore`].
234///
235/// Implementations provide input identity, duplicate detection, read-model updates, and any atomic
236/// checkpoint persistence. The trait itself implements no deduplication. The projection must be `Send`,
237/// its event `Sync`, its cursor `Send + Sync`, and its error `Send` for the boxed `Send` future.
238/// The projection need not be `Sync`; mutable borrowing prevents overlapping calls on the same value.
239///
240/// Dropping a future stops polling but does not establish whether changes occurred or were rolled back,
241/// even before the first poll: implementations may act during the method call. Adapters must document
242/// cancellation, retry, partial-update, and panic behavior at their persistence boundary.
243///
244/// **日本語:** [`crate::AsyncProjection`] と [`AsyncProjectionCheckpointStore`] から独立した、
245/// 重複を区別する非同期プロジェクションです。
246///
247/// 入力の識別、重複検出、読み取りモデル更新、原子的なチェックポイント永続化は実装が提供します。
248/// トレイト自体は重複排除を実装しません。ボックス化された `Send` Future のため、
249/// プロジェクションには `Send`、イベントには `Sync`、カーソルには `Send + Sync`、エラーには `Send` が必要です。
250/// プロジェクション自体に `Sync` は不要です。同じ値への呼び出しの重複は可変借用が防ぎます。
251///
252/// Future の破棄はポーリングを止めますが、変更の有無やロールバックを保証しません。
253/// メソッド呼び出し中にも処理できるため、初回ポーリング前でも同様です。永続化境界における
254/// キャンセル、再試行、部分更新、パニックの挙動はアダプターが文書化する必要があります。
255pub trait AsyncIdempotentProjection: Send {
256    /// **English:** Domain event shareable through its borrowed reference in a `Send` future.
257    ///
258    /// **日本語:** `Send` Future 内の借用参照を通して共有できるドメインイベントです。
259    type Event: DomainEvent + Sync;
260    /// **English:** Progress representation used to associate the borrowed checkpoint with input.
261    ///
262    /// **日本語:** 借用したチェックポイントを入力に関連付ける進捗表現です。
263    type Cursor: Send + Sync;
264    /// **English:** Transferable failure from duplicate detection or read-model application.
265    ///
266    /// **日本語:** 重複検出や読み取りモデルへの適用で生じる、移動可能な失敗です。
267    type Error: Send;
268
269    /// **English:** Borrows the projection exclusively and the event and checkpoint jointly for `'a`.
270    /// The future reports [`ProjectionApplication::Applied`], [`ProjectionApplication::AlreadyApplied`],
271    /// or an error that does not rule out partial updates. It does not automatically call a checkpoint
272    /// store. Checkpoint interpretation and whether progress is persisted belong to the implementation.
273    ///
274    /// **日本語:** プロジェクションを排他的に、イベントとチェックポイントを共有で、いずれも `'a` の間借用します。
275    /// Future は [`ProjectionApplication::Applied`]、[`ProjectionApplication::AlreadyApplied`]、
276    /// または部分更新の可能性を排除しないエラーを返します。チェックポイントストアは自動では呼び出しません。
277    /// チェックポイントの解釈と進捗を永続化するかどうかは実装が定めます。
278    fn apply_once<'a>(
279        &'a mut self,
280        event: &'a Self::Event,
281        checkpoint: &'a ProjectionCheckpoint<Self::Cursor>,
282    ) -> Pin<Box<dyn Future<Output = Result<ProjectionApplication, Self::Error>> + Send + 'a>>;
283}