ドメイン駆動設計(DDD)でルックアップテーブルをモデル化する実用的な方法は何ですか?
-
03-07-2019 - |
質問
私はちょうどDDDを学んでいます(Eric Evansの本は私の前で公開されています)が、答えが見つからない問題に遭遇しました。ルックアップレコードの単純なリストを取得しようとしているとき、DDDで何をしますか?
例
従業員ID:123
従業員名:John Doe
州:アラスカ(ドロップダウン)
郡:ワシラ(ドロップダウン-状態に基づいてフィルタリングされます)。
たとえば、Employeeドメインオブジェクト、IEmployeeRepositoryインターフェイス、およびEmployeeRepositoryクラスがあるとします。これはUIによって使用され、従業員のリストと個々の詳細を表示します。 UIで、従業員が住んでいる州と郡のドロップダウンを使用します。利用可能な郡は、選択された州に基づいてフィルタリングされます。
残念ながら、データベーステーブルとUIの外観は大きく異なります。 tblEmployeesでは、州と郡名ではなく、州コード= AKと郡コード= 2130が含まれています。
(このDDDクエストを開始する前の)古い方法は非常に簡単で、2つのクエリを作成し、DataReaderを使用してドロップダウンにデータを入力します。ドロップダウンの表示の下にある値は、フォームの投稿で自動的に使用されます。
ただし、DDDでは、どのようにこれを行うべきかわかりません。最初に州と郡のオブジェクト、リポジトリ、リポジトリのインターフェイスを作成することから始めました。ただし、4つのクラスと2つのインターフェイスを記述し、hbm.xmlファイルとEmployee Businessオブジェクトに配管を追加すると、2つのドロップダウンに対して2つのクエリだけでは過剰に思えます。もっと良い方法が必要ですよね?州または郡のテーブルのレコードをすぐに変更することはありません。変更したとしても、このアプリケーションでは変更されません。そのため、必要がない場合は、州および郡のビジネスオブジェクトを作成したくありません。
私が見る最も簡単な解決策は、GetStatesAll()、GetState()、GetCounties()、GetCounty()などの辞書を返すメソッドを含むヘルパークラスを作成することですが、DDDの観点からは間違っているように感じます。
助けてください。いくつかの単純なルックアップだけをオーバーエンジニアリングせずにDDDを使用するにはどうすればよいですか?
最終的な解決策 経験から最終的に答えを見つけたと思います。これは、GetStates()メソッドをリポジトリクラスではなく、独自のデータアクセスクラスに入れることでした。読み取り専用アクセスのみを行っていたため、構造DTOにスローしました。データベースが小さいため、以下で説明するトッドのように、それらをすべて単一のクラスに投げました。
私の結論:
- ルックアップテーブルは常にIDを持っているため、ルックアップテーブルは決して値オブジェクトではありません。彼らがアイデンティティを持っていなかった場合、あなたは重複しているでしょう、それはあまり意味がありません。
- 読み取り専用のルックアップテーブルにはリポジトリを含めることができますが、おそらくリポジトリは必要ありません。リポジトリの目的は、集約のみを介してアクセスを強制することにより、複雑さを軽減することです。集合体を通過すると、車がない場合にタイヤを追加しないなど、ビジネスルールを実施できるようにする方法が提供されます。
- ルックアップテーブルでCRUDのメンテナンスを許可する場合、ルックアップテーブルに独自のリポジトリがあることは理にかなっています。
- 構造体としてコードを保存することになったという事実は、それらを「値型」にしません。 FowlerはPOEAAで、構造体は値型であると言っています。確かに、構造体は不変です。これが、ファウラーが「値型」だと言っている理由ですが、私はそれらを異なる方法で使用していました。構造体を使用して、最初の作成後に変更する予定のなかったDTOを簡単に渡すことができました。実際、私が使用した構造体には実際にIDがありましたが、読み取り専用であるため構造体として機能していました。
- 使用しているパターンの1つに、他の場所ではあまり見られないものとして、主キーフィールドを不変にするというものがあります。それらはコンストラクターによって設定されますが、読み取り専用です(プライベートではありません)
解決
コマンドクエリの分離。ルックアップ値の型指定されたリポジトリについては心配しませんが、データセットなどでDTO型クラスを使用する可能性があります。
これを現在に。彼は、ルックアップデータを具体的に入力することについては話しませんが、リポジトリの型付きメソッドを介してアプリケーションの読み取り/レポート機能を処理しないことについてよく話します。
他のヒント
DDDを使用すると、次のようなものがあります:
interface IAddressService
{
IList<Country> GetCountries ();
IList<State> GetStatesByCountry (string country);
IList<City> GetCitiesByState (string state);
// snip
}
Country、State、およびCityは、データベースのルックアップテーブルから取得される値オブジェクトです。
まあ、 Mathias Verraes の記事を読んで、このこちら。彼は、UIに役立つ概念からモデルの値オブジェクトを分離することについて話しています。
国をエンティティまたは値オブジェクトとしてモデル化するかどうかを尋ねられたときの記事からの引用:
モデル国には本質的に問題はありません。 エンティティとそれらをデータベースに保存します。しかし、ほとんどの場合、それは 複雑すぎるもの。国は頻繁に変わりません。とき 国の名前の変更、実際には、すべての実用的な目的のために、 新しい国。ある国がいつかもう存在しない場合は、できません 国が分割された可能性があるため、単にすべての住所を変更します 2つの国に。
彼は、 AvailableCountry
という新しい概念を導入するための別のアプローチを提案しました:
これらの利用可能な国は、データベース内のエンティティ、 JSON、または単にコード内のハードコードされたリストです。 (それはに依存します 企業がUIを介して簡単にアクセスできるようにする必要があるかどうか)
<?php
final class Country
{
private $countryCode;
public function __construct($countryCode)
{
$this->countryCode = $countryCode;
}
public function __toString()
{
return $this->countryCode;
}
}
final class AvailableCountry
{
private $country;
private $name;
public function __construct(Country $country, $name)
{
$this->country = $country;
$this->name = $name;
}
/** @return Country */
public function getCountry()
{
return $this->country;
}
public function getName()
{
return $this->name;
}
}
final class AvailableCountryRepository
{
/** @return AvailableCountry[] */
public function findAll()
{
return [
'BE' => new AvailableCountry(new Country('BE'), 'Belgium'),
'FR' => new AvailableCountry(new Country('FR'), 'France'),
//...
];
}
/** @return AvailableCountry */
public function findByCountry(Country $country)
{
return $this->findAll()[(string) $country];
}
}
したがって、ルックアップテーブルを値オブジェクトとエンティティの両方としてモデル化する3番目の解決策があるようです。
州と郡はエンティティではなく、値オブジェクトです。これらはシステムの対象ではありません。以前にこれらを処理したと言った方法は問題ありません。ドメインモデルの状態の変化に基づいて、データベースの状態または郡レコードをいつ変更しますか?そうではないので、これらはリポジトリを必要としません。
DDDを複雑にすることなく行う方法を学びたい場合は、間違った本を読んでいます。 :-)
提案している最も簡単な解決策は、ニーズに合っていれば問題ありません。アドレスデータをビジネスオブジェクトにカプセル化することは、アプリケーションが要求するのと同じくらい簡単でも複雑でもあります。たとえば、Stateオブジェクトは郡と1対多の関係にあるため、そのようにモデル化することを選択した場合、Employeeは本当に郡を参照するだけです。必要な場合にのみ、この種の複雑さを紹介します。
さらに、オブジェクトに複数のリポジトリがある可能性が実際にない限り、リポジトリのインターフェイスを定義することで得られるものはあまりないと思います。