Giter Site home page Giter Site logo

Comments (5)

slashdotdash avatar slashdotdash commented on April 28, 2024

Using domain events to model failures is how you'd solve the problem currently.

Instead of your aggregate returning an {:error, :no_available_seats} error, it should raise a domain event such as FlightReservationFailed and could provide the failure as a field (e.g. reason: :no_seats_available). Your travel process manager can then subscribe to both success and failure events and handle each appropriately. It would dispatch cancellation commands in the case of any step failure.

A benefit to using domain events for errors is they provide useful auditing and analytics. The business might find it convenient to report on these events in the future to answer pertinent questions (e.g. "how often do we fail to reserve seats on flights for airline X?").

There's a pending issue to add a retry/error handling mechanism to event handlers and process managers (#20). This feature would allow your aggregate to return error tuples, or raise an exception, and provide an extension point in your process manager to handle errors on a case-by-case basis. But it needs to be implemented.

Does that answer your question?

from commanded.

bamorim avatar bamorim commented on April 28, 2024

Yes, that seems a good idea. So the point is to return these events, but in the cases where this is a CreateX command it should not create a valid aggregate, right? By returning an event, we assume it worked and that we can fold over the events to get the state of the aggregate, which actually shouldn't exist. How would you work in that situation?
Just allowing the aggregate to "exist" but actually in an "invalid" state?
Also, if domain errors are just events, how can I know that a command worked after dispatching it? Also, what is the point to returning {:error, reason}? Which kind of errors you think I should return in the tuple and witch you think I should return in events?

from commanded.

slashdotdash avatar slashdotdash commented on April 28, 2024

It's ok to have an aggregate in such a state, think of it as an unfulfilled reservation request rather than an "error state". You can use Commanded's Aggregate lifespan feature to shutdown these aggregates after an error event if you are concerned with them running indefinitely.

how can I know that a command worked after dispatching it?

You receive an :ok response from a successful command dispatch, or an {:error, reason} when it fails. However, by using failure domain events the command will successfully dispatch (returning :ok). You will need an event handler to handle these events and notify the end user as appropriate (alert email, in-app notification, use Phoenix channels to push the failure to the user's browser).

The general pattern with long running processes, such as your reservation, is to notify the user that their request was accepted, start processing the request in the background, and inform the user that they will be notified upon success/failure. One approach is to have a read model projection for the reservation that is updated from the domain events. As an example if you're building a web app, after successful submission you redirect the user to a page that polls/subscribes the user to updates of their reservation status.

what is the point to returning {:error, reason}? Which kind of errors you think I should return in the tuple and witch you think I should return in events?

As a rule of thumb, use domain events for failures of commands dispatched by a process manager. You can use {:error, reason} tuples elsewhere and to guard against bugs (e.g. attempting to reserve a flight for a date in the past).

from commanded.

slashdotdash avatar slashdotdash commented on April 28, 2024

@bamorim You can now handle errors in your process managers using the new feature described in #93.

Here's your example travel process manager responding to seat failure by cancelling the hotel and car reservations:

defmodule TravelProcessManager do
  use Commanded.ProcessManagers.ProcessManager,
    name: "TravelProcessManager",
    router: TravelRouter

  def error({:error, :no_available_seats}, _failed_command, _pending_commands, context) do
    {:continue, [%CancelHotel{...}, %CancelCar{...}], context}
  end
end

Now you can choose to model failures as errors (e.g. {:error, :no_available_seats}) or domain events (e.g. FlightReservationFailed).

from commanded.

bamorim avatar bamorim commented on April 28, 2024

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.