在專案中,除了 domain 內上下層的關係外,domain 之間也會有依賴關係,而為了不讓 domain 間過度耦合,我們透過 wisper 來實現 pub-sub pattern。
關於 pub-sub pattern 的好處可以參考以下文章
我們現在可以利用 wisper 對所有有訂閱的 listener 發布 event,因此不再需要 marketing_client
。
# create_order.rb
class CreateOrder < Boxenn::UseCase
option :repo, default: -> { OrderRepository.new }
def steps(params:)
subscribe_listener
order = build_order(params)
yield valid_order(order) # 因為回傳是 Dry::Monads::Result 所以需要 yield
create_order(order)
publish_event(order.serial_number)
end
private
def subscribe_listener
subscribe(::Marketing::Listener, async: true, prefix: :on)
end
def build_order(params)
OrderEntity.new(params)
end
def valid_order(order)
# 假設 order entity 有個 method valid? 會回傳 Bool 告知是否合法
return Success() if order.valid?
Failure('Invalid Order')
end
def create_order(order)
repo.save(order)
end
def publish_event(serial_number)
publish(:order_created, order_serial_number: serial_number)
Success() # 最後一個 call 的 method 需要回傳 Dry::Monads::Result
end
end
# marketing_listener.rb
module Marketing
class Listener
class << self
def on_invoice_created(order_serial_number:)
result = SendPurchaseEmail.new.call(order_serial_number: order_serial_number)
if result.failure?
# 例外處理(打通知等等)
end
end
end
end
end
如果 domain 之間有明顯的上下依賴關係,則可以直接使用 client。
假設今天成立訂單後需要開立發票,而訂單和發票是不同的 domain;因為發票需要的資訊有一定的規範、介面相對穩定,設計上會傾向讓訂單依賴發票(依賴較穩定的介面),這時候成立訂單的 use case 即可直接使用發票 client 的介面。
下篇會介紹非同步的 use case 要怎麼實作。