web-dev-qa-db-ja.com

DDDの例外

私はDDDを学んでいて、特定の状況で例外をスローすることを考えています。私はオブジェクトが悪い状態に入ることができないことを理解しているので、ここで例外は問題ありませんが、多くの例では、たとえば、データベースにメールが存在する新しいユーザーを追加しようとした場合にも例外がスローされます。

public function doIt(UserData $userData)
{
    $user = $this->userRepository->byEmail($userData->email());
    if ($user) {
        throw new UserAlreadyExistsException();
    }

    $this->userRepository->add(
        new User(
            $userData->email(),
            $userData->password()
        )
    );
}

したがって、この電子メールを持つユーザーが存在する場合、アプリケーションサービスで例外をキャッチできますが、try-catchブロックを使用してアプリケーションの操作を制御するべきではありません。

これに最適な方法は何ですか?

11
yadiv

問題空間の簡単な復習から始めましょう。DDDの基本的な原則の1つは、ビジネスルールを適用する必要のある場所にできるだけ近づけることです。これは非常に重要な概念です。これは、システムをより「凝集性」にするためです。ルールを「上に」移動することは一般的に貧血モデルの兆候です。ここで、オブジェクトは単なるデータのバッグであり、ルールには適用されるデータが注入されます。

貧血モデルは、DDDを使い始めたばかりの開発者には非常に理にかなっています。電子メールを検証するために必要な情報が注入されるUserモデルとEmailMustBeUnqiueRuleを作成します。シンプル。エレガント。問題は、この「一種の」思考が根本的に手続き型であるということです。 DDDではありません。何十ものRulesがきちんとラップされてカプセル化されたモジュールが残っていますが、いつ/どこでそれらが明確でないため、変更できなくなるまでのコンテキストが完全になくなります。強制されます。それは理にかなっていますか? mayEmailMustBeUnqiueRuleUserの作成に適用されることは自明ですが、UserIsInGoodStandingRule?はどうでしょうか。ゆっくりではあるが確実に、コンテキストからRulesを抽出する細分化により、理解しにくい(したがって変更できない)システムが残ります。ルールをカプセル化する必要があるのは、実際のクランチ/実行が非常に詳細であり、モデルがフォーカスを失い始めた場合のみです。

さて、あなたの特定の質問に:Service/CommandHandlerExceptionをスローさせることの問題は、ビジネスロジックがドメイン。 Service/CommandHandlerがメールが一意でなければならないことを知る必要があるのはなぜですか?アプリケーションサービスレイヤーは一般にであり、実装ではなく調整に使用されます。この理由は、ChangeEmailメソッド/コマンドをシステムに追加した場合に簡単に説明できます。これで、メソッド/コマンドハンドラーの両方に独自のチェックを含める必要があります。これは、開発者がEmailMustBeUniqueRuleを「抽出」する誘惑に駆られる可能性がある場所です。上で説明したように、そのルートには行きたくありません。

追加の知識を駆使すると、よりDDDの答えを導き出すことができます。メールの一意性は不変であり、Userオブジェクトのコレクション全体に適用する必要があります。 「Userオブジェクトのコレクション」を表す概念がドメインにありますか?たぶん私がどこに行くのかわかると思います。

この特定のケース(およびコレクション全体で不変式を適用することを含む多くのケース)では、このロジックを実装するのに最適な場所はRepositoryです。 Repositoryは、この種の検証を実行するために必要な追加のインフラストラクチャ(データストア)も "知っている"ため、これは特に便利です。あなたの場合、私はこのチェックをaddメソッドに入れます。これは理にかなっていますか?概念的には、システムにUserを本当に追加するのはこの方法です。データストアは実装の詳細です。

20
king-side-slide

アプリケーションのすべての層に検証を課すことができます。アプリケーションに依存するルールを課す場所。

たとえば、エンティティはビジネスルールを表すメソッドを実装し、ユースケースはアプリケーションに固有のルールを実装します。

Gmailのような電子メールサービスを構築している場合、「ユーザーには一意の電子メールアドレスが必要です」というルールはビジネスルールであると主張できます。

新しいCRMシステムの登録プロセスを構築する場合、このルールはユースケースの一部である可能性が高いため、このルールはユースケースで実装するのが最適です。さらに、ユースケーステストでリポジトリの呼び出しを簡単にスタブできるので、テストが簡単になる場合もあります。

追加のセキュリティのために、このルールをリポジトリに適用することもできます。

0
thenoob