196
Views
8
Comments
Solved
How to manage distributed transactions in ODC?
Question
Application Type
Reactive

In ODC i have 3 apps each I am using as DB service. Apps are exposing crud actions witht the help of Service actiojns.

Now sevice actions calls from different apps as one transaction. So either all call shoud be successful or all should roll back.

How to manage transactions in this context or How to manage distributed transactions in ODC?

2024-09-08 11-13-40
Nuno Damaso
 
MVP
Solution

Hi Mangesh

These things are hard to offer help because there isn't a single solution and often it really depends on understanding the full domain you are working on. That being said, let me offer some tips.


Domain and Bounded Contexts

Review your app composition - this may sound like "meh", but seriously, a DDD approach fits very nicely with ODCs architecture - so review your apps compositions. 

Do you really need them to be bounded in isolation in different apps? Do you need an extra app/context maybe? The concept of "DRY", and reusability is not the exact same here and you want things that belong to the same context(the same language) to be under the same app, even if you have to repeat it in a different app with the same functionality (although in a different context/domain language)

https://success.outsystems.com/documentation/outsystems_developer_cloud/app_architecture/building_a_well_architected_app/

Orchestration vs Choreography

For your scenario, implement one of:

  • Orchestration - a service that calls service A then B then C and deals with success/failure for each one. It is also responsible to call compensation strategies(see below) on each one for a successful rollback.
  • Choreography - each service(A,B,C) is aware of what/who to call next and is also responsible for its own compensation. 

Compensation strategies

Outsystems provides a nice video on some patterns to apply, you will need to apply them:

https://learn.outsystems.com/training/journeys/architecture-patterns-581/distributed-transactions-patterns/odc/629

Essentially, you need to have in mind a full compensation strategy when architecting your scenarios within your domain and contexts, and apply auto-retries, rollbacks, model compensations...



I purposely left out the communication pattern - service/event - because its the least of the problems here. But let me also tip on the following:

Atomicity does NOT play well with these architectures. Expecting for a full orchestrated/choreographed rollback on a single transaction will drive anyone nuts. This is where eventual consistency is helpful - model for your services to EVENTUALLY render the model consistent again, even if your model will lag behind in some cases for a few minutes/hours. 

With this in mind, shifting to events will go a long away, where either your orchestrator emits an event for a compensation or, in a choreography, each A,B,C listens to each other for compensation.


Finally, I don't have any .oml to give you, because this is way too abstract of a problem to do that - properly composing your apps in a DDD approach, foreseeing these and a whole lot of other challenges is the way to go - https://success.outsystems.com/documentation/outsystems_developer_cloud/app_architecture/building_a_well_architected_app/


2023-10-19 05-09-59
Mangesh Phanse

Thanks @Nuno Damaso 


Comprehensve inputs. I am going through it.

2023-10-19 05-09-59
Mangesh Phanse

Thanks @Nuno Damaso 

I agree there is no solution to it but Your post help me to conclude that

1 - When we have to consume service actions from different applications Need to write roll back logic

2 - Some scenarios we need to implement Retry

3 - Some scenarios need to manage status of the record cancel/delete etc

4 - Some scenarios need to hard delete.

So it is very abstract and depends on business reuirement

2021-09-06 15-09-53
Dorine Boudry
 
MVP

Hi @Mangesh Phanse ,

i don't have any experience yet with building large systems in ODC, but let me try anyway to give a more general answer :

Application architectures can be imagined (simplyfied) somewhere on a spectrum between a single big monolith and hundreds of small microservices.

On one end of that spectrum you have transactions, which is an extremely convenient tool for ensuring integrity accross all the data, coming at the cost of the monolith being dense and tightly coupled and hard to maintain a single part without touching much more than you'd like.

On the other end of that spectrum, any meaningfull bit of logic will likely span somewhere between a couple and a dozen of separate microservices that can only do their own thing and can't consider any of the other bits because they are completely decoupled.  

This makes all bits agile and changeable without having to consider the world around them, but it comes at a high cost of added complexity, because you will have to put in mechanisms in each microservice to somehow undo what has been done, repairing any change if an event in some other microservice means data integrity is compromised.  

And you'll have to make some sort of orchestration logic to keep track of the total picture (which is done by the transaction on monolith side) and tells the microservices to execute repair logic if needed.

Being somewhere in the middle of that spectrum with your application architecture, in O11 you can choose between server actions and service actions, choosing how tightly or loosely coupled things are, so you can choose the level at which you want to have transactionality, without having to push everything in the same module.

With O11 I can use different levels of coupling, where some modules that belong together (same domain) are tightly coupled through server actions, without having to cram all the code in a single module, and the loose coupling/decoupling is put at a higher level, to span code over different domains, using service actions or api's (and in ODC events).

If in ODC you only have service actions available between apps, this will force you to move on the spectrum either more in the direction of monolith, or more in the direction of microservices, the one making your apps large and harder to maintain, the other, making your architecture complex and expensive and harder to develop / test / troubleshoot / ...

Here are some links that are not related to ODC, but talk about the kind of thing you will have to do to replace transactions :

A medium article about O11 service actions 

A non-OutSystems article about possible patterns to use

Dorine

2023-10-19 05-09-59
Mangesh Phanse

Hi @Dorine Boudry 

Thanks for sharing information on why . Is there any scenario implemented in the os11 to maintain service action as one transaction or on faliure how to roll back. Please share me oml. Same I can implement in the outsystems

Thanks

Mangesh

2021-09-06 15-09-53
Dorine Boudry
 
MVP

No,

by design, it is not one transaction.

Moving towards loose = you have to implement one of the strategies for dealing with distributed transactions, see the links, those are the type of patterns you will have to implement, simply rolling back is not available

Moving towards tight = you have to move more data in a single application to give you the convenience of using a single transaction

2019-01-07 16-04-16
Siya
 
MVP

The short answer is you need to explicitly handle rollback scenarios when they fail, as service actions run in a different transaction.

2023-10-19 05-09-59
Mangesh Phanse

Hi @Siya 

If you can provide sample OML on how to handle server actions it will be great help

2024-09-08 11-13-40
Nuno Damaso
 
MVP
Solution

Hi Mangesh

These things are hard to offer help because there isn't a single solution and often it really depends on understanding the full domain you are working on. That being said, let me offer some tips.


Domain and Bounded Contexts

Review your app composition - this may sound like "meh", but seriously, a DDD approach fits very nicely with ODCs architecture - so review your apps compositions. 

Do you really need them to be bounded in isolation in different apps? Do you need an extra app/context maybe? The concept of "DRY", and reusability is not the exact same here and you want things that belong to the same context(the same language) to be under the same app, even if you have to repeat it in a different app with the same functionality (although in a different context/domain language)

https://success.outsystems.com/documentation/outsystems_developer_cloud/app_architecture/building_a_well_architected_app/

Orchestration vs Choreography

For your scenario, implement one of:

  • Orchestration - a service that calls service A then B then C and deals with success/failure for each one. It is also responsible to call compensation strategies(see below) on each one for a successful rollback.
  • Choreography - each service(A,B,C) is aware of what/who to call next and is also responsible for its own compensation. 

Compensation strategies

Outsystems provides a nice video on some patterns to apply, you will need to apply them:

https://learn.outsystems.com/training/journeys/architecture-patterns-581/distributed-transactions-patterns/odc/629

Essentially, you need to have in mind a full compensation strategy when architecting your scenarios within your domain and contexts, and apply auto-retries, rollbacks, model compensations...



I purposely left out the communication pattern - service/event - because its the least of the problems here. But let me also tip on the following:

Atomicity does NOT play well with these architectures. Expecting for a full orchestrated/choreographed rollback on a single transaction will drive anyone nuts. This is where eventual consistency is helpful - model for your services to EVENTUALLY render the model consistent again, even if your model will lag behind in some cases for a few minutes/hours. 

With this in mind, shifting to events will go a long away, where either your orchestrator emits an event for a compensation or, in a choreography, each A,B,C listens to each other for compensation.


Finally, I don't have any .oml to give you, because this is way too abstract of a problem to do that - properly composing your apps in a DDD approach, foreseeing these and a whole lot of other challenges is the way to go - https://success.outsystems.com/documentation/outsystems_developer_cloud/app_architecture/building_a_well_architected_app/


2023-10-19 05-09-59
Mangesh Phanse

Thanks @Nuno Damaso 


Comprehensve inputs. I am going through it.

2023-10-19 05-09-59
Mangesh Phanse

Thanks @Nuno Damaso 

I agree there is no solution to it but Your post help me to conclude that

1 - When we have to consume service actions from different applications Need to write roll back logic

2 - Some scenarios we need to implement Retry

3 - Some scenarios need to manage status of the record cancel/delete etc

4 - Some scenarios need to hard delete.

So it is very abstract and depends on business reuirement

Community GuidelinesBe kind and respectful, give credit to the original source of content, and search for duplicates before posting.