ddd-toolkit 0.3.1

Domain-Driven Design building blocks for Rust: a facade over ddd-toolkit-core and its derive macros.
Documentation
# ddd-toolkit

[English]README.md | 日本語

[![crates.io]https://img.shields.io/crates/v/ddd-toolkit.svg]https://crates.io/crates/ddd-toolkit
[![docs.rs]https://img.shields.io/docsrs/ddd-toolkit]https://docs.rs/ddd-toolkit
[![CI]https://github.com/ryoshrimp/ddd-toolkit/actions/workflows/ci.yml/badge.svg]https://github.com/ryoshrimp/ddd-toolkit/actions/workflows/ci.yml
[![license]https://img.shields.io/crates/l/ddd-toolkit.svg]#ライセンス

ドメイン駆動設計のトレイトとヘルパーを提供するファサードクレートです。
[`ddd-toolkit-core`](crates/core) 0.4.1 の公開 API と、
[`ddd-toolkit-macro`](crates/macro) 0.4.1 の四つの derive マクロを再エクスポートします。
公開 API はすべて `ddd_toolkit::` から利用できます。

最新の Rust stable(edition 2024)で検証しています。
最低サポート Rust バージョン(MSRV)は定めていません。

このリポジトリは、ファサード・core・macro を含む Cargo ワークスペースです。
crates.io では別々のパッケージとして公開し、開発とテストを一緒に行います。
開発手順は [CONTRIBUTING.md](CONTRIBUTING.md)、公開手順は
[リリースガイド](docs/releasing.md)を参照してください。

## インストール

トレイトとヘルパーだけを使い、derive マクロを使わない場合:

```toml
[dependencies]
ddd-toolkit = { version = "0.3", default-features = false }
```

derive マクロを使う場合(デフォルトで有効):

```toml
[dependencies]
ddd-toolkit = "0.3"
ddd-toolkit-core = "0.4.1"
```

**重要:** マクロ 0.4 は、ファサード経由で呼び出す場合も利用側クレートに
`ddd-toolkit-core` の直接依存を要求します。コア依存の別名には対応しています。
以前のようにファサードだけに依存する構成では derive を利用できません。
マクロの再エクスポートによって依存の解決方法が変わるわけではありません。

ファサード 0.3.0 は core/macro 0.4.0 を採用しており、ファサード 0.2 から
破壊的な API 変更があります。後述の移行ガイドと [CHANGELOG.md](CHANGELOG.md)を参照してください。

## 提供する機能

- `Entity`、`ValueObject`、`AggregateRoot`、`DomainEvent` のトレイトと derive。
- `Repository` / `AsyncRepository`、`DomainEventPublisher` /
  `AsyncDomainEventPublisher`、`dispatch_events` / `dispatch_events_async`。
- CQRS のコマンド/クエリハンドラーとライフサイクルコンテキスト。
- イベントストア、リビジョンの型、集約の再生、スナップショット。
- イベント処理、プロジェクション、チェックポイント、トランザクショナルアウトボックスの契約。
- 仕様、ドメインサービス/ポリシー/ファクトリー、プロセスマネージャー、
  Unit of Work、イベントのアップキャスター。
- 書式出力の秘匿化と破棄時のゼロ化を行う `Secret<T>`。

コアが定義するのは契約です。ストレージアダプター、時計、ID 生成器、非同期ランタイムは
提供せず、トランザクションや配信も自動的には保証しません。アダプターはアプリケーション側で
実装してください。非同期ポートは、ランタイムを選ばずにボックス化された `Send` Future を返します。

## 使用例

上記の二つの依存を追加した場合:

```rust
#[derive(ddd_toolkit::ValueObject)]
#[value_object(validate = validate_email, error = &'static str)]
struct Email(String);

fn validate_email(value: &str) -> Result<(), &'static str> {
    if value.contains('@') { Ok(()) } else { Err("missing @") }
}

fn main() {
    let email = Email::try_new("a@b.com".to_owned()).unwrap();
    assert_eq!(email.as_inner(), "a@b.com");
    assert_eq!(Email::try_new("invalid".to_owned()), Err("missing @"));

    let key = ddd_toolkit::Secret::new("sekret".to_owned());
    assert_eq!(format!("{key:?}"), "[REDACTED]");
    assert_eq!(key.expose_secret(|value| value.len()), 6);
}
```

`ValueObject` は `Debug`、`Clone`、`PartialEq`、`Eq`、`Hash` を生成するため、
重複して derive しないでください。検証なしの構造体では `new`、検証付きでは
`try_new` を使います。名前付き・複数フィールドの構造体と、データを持つ列挙型にも対応します。
`Entity` は `#[entity(id)]` フィールドだけを使った等値比較とハッシュを生成します。

### 集約イベントの発行

```rust
use ddd_toolkit::{AggregateRoot as _, DomainEventPublisher, dispatch_events};

#[derive(Debug, Clone, PartialEq, ddd_toolkit::DomainEvent)]
struct OrderPlaced;

#[derive(ddd_toolkit::Entity, ddd_toolkit::AggregateRoot)]
struct Order {
    #[entity(id)]
    id: u64,
    #[aggregate(events)]
    events: Vec<OrderPlaced>,
}

struct Publisher;

impl DomainEventPublisher for Publisher {
    type Event = OrderPlaced;
    type Error = &'static str;

    fn publish(&mut self, _event: &OrderPlaced) -> Result<(), Self::Error> {
        Ok(())
    }
}

fn main() {
    let mut order = Order { id: 1, events: vec![] };
    order.record_event(OrderPlaced);
    dispatch_events(&mut order, &mut Publisher).unwrap();
    assert!(order.pull_events().is_empty());
}
```

リポジトリによる永続化とイベント発行は別の操作です。`Repository::save` は集約を
不変で借用し、保留イベントを自動的には取り出しません。`dispatch_events` は成功時に
イベントを取り出してキューを空にし、発行失敗時には取り出した**すべての**イベントを復元します。
発行済みのイベントはロールバックしないため、再試行で重複配信される可能性があります。
トランザクションの保証はアプリケーション側のアダプターが定めます。

## Feature

| Feature | デフォルト | 有効になる機能 |
|---------|---------|---------|
| `derive` | 有効 | `ValueObject`、`Entity`、`DomainEvent`、`AggregateRoot` の derive。利用側にはコアの直接依存も必要 |
| `serde` | 無効 | コアの serde 再エクスポートと、`#[value_object(serde)]` に必要な serde derive 対応。`derive` とは独立 |

値オブジェクトをシリアライズするには、ファサードの `serde` feature を有効にし、
型ごとに次の属性を指定します。

```rust
#[derive(ddd_toolkit::ValueObject)]
#[value_object(serde)]
struct UserId(String);
```

生成された実装のために `serde` へ直接依存する必要はありません。
検証付きのデシリアライズでは検証関数を実行し、失敗を `value object validation failed` に
変換します。列挙型は外部タグ形式を使います。同じ属性で検証と併用するときは
`#[value_object(validate = check, error = Error, serde)]` のように `serde` を最後に置いてください。
マクロ 0.4.1 は、フラグの後ろに他のオプションを記述すると拒否します。

**マクロ 0.4.1 の制限:** `Code(u32)` のような単一フィールドのタプルバリアントは
`{"Code":[7]}` にシリアライズしますが、生成されたデシリアライザーは `{"Code":7}` を
期待するため、自身の JSON 出力を復元できません。JSON の往復変換が必要な場合は
`Code { value: u32 }` のような名前付きバリアントを使ってください。

Clippy 1.99 は、マクロ 0.4.1 が生成する名前付き値オブジェクトのコンストラクターに
`clippy::redundant_field_names` を報告します。`-D warnings` を使う場合、生成器が修正されるまでは
利用側モジュールでこの lint に限定した許可が必要になる場合があります。
`Secret<T>` は feature の指定なしで使え、自動的にはシリアライズされません。
`Secret::into_inner` を呼び出した後のゼロ化は呼び出し側の責任です。

## core/macro 0.3 からの移行

- `domain`、`port`、`adapter`、`mock`、`application` のモジュールパスをルートからの
  インポートに置き換えてください。組み込みアダプター/テストダブルと `UseCase` は廃止されました。
- `EntityId` / `EnumVo` の derive を適切な `ValueObject` 型に置き換えてください。
  `Wrapped` の代わりに `ValueObject` の `as_inner` / `into_inner` を使います。
- `#[vo(validate = "function")]` を
  `#[value_object(validate = function, error = YourError)]` に置き換えてください。
  `ValidationError` は廃止され、エラー型は利用側で選びます。
- 生成された `TryFrom` / `From` / `Display` の使用を、コンストラクターや必要に応じた
  アプリケーション側の明示的な実装に置き換えてください。
- `SecretVo` / `#[vo(zeroize)]` を `Secret<T>` に置き換えてください。
- `record` / `take_events` を `record_event` / `pull_events` に置き換えてください。
- `Load` / `Save` / `Delete` を `Repository` または `AsyncRepository` に、
  `EventDispatcher` をイベントパブリッシャーの契約に置き換えてください。
  `PortError` は廃止され、エラー型はアダプターが選びます。
- `chrono`、`uuid`、`zeroize` の feature 指定を削除してください。
- derive を使う場合はコアの直接依存を追加し、serde は値オブジェクトごとに有効にしてください。

詳細は [API ドキュメント](https://docs.rs/ddd-toolkit)を参照してください。
[`facade-tests`](facade-tests/tests) では、独自リポジトリ、非同期ポート、複数集約の処理、
発行失敗と再試行、derive、serde、秘密値をファサード経由で検証しています。

## 変更履歴

[CHANGELOG.md](CHANGELOG.md)を参照してください。

## ライセンス

[Apache License, Version 2.0](LICENSE-APACHE) または [MIT license](LICENSE-MIT) の
いずれかを選択して利用できます。