2013-03-15 2 views
5

dddでの検証方法について質問があります。私はそれについてかなり議論のある意見を読んだ。これは実体から生き残るべきだと言う人もいれば、これはエンティティに置くべきだと言う人もいます。私は私が続けることができるアプローチを見つけようとしています。dddでの検証方法

たとえば、メールとパスワードを持つUserエンティティがあるとします。ユーザーには登録(電子メール、パスワード)メソッドがあります。電子メールとパスワードの検証はどこに置かれるべきですか?私の個人的な意見は、Register()メソッドの内部にあるべきだということです。しかし、そのようなアプローチでは、検証クラスを使ってUserクラスを駄目にする可能性があります。 1つの方法は、別々のポリシーオブジェクトで電子メールとパスワードのルールを抽出し、引き続きRegister()メソッドからそれらを呼び出すことです。

DDDの検証手法についてご意見はありますか?

答えて

3

実際には、ユーザーの有効性はコンテキストに依存します。ユーザーは有効です(名前と電子メールは有効です)が、登録操作は実行できません。どうして?同じ名前の既存のユーザーが存在する可能性があるためです。したがって、いくつかのコンテキストで有効と思われるユーザーは、登録コンテキストでは無効になる可能性があります。

したがって、エンティティの無効な状態(例:nullという名前のユーザー)を避けるため、検証の一部をエンティティまたは値オブジェクト(例:Mailオブジェクト)に移動しました。しかし、文脈に依存した検証がその文脈に存在するはずです。私は、ユーザーを登録してるのであれば、:

Mail mail = new Mail(blahblahblah); // validates if blah is valid email 
User user = new User(name, mail); // validates if name is valid and mail not null 
// check if there already exist user with such name or email 
repository.Add(user); 

はまた、私はそれは間違いなくいくつかのリポジトリを使用する必要がありますので、この方法Registerは、(MembershipServiceのような)いくつかのドメインサービスの方法であるべきだと思います。

+0

Register()は、パスワード、電子メール、登録日、登録ステータスなどのデータでユーザーを初期化します。リポジトリは呼び出されません。これは、セキュリティアプリケーションサービスの責任です。しかし、それはここでの主な問題ではありません。私はエンティティ操作の例を提供し、検証をどこに置くべきかを分析しようとしました。 – Markus

+0

@ Markusそのようなメソッド名が混乱していると思います。 –

5

まず、バリデーションは、それを考慮する必要がある様々なcontextsに部分的に起因する滑りやすい主題だと思います。最終的に、さまざまなアプリケーション層で実行される検証ルールが存在します。少なくとも、ドメインオブジェクトには標準ガードが存在する必要があります。これらは、よく設計されたオブジェクトの一部であり、Reigsterメソッドのあなたの意見に合った正規の事前条件と引数チェックです。 lazyberezovskyが述べたように、これはオブジェクトが無効な状態にならないようにするためです。私は常に有効な学校と一緒にいます。エンティティを無効な状態にしておく必要がある場合は、この目的のために新しいエンティティを作成する必要があります。

ただし、このアプローチだけでは、これらの検証ルールをプレゼンテーションレイヤーなどの他のレイヤーにエクスポートする必要があることがあります。さらに、プレゼンテーションレイヤーでは、ルールの形式を変更する必要があります。クライアント側からの即時のフィードバックのために、それらを一度にすべて提示し、潜在的に別の言語に翻訳する必要があります。クラスによって発生した例外から検証ルールを抽出しようとするのは、難しく、または実際的ではありません。代替的に、バリデーションルールは、プレゼンテーション層で再作成することができる。これははるかに簡単ですが、潜在的にDRYに違反しますが、ルールはコンテキストに依存します。特定のワークフローでは、エンティティ自体によって実行されるものとは異なる検証ルールが必要になる場合があります。

説明されたアプローチのもう1つの問題は、エンティティの範囲外に存在する検証ルールが存在する可能性があり、これらのルールを他のルールとともに統合する必要があることです。たとえば、ユーザー登録の場合、別のルールは電子メールアドレスが一意であることを確認することです。適用可能なユースケースをホストしているアプリケーションサービスは通常、このルールを適用します。ただし、プレゼンテーションなどの他のレイヤーにこのルールをエクスポートすることもできる必要があります。

全体的に、私はエンティティが常に有効であるべきだと思うので、エンティティ自体に多くの制約チェックを配置しようとします。ルールフレームワークを設計して、例外を発生させたり、外部レイヤにエクスポートできるようにすることが可能な場合もあります。それ以外の場合は、レイヤー間で単純にルールを複製する方が簡単です。

2

たとえば、電子メールとパスワードを持つUserエンティティがあるとします。ユーザーには登録(電子メール、パスワード)メソッドがあります。電子メールとパスワードの検証はどこに置かれるべきですか?

DDDに従っている場合は、電子メールアドレスとユーザー名がドメインの重要な概念であるため、エンティティとして表現する必要があります。その場合、電子メールアドレスの検証はEmailAddressエンティティにあり、ユーザー名の検証はUsernameエンティティに配置する必要があります。

擬似コード例:検証とビジネス・ルール後者は、通常、サービスまたはエンティティのメソッドに行く:私は二つのことの間で分離されるすべてのの

class EmailAddress 
{ 
    Constructor(string email) 
    { 
     if (email.DoesNotMatchRegex("\[email protected]\w+.\w{2,3}")) 
      throw error "email address is not valid" 
    } 
} 

class Username 
{ 
    Constructor(string username) 
    { 
     if (username.length < 6) 
      throw error "username must be at least 6 characters in length" 
    } 
} 

class User 
{ 
    Register(Username username, EmailAddress email) 
    { 
     if (username == null) 
      throw error "user must have a username" 

     if (email == null) 
      throw new error "user must provide email address" 

     // At this point, we know for sure that the username and email address are valid... 
    } 
} 
+1

このアプローチがうまく機能することを確認できます。私はそれだけを追加することにします[例外はユビキタス言語の用語です](http://epic.tesio.it/2013/03/04/exceptions-are-terms-ot-the-ubiquitous-language.html)。 –

+0

値オブジェクトUsernameとEmailAddressはありませんか? –

+0

@Barthelomeusはい、彼らはバリューオブジェクトの優れた候補になるでしょう。エンティティ/値オブジェクトとクラス/構造体との間のマッピングは必ずしも*必須ではありません。クラスは値オブジェクトにすることができますが、それらの行に沿って区別することはしばしば役に立ちます。 – MattDavey

0

まず。有効な電子メールアドレスや有効なパスワードなどの検証のために、私は通常、それらを所有するエンティティに貼り付けていますので、アプリケーションを別のプラットフォームに移植した場合、エンティティでそれらを移動できます。今あなたの登録メソッドの例については、私はなぜオブジェクトが無効な状態にあった場合、最初にこのメソッドが呼び出されるのかわかりません、そして、それが呼び出されるならば、私はそれをSignUpのような別のメソッドこのようなもの:if (isValid()) register(username, password)