之后调用apply/2
回调handle/2
是设计使然,不可能将 Commanded 配置为不同的行为。
我同意您的推理,即在尝试处理事件以生成任何命令之前将事件应用于进程管理器的状态更有意义。这似乎是对 Commanded 进行的一项有价值的更改,可以通过您已经提出的问题 ( #176 ) 进行跟踪。
同时,您可以按如下方式实现您的流程管理器(saga):
defmodule InvoicingProcessManager do
use Commanded.ProcessManagers.ProcessManager,
name: __MODULE__,
router: InvoicingRouter
defstruct [
:batch_uuid,
pending_invoice_ids: MapSet.new()
]
def interested?(%InvoiceBatchStarted{batch_uuid: batch_uuid}), do: {:start, batch_uuid}
def interested?(%InvoiceCreated{batch_uuid: batch_uuid}), do: {:continue, batch_uuid}
def interested?(%InvoiceFailed{batch_uuid: batch_uuid}), do: {:continue, batch_uuid}
def interested?(%InvoiceBatchStopped{batch_uuid: batch_uuid}), do: {:stop, batch_uuid}
def interested?(_event), do: false
# Event handlers
def handle(%InvoicingSaga{}, %InvoiceBatchStarted{} = started) do
%InvoiceBatchStarted{batch_uuid: batch_uuid, invoice_ids: invoice_ids} = started
Enum.map(invoice_ids, fn invoice_id ->
%CreateInvoice{
invoice_id: invoice_id,
batch_uuid: batch_uuid
}
end)
end
def handle(%InvoicingSaga{}, %InvoiceCreated{invoice_id: invoice_id}),
do: attempt_stop_batch(pm, invoice_id)
def handle(%InvoicingSaga{}, %InvoiceFailed{invoice_id: invoice_id}),
do: attempt_stop_batch(pm, invoice_id)
## State mutators
def apply(%InvoicingSaga{} = pm, %InvoiceBatchStarted{} = started) do
%InvoiceBatchStarted{batch_uuid: batch_uuid, invoice_ids: invoice_ids} = started
%InvoicingSaga{
transfer
| batch_uuid: batch_uuid,
pending_invoice_ids: MapSet.new(invoice_ids)
}
end
def apply(%InvoicingSaga{} = pm, %InvoiceCreated{invoice_id: invoice_id}) do
%InvoicingSaga{pm | pending_invoice_ids: invoice_completed(pm, invoice_id)}
end
def apply(%InvoicingSaga{} = pm, %InvoiceFailed{invoice_id: invoice_id}) do
%InvoicingSaga{pm | pending_invoice_ids: invoice_completed(pm, invoice_id)}
end
## Private helpers
def attempt_stop_batch(%InvoicingSaga{batch_uuid: batch_uuid} = pm, invoice_id) do
pending_invoices = invoice_completed(pm, invoice_id)
case empty?(pending_invoices) do
true -> %StopInvoiceBatch{batch_uuid: batch_uuid}
false -> []
end
end
defp invoice_completed(%InvoicingSaga{pending_invoice_ids: pending_invoice_ids}, invoice_id) do
MapSet.delete(pending_invoice_ids, invoice_id)
end
defp empty?(map_set, empty \\ MapSet.new())
defp empty?(%MapSet{} = empty, %MapSet{} = empty), do: true
defp empty?(%MapSet{}, %MapSet{}), do: false
end