コンテンツにスキップ
検索語を入力してください

    クレートガイド

    thiserrorserdenutype などは Kamae のドメイン規約を補助するクレートである。トピック別リファレンス(エラーハンドリング、境界防御など)と矛盾する場合は、そちらを優先する。

    ここでは「よくある組み合わせ」とデフォルトの置き場所をまとめる。個別の設計判断は エラーハンドリング境界防御ドメインモデリングPII 保護 を参照する。

    用途ガイド付きクレート検出のみ(ローカル慣習の参考)
    エラーthiserroranyhoweyresnafu
    シリアライズserdeserde_jsontomlconfig
    検証 / newtypevalidatorgardenutypederive_more
    PII / シークレットsecrecyzeroize
    ログ / トレースtracinglogmetricsopentelemetryprometheus
    テストproptestquickchecktrybuild

    ドメイン・ユースケースの thiserror 列挙型を導入・整備するときに参照する。詳細は エラーハンドリング を優先する。

    #[derive(Debug, thiserror::Error)]
    pub enum DomainError {
    #[error("invalid request id")]
    InvalidRequestId,
    }

    バリアントは意味論的に保つ。アプリケーション境界でインフラ失敗を包む場合を除き、ドメインエラーに Other(String) のような catch-all は避ける。

    スタックパターントピックガイド
    thiserror + serde boundaryTryFrom<Dto>type Error = CommandError境界防御
    thiserror + sqlxadapter 境界で RepositoryErrorsqlx::Error を包む永続化、集約、イベント
    thiserror + transitionsAssignDriverError が domain / not-found / conflict を分離状態遷移永続化、集約、イベント

    anyhow / eyre報告境界向けである。main、HTTP ハンドラ、移行スクリプト、接着コードでは、多様な失敗を 1 本のチェーンにまとめてよい。

    ドメインのコンストラクタやユースケースの戻り値に anyhow::Result を使うと、呼び出し元が網羅的に分岐できなくなる。ドメイン層は具体的な enumResult を返し、ハンドラで HTTP ステータスやログ用メッセージに変換する。

    外部形状には serde DTO を使い、ドメイン型に変換する。詳細は 境界防御 を優先する。

    デシリアライズが検証を迂回したり不可能な状態を許したりする場合、ドメインエンティティに直接 Deserialize を付けない。

    出力が意図的で secret を含まない読み取りモデルでは、ドメインへの Serialize も許容されうる。PII については redaction を制御する明示的な response DTO をシリアライズする。

    検証付き値オブジェクトに try_from を使う

    Section titled “検証付き値オブジェクトに try_from を使う”

    小さな不変条件付き値オブジェクトでは、serde パスが通常コードと同じ検証コンストラクタに委譲するなら Deserialize も許容される:

    #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
    #[serde(try_from = "String")]
    pub struct EmailAddress(String);
    impl TryFrom<String> for EmailAddress {
    type Error = EmailAddressError;
    fn try_from(value: String) -> Result<Self, Self::Error> {
    EmailAddress::new(value)
    }
    }

    ID、メール、スラッグ、有界数量などのリーフ値オブジェクト向け。集約、エンティティ、state 型、コマンド、複数フィールドをまとめて検証するものは DTO -> TryFrom を優先する。

    テストや persistence の都合だけで、不変条件付き型に無制限 Serialize/Deserialize を derive しない。シリアライズ形式が公開契約でないなら DTO または row 型に留める。

    スタックパターントピックガイド
    serde + thiserrorDTO DeserializeTryFrom が型付き error enum を返す境界防御
    serde + sqlxrow struct のみ FromRow、ドメインへ TryFrom境界防御永続化、集約、イベント
    serde + eventsドメイン event enum に #[serde(tag = "event_type")]永続化、集約、イベント
    serde + gardeTryFrom 前に DTO を garde で検証garde

    プロジェクトが derive ベースのリクエスト検証をすでに使う場合、DTO 向け validator を使う。

    検証済み DTO もドメイン newtype に変換する。validation derive は DTO 境界をチェックする。ドメインコンストラクタが他のすべての構築経路の不変条件を保つ。

    #[derive(serde::Deserialize, validator::Validate)]
    pub struct CreateUserDto {
    #[validate(email)]
    email: String,
    }

    プロジェクトが composable な検証ルール付き derive ベース検証を好む場合、DTO 向け garde を使う。

    ドメインコンストラクタを権威とする。DTO 検証ルールだけがドメイン不変条件の唯一の所在にならないようにする。

    スタックパターントピックガイド
    garde + serde + axumJson<Dto> -> dto.validate() -> Command::try_from(dto)境界防御
    garde + thiserroradapter で garde report を境界 error enum にマップエラーハンドリング
    garde + leaf newtypesDTO フィールド検証 + ドメイン newtype 向け TryFromドメインモデリング

    garde は DTO 形状を検証する。TryFrom はドメイン意味(フィールド横断ルール、テナントスコープ、ID 意味論)の権威のままである。

    プロジェクトがすでに nutype を使う場合、または多数の検証付き newtype でボイラープレートが繰り返される場合に newtype 向けに使う。詳細は ドメインモデリング を優先する。

    フィールドは private と生成コンストラクタを優先する。型名は意味論的に保つ(EmailAddressOrderIdMoneyAmount)。意味をぼかす汎用 wrapper は避ける。

    Debug 出力に現れてはならず、メモリに必要以上に残してはならない資格情報などの secret 向けに secrecy を使う。詳細は PII 保護 を優先する。

    個人データ(PII)は Redacted<T> または custom Debug 付きドメイン newtype を優先する。

    secret は SecretString または SecretBox 周りのプロジェクト固有 wrapper で保持する。ExposeSecret 経由の狭い adapter 関数でのみ値を露出する。

    露出した secret 値を error バリアントに含めない。

    スタックパターントピックガイド
    secrecy + adapterpayment/auth モジュールのみ ExposeSecretPII 保護
    secrecy + tracingSecretString をログしない。資格情報 struct は skipPII 保護
    PII vs secrets個人データは Redacted<T>、資格情報は secrecyPII 保護

    crate がすでに依存している場合、または property test が入力全体の法則を最も明確にカバーできる場合に、ドメイン不変条件テスト向け proptest を使う。generator 設計、state machine property、CI 予算、regression ファイルは プロパティベーステスト を参照する。

    [dev-dependencies] に置く。public コンストラクタを呼ぶ strategy を優先し、無効なドメイン状態を直接構築しない。

    use proptest::prelude::*;
    proptest! {
    #[test]
    fn round_trip(input in strategy()) {
    // assert law
    }
    }