Nebo15 / sage

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

Composing sagas #47

Open ssbb opened 4 years ago

ssbb commented 4 years ago

Just curious if we can introduce API to compose sagas. For example in our rental application process we have such sagas:

  1. application form processing - add row into db, charge fee, generate PDF, send emails
  2. credit report processing - based on form we sending data into Credit bureau, generating PDFs, etc. adding info to db, etc.
  3. rental deposit processing - add info to db, charge deposit amount, etc.

These 3 steps in it's own saga since we need to do it independently (eg. ask for credit report again after it was failed on credit bureau side).

But we have high-level super-saga (we name it ApplicationSaga) which runs all this 3 sagas and something more.

Right now we have 2 functions in each saga - run which runs saga independently and process which accept %Sage{}, piping stages and returns it.

But I am curious if it will make sense something like

Sage.run_sub(sage, :credit_report, another_sage, ...). Notice there is another_sage instead of function as 3rd argument.

Basically my idea is to:

  1. isolate stage names and params between this sagas when it's used as part of another sagas
  2. isolate saga results. In my "main" saga I don't care about each stage result from sub-saga. I just need final result provided by sub-saga.
AndrewDryga commented 4 years ago

Hey @ssbb, I like this idea a lot and I have an idea how this can be implemented easily.

We can add a merge(sage, step, child_sage) which accepts a Sage struct and merges it a way where steps are namespaces, so :effect1 from child sage would become {step, :effect1}.

This can be even added to run/3 because semantically it still does the job.

I even think that its a good idea to accept Ecto.Multi struct in a similar way.