{"id":13640437,"url":"https://github.com/VladKopanev/zio-saga","last_synced_at":"2025-04-20T02:33:43.578Z","repository":{"id":35685214,"uuid":"187219128","full_name":"VladKopanev/zio-saga","owner":"VladKopanev","description":"Purely Functional Transaction Management In Scala With ZIO","archived":true,"fork":false,"pushed_at":"2023-09-14T14:34:04.000Z","size":456,"stargazers_count":229,"open_issues_count":0,"forks_count":21,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-08-03T01:16:53.490Z","etag":null,"topics":["concurrency","distributed-systems","fp","functional-programming","saga","saga-pattern","sagas","scala","zio"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VladKopanev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-05-17T13:14:46.000Z","updated_at":"2024-05-06T17:38:17.000Z","dependencies_parsed_at":"2023-02-17T00:15:38.533Z","dependency_job_id":"434d7e4a-af2a-4c7e-a080-7ef0507e3862","html_url":"https://github.com/VladKopanev/zio-saga","commit_stats":{"total_commits":209,"total_committers":9,"mean_commits":23.22222222222222,"dds":"0.26794258373205737","last_synced_commit":"aa12aaca59ce7988d9db506b56fba6c649052a05"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VladKopanev%2Fzio-saga","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VladKopanev%2Fzio-saga/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VladKopanev%2Fzio-saga/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VladKopanev%2Fzio-saga/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VladKopanev","download_url":"https://codeload.github.com/VladKopanev/zio-saga/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223816597,"owners_count":17207887,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["concurrency","distributed-systems","fp","functional-programming","saga","saga-pattern","sagas","scala","zio"],"created_at":"2024-08-02T01:01:11.122Z","updated_at":"2024-11-09T10:31:15.930Z","avatar_url":"https://github.com/VladKopanev.png","language":"Scala","funding_links":[],"categories":["Algorithm","Scala"],"sub_categories":[],"readme":"# ZIO-SAGA\n\n\u003e [!WARNING]\n\u003e This project is no longer supported. For implementing real world sagas consider workflow orchestration tools like Temporal that has available libraries for Scala e.g. [zio-temporal](https://github.com/vitaliihonta/zio-temporal). Also feel free to fork this repository and modify it for your own needs.\n\n[![Support Ukraine](https://img.shields.io/static/v1?label=United24\u0026message=Support%20Ukraine\u0026color=lightgrey\u0026link=https%3A%2F%2Fu24.gov.ua\u0026logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAASwAAADICAYAAABS39xVAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANKSURBVHhe7dZBThRhFEbRnx1IgvtFiIoxbgemOHLAhAoJ1QyaBahroKxqE%2BMS6iZncPKSbwE3b4yr6W58en4Z148zwC5tjbqabrdgvZ59PS5nn2eAfVobtbbquAXrcBquJ4B9Wht1%2BrQEC9g9wQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8j4F6zL%2BTA%2BHpfxYR0A9mhr1OXzPC5u7g%2Fvv%2F1YLr58B9ilU6Nu7ufx6%2BH88Hs6X9YLsEtbo34%2BvJvH29M4LC9jWZ4Admpt1NqqNVjTGqz5bFkmgJ1aG%2FX2KFhAgWABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVk%2FBes1%2BX4dwDYpbVRa6uOW7Du3p7Hy1YvgF3aGjWN2z9qCgwkg1n6XwAAAABJRU5ErkJggg%3D%3D)](https://u24.gov.ua)\n[![badge-scala-ukraine](https://img.shields.io/badge/Scala-Ukraine-EBD038?labelColor=4172CC)](https://t.me/scala_ukraine)\n| CI | Coverage | Release |  |\n| --- | --- | --- | --- |\n| [![Build Status][Badge-Travis]][Link-Travis] | [![Coverage Status][Badge-Codecov]][Link-Codecov] | [![Release Artifacts][Badge-SonatypeReleases]][Link-SonatypeReleases] | [![Scala Steward badge][Badge-ScalaSteward]][Link-ScalaSteward] |\n\nBuild your transactions in purely functional way.\n\nzio-saga allows you to compose your requests and compensating actions from Saga pattern in one transaction\nwithout any boilerplate.\n\n\nBacked by ZIO it adds a simple abstraction called Saga that takes the responsibility of\nproper composition of effects and associated compensating actions.\n\n# Getting started\n\nAdd zio-saga dependency to your `build.sbt`:\n\n`libraryDependencies += \"com.vladkopanev\" %% \"zio-saga-core\" % \"0.4.0\"`\n\n# Example of usage:\n\nConsider the following case, we have built our food delivery system in microservices fashion, so\nwe have `Order` service, `Payment` service, `LoyaltyProgram` service, etc. \nAnd now we need to implement a closing order method, that collects *payment*, assigns *loyalty* points \nand closes the *order*. This method should run transactionally so if e.g. *closing order* fails we will \nrollback the state for user and *refund payments*, *cancel loyalty points*.\n\nApplying Saga pattern we need a compensating action for each call to particular microservice, those \nactions needs to be run for each completed request in case some of the requests fails.\n\n![Order Saga Flow](./images/diagrams/Order%20Saga%20Flow.jpeg)\n\nLet's think for a moment about how we could implement this pattern without any specific libraries.\n\nThe naive implementation could look like this:\n\n```scala\ndef orderSaga(): IO[SagaError, Unit] = {\n    for {\n      _ \u003c- collectPayments(2d, 2) orElse refundPayments(2d, 2)\n      _ \u003c- assignLoyaltyPoints(1d, 1) orElse cancelLoyaltyPoints(1d, 1)\n      _ \u003c- closeOrder(1) orElse reopenOrder(1)\n    } yield ()\n  }\n```\n\nLooks pretty simple and straightforward, `orElse` function tries to recover the original request if it fails.\nWe have covered every request with a compensating action. But what if last request fails? We know for sure that corresponding \ncompensation `reopenOrder` will be executed, but when other compensations would be run? Right, they would not be triggered, \nbecause the error would not be propagated higher, thus not triggering compensating actions. That is not what we want, we want \nfull rollback logic to be triggered in Saga, whatever error occurred.\n \nSecond try, this time let's somehow trigger all compensating actions.\n  \n```scala\ndef orderSaga(): IO[SagaError, Unit] = {\n    collectPayments(2d, 2).flatMap { _ = \u003e\n        assignLoyaltyPoints(1d, 1).flatMap { _ =\u003e \n            closeOrder(1) orElse(reopenOrder(1)  *\u003e IO.fail(new SagaError))\n        } orElse (cancelLoyaltyPoints(1d, 1)  *\u003e IO.fail(new SagaError))\n    } orElse(refundPayments(2d, 2) *\u003e IO.fail(new SagaError))\n  }\n```\n\nThis works, we trigger all rollback actions by failing after each. \nBut the implementation itself looks awful, we lost expressiveness in the call-back hell, imagine 15 saga steps implemented in such manner,\nand we also lost the original error that we wanted to show to the user.\n\nYou can solve this problems in different ways, but you will encounter a number of difficulties, and your code still would \nlook pretty much the same as we did in our last try. \n\nAchieve a generic solution is not that simple, so you will end up\nrepeating the same boilerplate code from service to service.\n\n`zio-saga` tries to address this concerns and provide you with simple syntax to compose your Sagas.\n\nWith `zio-saga` we could do it like so:\n\n```scala\ndef orderSaga(): IO[SagaError, Unit] = {\n    import com.vladkopanev.zio.saga.Saga._\n\n    (for {\n      _ \u003c- collectPayments(2d, 2) compensate refundPayments(2d, 2)\n      _ \u003c- assignLoyaltyPoints(1d, 1) compensate cancelLoyaltyPoints(1d, 1)\n      _ \u003c- closeOrder(1) compensate reopenOrder(1)\n    } yield ()).transact\n  }\n```\n\n`compensate` pairs request IO with compensating action IO and returns a new `Saga` object which then you can compose with other\n`Sagas`.\nTo materialize `Saga` object to `ZIO` when it's complete it is required to use `transact` method.\n\nAs you can see with `zio-saga` the process of building your Sagas is greatly simplified comparably to ad-hoc solutions. \nZIO-Sagas are composable, boilerplate-free and intuitively understandable for people that aware of Saga pattern.\nThis library let you compose transaction steps both in sequence and in parallel, this feature gives you more powerful control \nover transaction execution.\n\n# Advanced\n\nAdvanced example of working application that stores saga state in DB (journaling) could be found \nhere [examples](/examples).\n\n### Retrying \n`zio-saga` provides you with functions for retrying your compensating actions, so you could \nwrite:\n\n ```scala\ncollectPayments(2d, 2) retryableCompensate (refundPayments(2d, 2), Schedule.exponential(1.second))\n```\n\nIn this example your Saga will retry compensating action `refundPayments` after exponentially \nincreasing timeouts (based on `ZIO#retry` and `ZSchedule`).\n\n\n### Parallel execution\nSaga pattern does not limit transactional requests to run only in sequence.\nBecause of that `zio-saga` contains methods for parallel execution of requests. \n\n```scala\n    val flight          = bookFlight compensate cancelFlight\n    val hotel           = bookHotel compensate cancelHotel\n    val bookingSaga     = flight zipPar hotel\n```\n\nNote that in this case two compensations would run in sequence, one after another by default.\nIf you need to execute compensations in parallel consider using `Saga#zipWithParAll` function, it allows arbitrary \ncombinations of compensating actions.\n\n### Result dependent compensations\n\nDepending on the result of compensable effect you may want to execute specific compensation, for such cases `zio-saga`\ncontains specific functions:\n- `compensate(compensation: Either[E, A] =\u003e Compensator[R, E])` this function makes compensation dependent on the result \nof corresponding effect that either fails or succeeds.\n- `compensateIfFail(compensation: E =\u003e Compensator[R, E])` this function makes compensation dependent only on error type \nhence compensation will only be triggered if corresponding effect fails.\n- `compensateIfSuccess(compensation: A =\u003e Compensator[R, E])` this function makes compensation dependent only on\nsuccessful result type hence compensation can only occur if corresponding effect succeeds.\n\n### Notes on compensation action failures\n\nBy default, if some compensation action fails no other compensation would run and therefore user has the ability to \nchoose what to do: stop compensation (by default), retry failed compensation step until it succeeds or proceed to next \ncompensation steps ignoring the failure.\n\n### Cats Compatible Sagas\n\n[cats-saga](https://github.com/VladKopanev/cats-saga)\n\n[Link-Codecov]: https://codecov.io/gh/VladKopanev/zio-saga?branch=master \"Codecov\"\n[Link-Travis]: https://travis-ci.com/VladKopanev/zio-saga \"circleci\"\n[Link-SonatypeReleases]: https://oss.sonatype.org/content/repositories/releases/com/vladkopanev/zio-saga-core_2.12/ \"Sonatype Releases\"\n[Link-ScalaSteward]: https://scala-steward.org\n\n[Badge-Codecov]: https://codecov.io/gh/VladKopanev/zio-saga/branch/master/graph/badge.svg \"Codecov\" \n[Badge-Travis]: https://travis-ci.com/VladKopanev/zio-saga.svg?branch=master \"Codecov\" \n[Badge-SonatypeReleases]: https://img.shields.io/nexus/r/https/oss.sonatype.org/com.vladkopanev/zio-saga-core_2.11.svg \"Sonatype Releases\"\n[Badge-ScalaSteward]: https://img.shields.io/badge/Scala_Steward-helping-brightgreen.svg?style=flat\u0026logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVladKopanev%2Fzio-saga","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FVladKopanev%2Fzio-saga","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVladKopanev%2Fzio-saga/lists"}