Skip to main content

ddd_toolkit_core/
transactional_outbox.rs

1#![allow(clippy::type_complexity)]
2
3use crate::{AggregateRoot, AppendError, AppendResult, ExpectedRevision};
4
5/// **English:**<br>
6/// Owned adapter-assigned identifier for an outbox message.
7///
8/// The string is not validated or generated here. It need not equal a domain event's
9/// delivery identifier; uniqueness and identifier scope belong to the outbox adapter.
10///
11/// **日本語:**<br>
12/// アダプターが割り当てる、所有されたアウトボックスメッセージ識別子です。
13///
14/// ここでは文字列を検証・生成しません。ドメインイベントの配信識別子と同じである
15/// 必要はなく、一意性と識別子のスコープはアウトボックスアダプターが定めます。
16#[derive(Clone, Debug, Eq, Hash, PartialEq)]
17pub struct OutboxMessageId(String);
18
19impl OutboxMessageId {
20    /// **English:**<br>
21    /// Converts `value` to the stored string, accepting empty strings as well.
22    ///
23    /// **日本語:**<br>
24    /// `value` を保存用の文字列へ変換します。空文字列も受け付けます。
25    pub fn new(value: impl Into<String>) -> Self {
26        Self(value.into())
27    }
28
29    /// **English:**<br>
30    /// Borrows the message identifier without cloning its string.
31    ///
32    /// **日本語:**<br>
33    /// 文字列を複製せずにメッセージ識別子を借用します。
34    pub fn as_str(&self) -> &str {
35        &self.0
36    }
37
38    /// **English:**<br>
39    /// Consumes the identifier and returns its owned string.
40    ///
41    /// **日本語:**<br>
42    /// 識別子を消費し、所有された文字列を返します。
43    pub fn into_inner(self) -> String {
44        self.0
45    }
46}
47
48/// **English:**<br>
49/// Owned message identifier and event returned by an outbox claim.
50///
51/// Constructing or dropping this container does not reserve, acknowledge, or release a message.
52/// Leases, redelivery, and acknowledgement effects are adapter responsibilities.
53///
54/// Split a claim to transfer its identifier and payload independently.
55///
56/// **日本語:**<br>
57/// アウトボックスの取得操作が返す、所有されたメッセージ識別子とイベントです。
58///
59/// このコンテナーの構築や破棄は、メッセージの予約、確認応答、解放を行いません。
60/// リース、再配信、確認応答の効果はアダプターの責務です。
61///
62/// 識別子とペイロードを別々に渡すために取得結果を分解します。
63///
64/// # Example / 使用例
65///
66/// ```rust
67/// use ddd_toolkit_core::{OutboxClaim, OutboxMessageId};
68///
69/// let claim = OutboxClaim::new(OutboxMessageId::new("message-1"), 42_u32);
70/// assert_eq!(claim.message_id().as_str(), "message-1");
71/// assert_eq!(*claim.event(), 42);
72/// let (id, event) = claim.into_parts();
73/// assert_eq!(id.into_inner(), "message-1");
74/// assert_eq!(event, 42);
75/// ```
76#[derive(Debug, Eq, PartialEq)]
77pub struct OutboxClaim<E> {
78    message_id: OutboxMessageId,
79    event: E,
80}
81
82impl<E> OutboxClaim<E> {
83    /// **English:**<br>
84    /// Pairs `message_id` with `event` without contacting an outbox adapter.
85    ///
86    /// **日本語:**<br>
87    /// アウトボックスアダプターにアクセスせず、`message_id` と `event` を組にします。
88    pub fn new(message_id: OutboxMessageId, event: E) -> Self {
89        Self { message_id, event }
90    }
91
92    /// **English:**<br>
93    /// Borrows the identifier used to acknowledge this message.
94    ///
95    /// **日本語:**<br>
96    /// このメッセージの確認応答に使う識別子を借用します。
97    pub fn message_id(&self) -> &OutboxMessageId {
98        &self.message_id
99    }
100
101    /// **English:**<br>
102    /// Borrows the claimed payload without removing it from this container.
103    ///
104    /// **日本語:**<br>
105    /// このコンテナーから取り出さずに、取得したペイロードを借用します。
106    pub fn event(&self) -> &E {
107        &self.event
108    }
109
110    /// **English:**<br>
111    /// Consumes the claim and returns the identifier and event without cloning.
112    /// This does not acknowledge the message.
113    ///
114    /// **日本語:**<br>
115    /// 取得結果を消費し、複製せずに識別子とイベントを返します。
116    /// この操作はメッセージの確認応答を行いません。
117    pub fn into_parts(self) -> (OutboxMessageId, E) {
118        (self.message_id, self.event)
119    }
120}
121
122/// **English:**<br>
123/// Persistence boundary for saving an aggregate and staging events for delivery.
124///
125/// An adapter supplies the transaction joining aggregate persistence and outbox insertion;
126/// this trait contains no transaction implementation. Adapters must describe atomicity,
127/// durability, concurrent claims, claim order, retry/lease policy, and panic conditions.
128/// Claiming and acknowledging are separate from saving; no exactly-once delivery mechanism
129/// is supplied, so consumers may need to handle duplicate events.
130///
131/// **日本語:**<br>
132/// 集約の保存と配信対象イベントの登録を行う永続化境界です。
133///
134/// 集約の永続化とアウトボックス挿入を結ぶトランザクションはアダプターが提供し、
135/// このトレイト自体には実装しません。原子性、永続性、並行取得、取得順序、再試行・
136/// リース方針、パニック条件はアダプターが記述する必要があります。取得と確認応答は
137/// 保存から分離されています。厳密に一度だけ配信する機構は提供しないため、
138/// コンシューマー側で重複イベントへの対応が必要になる場合があります。
139pub trait TransactionalOutbox {
140    /// **English:**<br>
141    /// Aggregate whose state and associated events are staged together.
142    ///
143    /// **日本語:**<br>
144    /// 状態と関連イベントをまとめて登録する対象の集約です。
145    type Aggregate: AggregateRoot;
146    /// **English:**<br>
147    /// Adapter failure used by save, claim, and acknowledgement operations.
148    ///
149    /// **日本語:**<br>
150    /// 保存、取得、確認応答が使うアダプター障害です。
151    type Error;
152
153    /// **English:**<br>
154    /// Borrows `aggregate` and consumes `events` to save and enqueue under `expected`.
155    /// Returns the resulting stream revision, or [`AppendError`] for an empty batch, conflict,
156    /// revision exhaustion, or adapter failure. Events are supplied explicitly; this signature
157    /// does not pull or clear the aggregate's pending events. The batch is not returned on error;
158    /// consult the adapter's transaction contract before retrying.
159    ///
160    /// **日本語:**<br>
161    /// `aggregate` を借用し、`events` を消費して `expected` を条件に保存・登録します。
162    /// 結果のストリームリビジョン、または空バッチ、競合、リビジョン枯渇、アダプター障害を
163    /// 示す [`AppendError`] を返します。イベントは明示的に渡します。このシグネチャは
164    /// 集約の保留イベントを取り出したり消去したりしません。エラー時にバッチは返されないため、
165    /// 再試行する前にアダプターのトランザクション契約を確認してください。
166    fn save_and_enqueue(
167        &mut self,
168        aggregate: &Self::Aggregate,
169        expected: ExpectedRevision,
170        events: Vec<<Self::Aggregate as AggregateRoot>::Event>,
171    ) -> Result<AppendResult, AppendError<Self::Error>>;
172
173    /// **English:**<br>
174    /// Claims an available message, returning its owned identifier and event.
175    /// `Ok(None)` means no message is available to this call; adapter failures use `Self::Error`.
176    /// Reservation duration and eligibility for redelivery are not represented by [`OutboxClaim`].
177    ///
178    /// **日本語:**<br>
179    /// 利用可能なメッセージを取得し、所有された識別子とイベントを返します。
180    /// `Ok(None)` はこの呼び出しで取得できるものがないことを表し、障害は `Self::Error` です。
181    /// 予約期間と再配信の条件は [`OutboxClaim`] では表現しません。
182    fn claim(
183        &mut self,
184    ) -> Result<Option<OutboxClaim<<Self::Aggregate as AggregateRoot>::Event>>, Self::Error>;
185
186    /// **English:**<br>
187    /// Reports completed handling of `message_id`, returning an adapter error on failure.
188    /// Unknown IDs, repeated acknowledgements, and removal or retention of acknowledged messages
189    /// follow the adapter's policy. No claim token or ownership proof is required by this API.
190    ///
191    /// **日本語:**<br>
192    /// `message_id` の処理完了を通知し、失敗時にアダプターエラーを返します。
193    /// 未知の識別子、確認応答の繰り返し、応答済みメッセージの削除・保持はアダプターの
194    /// 方針に従います。この API は取得トークンや所有権の証明を要求しません。
195    fn acknowledge(&mut self, message_id: &OutboxMessageId) -> Result<(), Self::Error>;
196}