Giter Site home page Giter Site logo

Comments (7)

Papipo avatar Papipo commented on April 28, 2024

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.

from commanded.

slashdotdash avatar slashdotdash commented on April 28, 2024

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.

from commanded.

drozzy avatar drozzy commented on April 28, 2024

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.

from commanded.

slashdotdash avatar slashdotdash commented on April 28, 2024

Closing, could be implemented as an external library.

from commanded.

ayarulin avatar ayarulin commented on April 28, 2024

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 CheckOrderStatus once in minute, it will be processed by aggregate and emit OrderStatusRequested event which then will be handled by PM. In domain terms, this is almost meaningless event, the only purpose of it - is run PM handler (and eventstore will contain tons of them). So why can't we just trigger PM directly? I could imagine something like this:

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?

from commanded.

slashdotdash avatar slashdotdash commented on April 28, 2024

@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).

from commanded.

ayarulin avatar ayarulin commented on April 28, 2024

@slashdotdash, +1 to Commanded Scheduler
and thanks for clarifications, i've totally missed that services can be called from command handlers as well.

The benefit of this approach is that process managers are purely functional

good point

from commanded.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.