DDDによると、ドメインロジックは、シリアル化、オブジェクトリレーショナルマッピングなどの技術的な問題で汚染されるべきではありません。
では、ゲッターとセッターを介して集計を公開せずに、集計の状態をどのようにシリアル化またはマップするのでしょうか。例えば、私はたくさんの例を見てきました。リポジトリの実装ですが、実際にはすべて、エンティティと値オブジェクトのパブリックアクセサーに依存してマッピングを行っています。
リフレクションを使用してパブリックアクセサーを回避することもできますが、IMOこれらのドメインオブジェクトは、依然として暗黙的にシリアル化の問題に依存しています。例えば。シリアル化/マッピング構成を調整せずにプライベートフィールドの名前を変更したり削除したりすることはできません。したがって、ドメインロジックに焦点を当てる代わりに、シリアル化を検討する必要があります。
ここで従うのに最適な妥協点は何ですか?パブリックアクセサーと一緒に暮らしますが、マッピングコード以外には使用しないでください。それとも私は明白な何かを逃しただけですか?
DDDドメインオブジェクト(エンティティと値オブジェクトで構成される集合体)の状態をシリアル化することに明確に関心があります。これは、一般的なシリアライゼーションや、ステートレスサービスが単純なデータコンテナオブジェクトを操作する transcation script シナリオに関するものではありません。
説明のために、オブジェクトを3つの異なる種類に分けましょう。
これらは、作業を完了するオブジェクトです。彼らは、1つの当座預金から別の当座預金にお金を移動し、注文を処理し、ビジネスソフトウェアが実行することを期待する他のすべてのアクションを実行します。
ドメインロジックオブジェクトは通常、アクセサー(ゲッターとセッター)を必要としません。むしろ、コンストラクタを介して依存関係を渡すことによってオブジェクトを作成し、メソッドを介してオブジェクトを操作します(教えてください、尋ねないでください)。
データ転送オブジェクトは、純粋な状態です。ビジネスロジックは含まれていません。彼らは常にアクセサーを持っています。 immutableの方法でそれらを書き込んでいるかどうかに応じて、セッターがある場合とない場合があります。コンストラクターでフィールドを設定し、その値がオブジェクトの存続期間中変更されないか、またはアクセサーが読み取り/書き込み可能になります。実際には、これらのオブジェクトは通常、変更可能であるため、ユーザーが編集できます。
ビューモデルオブジェクトには、表示可能/編集可能なデータ表現が含まれています。通常、データ検証に限定されたビジネスロジックが含まれる場合があります。ビューモデルオブジェクトの例としては、Customerオブジェクト、Invoiceヘッダーオブジェクト、およびInvoice Line Itemsを含むInvoiceViewModelがあります。ビューモデルオブジェクトには常にアクセサーが含まれます。
したがって、フィールドアクセサーを含まないという意味で「純粋」になるオブジェクトの種類は、ドメインロジックオブジェクトだけです。このようなオブジェクトをシリアル化すると、その現在の「計算状態」が保存されるため、後で取得して処理を完了することができます。ビューモデルとDTOは自由にシリアル化できますが、実際にはそれらのデータは通常データベースに保存されます。
シリアル化によって依存関係が作成されることは事実ですが、互換性のあるオブジェクトに逆シリアル化する必要があるという意味では、シリアル化構成を変更する必要があるとは限りません。優れたシリアル化メカニズムは汎用です。プロパティまたはメンバーの名前を変更しても、値をメンバーにマップできる限り、それらは関係ありません。実際には、これは、オブジェクトインスタンスを再シリアル化して、シリアル化表現(xml、jsonなど)を新しいオブジェクトと互換にする必要があることを意味します。シリアライザの構成を変更する必要はありません。
オブジェクトがシリアル化された方法に関係するべきではないことは事実です。このような懸念をドメインクラスから分離できる1つの方法、つまりリフレクションについてはすでに説明しました。しかし、シリアライザは、オブジェクトをシリアライズおよびデシリアライズする方法を心配する必要があります。結局のところ、それがその機能です。オブジェクトをシリアル化プロセスから切り離しておく方法は、シリアル化を汎用関数にして、すべてのオブジェクトタイプで機能できるようにすることです。
人々が混乱することの1つは、デカップリングが両方向で発生する必要があることです。ありません。 1つの方向にのみ機能する必要があります。実際には、完全に分離することはできません。常にsomeカップリングがあります。疎結合の目的は、コードのメンテナンスを容易にすることであり、すべての依存関係を削除することではありません。
シリアライゼーションの基本的な目的は、1つのシステムで生成されたデータを1つ以上の互換性のあるシステムで確実に使用できるようにすることです。
シリアライゼーションに対する最も簡単で堅牢なアプローチは、データを型にとらわれない形式に変換し、構造をシンプルで使いやすい形式に維持することです。たとえば、最も一般的なシリアル化形式(JSON、XMLなど)では、明確に定義されたテキストベースの形式を使用しています。テキストは簡単に作成、送信、使用できます。
これらのフォーマットのいずれかを使用することが理想的でない可能性がある理由は2つあります。
効率
すべてのデータをテキストベースの同等物に変換するには、固有のコストがかかります。さまざまな形式のデータをすべて表現する最も効率的な方法がテキストである場合、データ型は存在しません。また、これらのフォーマットの構造は、データのサブセットを非同期または部分的に取得するのには理想的ではありません。
たとえば、XMLとJSONは、使用されるデータが最初から最後まで書き込まれ、読み取られることを前提としています。メモリが不足している非常に大きなデータセットを処理する場合、データを消費するシステムでは、データを部分的に処理する機能が必要になる場合があります。その場合、データを処理するために、特別な目的のシリアライゼーション/デシリアライゼーションの実装が必要になることがあります。
精度
意図した型からデータにとらわれない型にデータをシリアル化/逆シリアル化するために必要なキャストでは、精度が失われます。
オブジェクトとデータのバイナリ表現を生成することが、最も効率的で正確なソリューションであることは明らかです。主な欠点は、データを消費および生成するすべてのシステムの実装に互換性を維持する必要があることです。これは理論的には単純な制約ですが、実動システムは時間とともに変化/進化する傾向があるため、実際に維持することは悪夢です。
とは言うものの。シリアル化/非シリアル化をドメイン固有の詳細から切り離すことは、一般的なルールとして理にかなっています。これは、汎用フォーマットがより堅牢であり、さまざまなシステムでより適切にサポートされ、使用するメンテナンスオーバーヘッドがほとんどまたはまったく必要ないためです。