イベント - 要求分析駆動設計

Entityの操作の中で条件が満たされ、メールを送信したかったり、他のサービスにメッセージを送りたい場合がある。 こういう場合はEntityの操作の後に、条件が満たされたかをUseCase内でチェックすれば要件は満たせる。

entity.command
if entity.notify_to_user?
  # ユーザーに通知する処理
end

しかし、この場合、ユーザーに通知する内容をUseCaseで組み立てるか、entityに取得するメソッドを定義しなければならない。 また、条件が満たされた瞬間に存在する情報を通知内容に含めたい場合、entityに保持し続けるようにインスタンス変数を追加しなければならなくなる。

こういった問題を避けるために、イベントという方法がある。 Entityのレイヤスーパータイプを以下のように定義し、Eventクラスを用意していく。

class EntitySuperType
  def add_event(event)
    return if event.blank?
    events << event
  end

  def events
    @events ||= []
  end
end
class NotifyToUserEvent
  attr_acessor :message
  def initialize(message)
    self.message = message
  end
end
# アクションイネーブラのビジネスルール
class SameRule
  def notify_to_user_event(message)
    return if condition # 条件が満たされていなければイベントを返さない

    NotifyToUserEvent.new(message)
  end
end
class SomeEntity < EntitySuperType
  def command
    # ...
    # ユーザー通知の条件が満たされていたら、eventを追加する
    add_event(SameRule.new(...).notify_to_user_event('メッセージ'))
  end
end
class SomeUseCase
  def method
    # ...
    some_entity.command
    some_entity.events.each do |event|
      if event.is_a?(NotifyToUserEvent)
        # ユーザーに通知する処理
      end
    end
  end
end

この実装例は、EventをためてUseCaseに戻ってきたタイミングでEventを処理する方法をとっている。 普通のイベント処理ではイベントが発生したタイミングでハンドラが呼ばれるが、大半のシステムではそのような仕組みを構築するのはオーバーエンジニアリングだろうし、単純でわかりやすい形をとった。 イベント発生から処理されるまで遅延が発生することで問題がおきるなら、Observerパターンなどでイベントの発生したらすぐに処理する仕組みを構築すれば良い。

正直なところ、あまりドメインイベントを理解できているとは言い難いので、必要なら実践ドメイン駆動設計の「第8章ドメインイベント」を参照して欲しい。