49

私はちょうど DDD を学んでいて (Eric Evans の本が目の前で開かれています)、答えが見つからない問題に遭遇しました。検索レコードの単純なリストを取得しようとしているだけの場合、DDD で何をしますか?

元。

EmployeeID: 123
EmployeeName: John Doe
州: Alaska (ドロップダウン)
County: Wasilla (ドロップダウン -- 州に基づいてフィルタリングされます)。

たとえば、Employee ドメイン オブジェクト、IEmployeeRepository インターフェイス、EmployeeRepository クラスがあるとします。これは、従業員のリストと個人の詳細を表示するために UI によって使用されます。UI で、従業員が住んでいる州と郡のドロップダウンを使用したいと考えています。利用可能な郡は、選択された州に基づいてフィルタリングされます。

残念ながら、データベース テーブルと UI は大きく異なります。tblEmployees には、州名と郡名ではなく、州コード=AK と郡コード=02130 が含まれています。

古い方法 (この DDD クエストを開始する前) は非常に簡単で、2 つのクエリを作成し、DataReader を使用してドロップダウンに入力するだけです。ドロップダウンの表示の下に値があり、フォームの投稿で自動的に使用されます。

ただし、DDD では、これをどのように行うべきかわかりません。まず、State オブジェクトと County オブジェクト、およびリポジトリとリポジトリ用のインターフェイスを作成することから始めました。ただし、4 つのクラス + 2 つのインターフェイスと hbm.xml ファイル内の配管 + Employee Business オブジェクトを記述することは、2 つのドロップダウンに対して 2 つのクエリだけを行うにはやり過ぎのように思えます。もっと良い方法があるはずですよね?State または County のテーブルのレコードを当面変更する予定はありません。変更したとしても、このアプリケーションによるものではありません。そのため、必要がなければ、州と郡のビジネス オブジェクトを作成したくありません。

私が考える最も簡単な解決策は、GetStatesAll()、GetState()、GetCounties()、GetCounty() などの辞書を返すメソッドを使用してヘルパー クラスを作成することですが、DDD の観点からすると、それは間違っているように感じられます。

助けてください。いくつかの単純なルックアップだけをオーバーエンジニアリングせずに DDD を使用するにはどうすればよいですか?

最終的な解決策 リポジトリ クラスではなく、独自のデータ アクセス クラスに GetStates() メソッドを配置するという経験を通じて、最終的に答えを見つけたと思います。私は読み取り専用アクセスしか行っていなかったので、構造体 DTO に入れました。データベースが小さかったので、Todd が後述するように、それらを 1 つのクラスにまとめました。

私の結論:

  1. ルックアップ テーブルには常に ID があるため、ルックアップ テーブルは決して値オブジェクトではありません。ID がない場合は重複が発生し、あまり意味がありません。
  2. 読み取り専用のルックアップ テーブルはリポジトリを持つことができますが、おそらく必要ありません。リポジトリの目標は、集約を介してのみアクセスを強制することにより、複雑さを軽減することです。集計を行うことで、車を持っていない場合はタイヤを追加しないなど、ビジネス ルールを適用できるようにする方法が提供されます。
  3. ルックアップ テーブルで CRUD メンテナンスを許可する場合は、ルックアップ テーブルが独自のリポジトリを持つことが理にかなっています。
  4. コードを構造体として保存することになったという事実は、それらを「値型」にしません。Fowler は POEAA で、構造体は値型であると述べています。確かに、構造体は不変です。そのため、ファウラーは「値型」であると言っていますが、私はそれらを別の方法で使用していました。最初の作成後に変更する予定がなかった DTO を渡すための軽量な方法として、構造体を使用していました。実際、私が使用した構造体には確かに ID がありましたが、それらは読み取り専用であるため、構造体として機能していました。
  5. 他ではあまり見かけない、私が使ってきた 1 つのパターンは、主キー フィールドを不変にすることです。それらはコンストラクターによって設定されますが、読み取り専用 (プライベート アクセサーではない) であり、オブジェクトが作成されると変更できません。
4

5 に答える 5

9

Command Query Separationの概念を調べたいと思うかもしれません。ルックアップ値の型付きリポジトリについては心配しませんが、おそらくデータセットなどに対して DTO 型クラスを使用するでしょう...

このブログから現在までの Greg Young のブログを読んでみてください。彼はルックアップ データの入力について具体的には語っていませんが、リポジトリの型指定されたメソッドを使用してアプリケーションの読み取り/レポート機能を処理しないことについてよく話します。

于 2009-04-30T21:20:57.670 に答える
7

DDD を使用すると、次のようなものがあります。

interface IAddressService
{
  IList<Country> GetCountries ();
  IList<State> GetStatesByCountry (string country);
  IList<City> GetCitiesByState (string state);
  // snip
}

Country、State、および City は、データベース内のルックアップ テーブルから取得される値オブジェクトです。

于 2009-04-30T21:12:41.573 に答える
4

さて、 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番目のソリューションがあるようです。

ところで、この記事に関する深刻な議論については、コメント セクションを確認してください。

于 2014-02-26T12:47:16.777 に答える
3

州と郡はエンティティではなく、値オブジェクトです。それらはあなたのシステムの主題ではありません。あなたが以前にこれらを扱ったとあなたが言った方法はOKです。ドメインモデルの状態の変化に基づいて、データベースの州または郡のレコードをいつ変更しますか?そうではないので、これらはリポジトリを必要としません。

于 2009-04-30T20:14:42.667 に答える
3

複雑にしすぎずにDDDを実行する方法を学びたい場合は、間違った本を読んでいます。:-)

あなたが提案している最も単純な解決策は、それがあなたのニーズを満たしていれば問題ありません。アドレスデータをビジネスオブジェクトにカプセル化することは、アプリケーションの要求に応じて単純または複雑にすることができます。たとえば、StateオブジェクトはCountyと1対多の関係にあるため、そのようにモデル化することを選択した場合、Employeeは実際にはCountyを参照する必要があります。必要な場合にのみ、そのような複雑さを紹介します。

さらに、オブジェクトに複数のリポジトリがある可能性が実際にない限り、リポジトリのインターフェイスを定義することで得られるものはそれほど多くないと思います。

于 2009-04-30T20:16:18.013 に答える