Open6

DDDに関して気付いた事を供養する場所

KatsukiniwaKatsukiniwa

エンティティを定義する時どうしてもRDBのテーブル単位で見てしまう。
そのコンテキストではどの概念を扱うのか、そのエンティティはどの要素から構成されているのかを常に意識する事。

KatsukiniwaKatsukiniwa

境界付けられたコンテキストと言う概念自体はDDDの文脈で出て来たものだと認識している。
IDDDでは
境界付けられたコンテキスト : コアドメイン = 1 : 1
と記述されている。
しかし、似た概念でモジュールというものも紹介されている。クリーンアーキテクチャではgemやjarファイルといったそれ単体でデプロイ可能な単位と紹介されていた気がする。
なので自分は境界づけられたコンテキストモジュール くらいの認識でいる。

KatsukiniwaKatsukiniwa

基底クラスの議論があまりないのが気になる。
例えばエンティティだったら下記のような基底クラスを作成して運用するがいかがだろうか?
下記の例は一番シンプルな例で、idをstringではなく固有のEntityIdentifierのようなバリューオブジェクトで定義している例もある。

abstract class Entity<T> {
  // idがRDBのautoincrementを使用していない場合
  // uuidなどで識別子の早期生成を採用している場合を想定している
  abstract id: string;

  public equals(that: Entity<T>): boolean {
    return this.id === that.id;
  }
}
KatsukiniwaKatsukiniwa

では集約は?と聞かれたら自分は下記のような基底クラスを書く。

interface DomainEvent {
    eventVersion: number;
    occurredOn: Date;
}

abstract class AggregateRoot<T> extends Entity {
  abstract domainEvents: DomainEvent[];

  public occur(domainEvent: DomainEvent): void {
    this.domainEvents.push(domainEvent);
  }

  public publish(domainEvent: DomainEvent): void {
    // DomainEventSubcriberContainerは発行された
    // ドメインイベントをサブスクライブしているクラスを引っ張ってくる
    // DIコンテナ的なものをイメージして頂けると良いと思う
    DomainEventSubcriberContainer.handle(domainEvent);
  }

  public publishAll(): void {
    this.domainEvents.forEach(domainEvent => {
       this.publish(domainEvent);
    });
  }
}

エンティティとの違いはドメインイベントを保持しているかどうかだという認識。IDDDの通り、集約は他の集約を識別子のみで参照する。他のコンテキストとの通信は全てドメインイベント経由で行い、発行した後のことは集約自身は知らない。トランザクションについてもドメインイベントの発行による結果整合性で担保する。結果整合性を許容できない場合は集約の境界が間違っている可能性が高いので集約の設計を見直す。

KatsukiniwaKatsukiniwa

ドメインイベントを発行するレイヤーだが

  • インフラ層
  • ドメイン層
  • ユースケース層

色々な層での発行例が見られるが、個人的にはインフラ層で発行することによってDBに正常に値が書き込まれた時にのみドメインイベントを発行する事ができるようになるのでインフラ層が有力な気がしている。
ドメイン イベント: 設計と実装
MSのドキュメントでもインフラ層での発行が紹介されている。永続化層をSingle Source of Truthとして設計しましょうという事なのかな?