しゃべぶろ

気になった技術の備忘録を残します。

【ドメイン駆動設計】サンプル(iddd_agilepm)を読解く その3

概要

前回はアプリケーションサービスについて触れたので、
今回はドメインモデルについて触れてみる。
hghyk023.hatenablog.jp

ドメインモデル

業務知識(ドメイン)を表現した概念のこと(?)
ドメイン駆動設計では、ドメインモデルをエンティティ値オブジェクトドメインサービスで表現していく。
業務知識を適切に表現出来ていないドメインモデルは、ドメインモデル貧血症と呼ばれる。

ドメインモデル貧血症とは業務知識がドメインモデル以外に点在している事である。
例えば、前回説明したアプリケーションサービスが業務知識を保持しているとそれに該当する。
陥ると各所に点在する業務知識を考慮する必要があり、改修がやりづらくなってしまう。
そこで、業務知識を1箇所に纏めて改修に強くしようというのがドメイン駆動設計の考え方である。

iddd_agilepmでのドメインモデルの使い方を見てみる。

domain/modelパッケージ

  • EntityとValueObjectという抽象クラスが存在する。
  • *ed系はドメインイベントとなり、DomainEventというInterfaceを実装している。
  • RepositoryはInterfaceとして定義、DIP(依存関係逆転の法則)が適用されている。
  • productパッケージ配下にbacklogitemやreleaseパッケージが存在する。

もう少し詳しく見ていく。

エンティティ

抽象クラス
public abstract class Entity extends AssertionConcern {
    private int concurrencyVersion;
    public Entity() {
        super();
        this.setConcurrencyVersion(0);
    }
    public int concurrencyVersion() {
        return this.concurrencyVersion;
    }
    private void setConcurrencyVersion(int aConcurrencyVersion) {
        this.concurrencyVersion = aConcurrencyVersion;
    }
}
  • assertionメソッドを纏めたAssertionConcernクラスを継承している。
  • 実装クラス内ではこのメソッドを使って引数チェックをしている。
  • concurrencyVersionは使ってない??
実装クラス
public class Product extends Entity {
    /** 省略 */
    public Product(/** 省略 */) {
        /** 省略 */
        DomainEventPublisher
            .instance()
            .publish(new ProductCreated(/** 省略 */));
    }

    public void changeProductOwner(ProductOwner aProductOwner) {
        if (!this.productOwnerId().equals(aProductOwner.productOwnerId())) {
            this.setProductOwnerId(aProductOwner.productOwnerId());
        }
    }
}
  • Productはアプリケーションサービスから作成される。
  • 作成するとドメインイベントが発行され、サブスクライバーに通知される。
  • changeProductOwnerメソッドで自身の状態を変更している。

値オブジェクト

抽象クラス

AssertionConcernクラスを継承しているだけ特記事項なし。

実装クラス
public final class ProductDiscussion extends ValueObject {

    private DiscussionAvailability availability;
    private DiscussionDescriptor descriptor;

    public static ProductDiscussion fromAvailability(
            DiscussionAvailability anAvailability) {

        if (anAvailability.isReady()) {
            throw new IllegalArgumentException("Cannot be created ready.");
        }

        DiscussionDescriptor descriptor =
                new DiscussionDescriptor(DiscussionDescriptor.UNDEFINED_ID);

        return new ProductDiscussion(descriptor, anAvailability);
    }

    public ProductDiscussion(
            DiscussionDescriptor aDescriptor,
            DiscussionAvailability anAvailability) {

        super();

        this.setAvailability(anAvailability);
        this.setDescriptor(aDescriptor);
    }
}
  • エンティティとは異なり、final classとなっている。
  • 値オブジェクトは自身の状態を変えることは出来ない。
  • fromAvailabilityメソッドでは、自身を作り変えている。

ドメインサービス

見当たらない。どれが該当するのだろう??

まとめ

割と浅くしか確認してないので、もう少し詳しく調べたい。
また、集約についても取り上げたい。

参考

codezine.jp