New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support time-based triggers for process managers #36
Comments
It feels weird to have commands that are dispatched later. It seems better to have some kind of timed process (which should be a PM probably, and we already talked about PMs and timers) which just sends the command later. Just a ExpireOrder command that changes the state of the Order, no? Besides, a PM can control the expiring process if the command is dropped for any reason, something you can't do if you just send a delayed command once. |
It's possible to implement time-based triggers using an external job scheduler and by dispatching commands to raise events to set the callback time period (date time, recurring interval) and command to dispatch. A standard event hander would then configure the job scheduler at the requested period to dispatch the command. This requires no changes to Commanded itself. It could be implemented as a standalone library. |
Why? Is there anyone who needs this? I agree with @Papipo that it should be done in PM. This seems to me like a business concern, and so it should not be done in a framework. Unless you need timers for things like periodic snapshotting etc. But that is not what you're talking about here, I think. |
Closing, could be implemented as an external library. |
Hey guys, could we please discuss this proposal once more. The PM triggers are different from scheduled commands. Say we have a PM which need to check order status every minute since order creation, by calling external http service. Currently it means that we'll need to post command defmodule ProcessManager do
# ...
def handle_trigger(%ProcessManager{}, %CheckOrderStatus{order_id: order_id}) do
# note %CheckOrderStatus{} is a not domain event and not persist in event store
case MyApi.get_order_status(order_id) do
{:ok, :complete} -> %CompleteOrder{id: order_id)
_ -> []
end
end
# ...
def handle(%ProjectManager{id: id}, %OrderOpened{}), do: MyScheduler.add_job("* * * * *",{TriggerProcessManager, [id: id]})
def handle(%ProjectManager{id: id}, %OrderClosed{}), do: MyScheduler.remove_job(id)
# ...
end
defmodule TriggerProcessManager do
# ...
def perform(id: id) do
ProcessManager.trigger(id, %CheckOrderStatus{order_id: i})
end
# ...
end does it makes any sense for you guys? |
@ayarulin I'm currently building a library for Commanded to support one-off and recurring commands: Commanded scheduler. This library allows you to directly schedule commands: alias Commanded.Scheduler
# schedule one-off command
Scheduler.schedule_once(reservation_id, %TimeoutReservation{..}, ~N[2020-01-01 12:00:00])
# schedule a recurring command to execute every 15 minutes
Scheduler.schedule_recurring(reservation_id, %TimeoutReservation{..}, "*/15 * * * *") It also supports scheduling commands from a process manager by returning a schedule once or schedule recurring command: defmodule OrderStatusProcessManager do
use Commanded.ProcessManagers.ProcessManager,
name: "order-status",
router: AppRouter
def handle(%OrderStatusProcessManager{}, %OrderOpened{order_id: order_id}) do
%Commanded.Scheduler.ScheduleRecurring
schedule_uuid: order_id,
command: %CheckOrderStatus{order_id: order_id},
schedule: "* * * * *",
}
end
def handle(%OrderStatusProcessManager{}, %OrderClosed{order_id: order_id}) do
%CancelSchedule{order_id: order_id}
end
end Then you'd do the order checking in the command handler: def OrderHandler do
def handle(%Order{} = order, %CheckOrderStatus{order_id: order_id}) do
case MyApi.get_order_status(order_id) do
{:ok, :complete} -> Order.mark_complete(order)
_ -> []
end
end
end The benefit of this approach is that process managers are purely functional. They return commands in response to events, thus are easily testable. The details of fetching the external order status can be done in the command handler. The response can be passed into the aggregate to create the appropriate domain event(s). |
@slashdotdash, +1 to Commanded Scheduler
good point |
To allow long running process managers to handle timeouts.
A process manager may request to be notified after a given period or at an exact date and time.
When the time arrives the process manager is notified and it may dispatch commands in response.
Requires consideration of how to persist these time-based triggers to survive application restarts and process crashes.
The text was updated successfully, but these errors were encountered: