8

最近、ddd とモデル層の概念を理解するのにとても忙しくしています。大量の記事、例、Q & A を読み、それに何時間も費やしました。それでも、いくつかの原則が正しいかどうかはわかりません。

それらの 1 つは、次の質問に対する答えです。ドメイン オブジェクトには、どの程度のビジネス ロジックが存在する必要がありますか? 一部のソースでは、ドメイン オブジェクトはビジネス ロジック全体にアタッチする必要があると言われていますが、一方、ドメイン オブジェクトは可能な限り小さくし、その値のみを表す必要があると想定する記事に出くわしました。それは私を本当に混乱させます。

私の理解では、ドメイン オブジェクトは、ドメイン内のエンティティを表すクラスです。

たとえば、Invoice エンティティを使用してみましょう。各請求書は、その項目で構成されています。請求書の値を計算するには、すべてのアイテムの値を合計する必要があります (これは非常に単純な例です。現実の世界では、税金を追加したり、支払った値を計算したりする場合があります)。

class Invoice
{
    public $id;
    public $items = [];
    public $status;

    const STATUS_PAID = 'paid';
    const STATUS_NOT_PAID = 'not_paid';

    public function isPaid()
    {
        return $this->status == self::STATUS_PAID;
    }

    public function getInvoiceValue()
    {
        $sum = 0;
        foreach($this->items as $item) {
            $sum += $item->value;
        }
        return $sum;
    }
}

私の理解では、メソッド isPaid() は適切な場所にあります。独自のデータを参照します。しかし、getInvoiceValue() についてはよくわかりません。ここでは、他のドメイン オブジェクトを操作します。

データを表すためだけにドメイン オブジェクトを使用し、より高度なタスクを実行するにはいくつかのデコレータを使用する必要があるのではないでしょうか?

前もって感謝します。

4

3 に答える 3

3

ドメイン オブジェクトにはどの程度のビジネス ロジックが存在する必要がありますか?

すべて(可能であれば)。DDD のポイントは、ドメイン内のビジネス ロジックをキャプチャすることです。ここでは、さまざまな戦術パターンを使用して支援できます (集計、エンティティ、値オブジェクト、ドメイン サービスなど...)。

ドメイン オブジェクトは、ドメイン内のエンティティを表すクラスです。

ドメイン内のクラスは、単なるエンティティ以上のものを表すことができます。集合体、エンティティ、値オブジェクト、ドメイン サービスなどはすべて、ドメイン内のクラスで表すことができます。

しかし、getInvoiceValue() についてはよくわかりません。ここでは、他のドメイン オブジェクトを操作します。

Invoice の例は、Aggregate の典型的な例です。Invoice には InvoiceItems が含まれます。ここでは getInvoiceValue() で問題ありません。

この場合、Invoice は集計です。Aggregate RootはInvoiceそのものですが、Entityでもありますよね?ただし、独自の ID (固有の請求書番号) があります。

はい正解

InvoiceItems とは何ですか? InvoiceItem リポジトリから直接取得できますか (作成する必要がある場合)、または常に Aggregate のみを操作する必要がありますか?

ユースケースによって異なります。書き込みモデルと読み取りモデルを別々に分割するのに役立ちます (CQRS)。読み取り側 (レポートなど) について話している場合は、ドメイン モデルをバイパスし、読み取りモデルを表すオブジェクトを使用します。これは単なるデータベース クエリである可能性があります。書き込み側 (つまり、コマンド、ドメイン) について話している場合は、通常、集約ルートごとにリポジトリがあります。集約ルートが何であるかは、モデリングの問題です。すべてのビジネスルールを適用するような方法でそれらを作成したい - あなたの例では、ルールを適用するために Invoice が InvoiceItems をロードする必要がある場合 (たとえば、「請求書ごとに 5 項目以下」)、はい、そうする必要があります。集約ルートを介してロードされる

于 2016-08-01T11:55:41.030 に答える
3

この種の質問に対する正しい答えがあるかどうかはわかりません。DDD の適用は、実際に適用する特定のドメインに依存するためです。ビジネス ニーズを満たせば、実装が完全に有効になる可能性がある場所があります。他の国では、あなたが税金などで述べたように、そうではありません。したがって、コードに変換する前に、ドメインについて質問し続けて、ニーズが何であるかを完全に理解する必要があると言えます。

そうは言っても、請求書の価値を引き出すために外界に関する追加の知識が必要な、より複雑なシナリオがある場合、1 つのオプションは、それをドメインで明示的に表現することです。あなたの例では、それは次のような署名を持つ可能性のある InvoiceProducer である可能性があります。

class InvoiceProducer {

    public function __construct(TaxProvider $taxProvider) {
        $this->taxProvider = $taxProvider;
    }

    public function invoiceFor(array $items) {
        new Invoice($items, $this->calculateValue($items));
    }

    private function calculateValue(array $items) {
        $sum = array_reduce($items, function($acc, $item){
            $acc += $item->value;
        }

        return $this->taxProvider->applyTaxTo($sum);
    }
}

別のオプションは、ある種の戦略パターンを使用することです。これにより、実装は現在の方法と非常によく似たままになりますが、課税を計算したい方法で呼び出しを渡すことになります。

public function getInvoiceValue(TaxProvider $taxProvider)
{
    $sum = 0;
    foreach($this->items as $item) {
        $sum += $item->value;
    }

    return $taxProvider->applyTaxFor($sum);
}

繰り返しますが、特定のドメインがどのように機能するかによって大きく異なりますが、ご覧のとおり、実装はそれほど大したことではありません。すべてがドメイン内にどのように収まるかについての詳細です。

于 2016-08-01T10:10:17.697 に答える