Nebo15 / sage

A dependency-free tool to run distributed transactions in Elixir, inspired by Sagas pattern.
MIT License
912 stars 40 forks source link

Easier way to add pre-defined stages #46

Open ssbb opened 4 years ago

ssbb commented 4 years ago

I have few sagas which can be used independently and in another more high-level sagas. Just curious what is the proper way to use shared arguments/stages with such workflow.

Right now I have two functions process(%Sage{} = sage) which running all stages and run(...). So I assume required data always in effects_so_far. For run function I pass accepted arguments into "fake" stages:

Sage.new()
|> Sage.run(:required_effect, fn _effects, _params -> {:ok, arg1} end)

What is proper way to do it?

AndrewDryga commented 4 years ago

Hey @ssbb, Im not sure that I understood the question, can you please give a real life use case (with more code) and Ill think how we can improve Sage in this direction? Thank you 🙏.

ssbb commented 4 years ago

Hi @AndrewDryga, we discussed this in Slack actually. My code looks like this:

  def run(%DepositForm{} = deposit, user, transaction, application) do
    result =
      Sage.new()
      |> Sage.run(:transaction, fn _effects, _params -> {:ok, transaction} end)
      |> Sage.run(:application, fn _effects, _params -> {:ok, application} end)
      |> process()
      |> Sage.transaction(Repo, %{deposit: deposit, user: user})

    case result do
      {:ok, _, %{update_payment: payment}} ->
        {:ok, payment}

      {:error, %Stripe.Error{user_message: user_message, message: message}} ->
        {:error, "Deposit error: #{user_message || message}"}

      err ->
        Sentry.capture_exception(nil, extra: %{error: err}, level: "error")
        err
    end
  end

  def skip(sage) do
    Sage.run(sage, :deposit, fn _effects, _params ->
      {:ok, nil}
    end)
  end

  def process(sage) do
    sage
    |> create_payment
    |> customer
    |> charge
    |> update_payment
    |> deliver_email
    |> deliver_slack
  end

Really I am ok with "fake" Sage.run calls but when I asked about this in Slack you said I should create an issue.

Basically it can be used 2 ways - via run to run this saga when we already have transaction and application or via process as part of another saga (so transaction and application will be created in previous stages).