3

Currently, I'm working on a project that using MicroServices as the main concept.

For a clearer picture, I'll give you the example:

I got Service A that has its own model and controller.

Basically, Service A only contains basic CRUD operations for Database A.

Second, I got Service B, same like Service A but different database (Database B).

Now, I created 1 Services to consume both Service A and Service B. Currently I'm using TransactionScope for 'wrap' the transaction, but it didn't work.

Here's the code :

//This is the service to call Service A and Service B
using (TransactionScope ts = new TransactionScope())
{
     callServiceAMethod(); // works good
     callServiceBMethod(); // something happened, and failed

     //from here I don't know what should I do
     //What I'm expecting is : if one of the service i just called didn't work as expected, 
     //the transaction will be rolled back else will committed     
    }

Any help will be appreciated :)

tom redfern
  • 30,562
  • 14
  • 91
  • 126
Webster
  • 1,113
  • 2
  • 19
  • 39
  • I think you need to create transaction for each service, store in some kind of an array, if something fails, just loop through all the `ts` instances and roll back. – Mr. Blond Jul 28 '16 at 07:45
  • okay, so that means, i need to create the Model for each service right? Is there any other way, so I don't need to create the model for each service? – Webster Jul 28 '16 at 08:03
  • What model? Sorry, don't really understand. Have you tried to just add `TransactionScopeOption.Required` to the `TransactionScope`? Also you would need to use nested transactioins, although have no idea how to implement that in your solution where service calls other service methods.Please see [Nested transactions- 19. requirement](http://www.codeproject.com/Articles/690136/All-About-TransactionScope) – Mr. Blond Jul 28 '16 at 09:08

2 Answers2

2

Currently I'm using TransactionScope for 'wrap' the transaction, but it didn't work

Wrapping calls to physically external resources in a transaction scope only works under very precisely configured (some would say contrived) conditions.

In your example (assuming service and database are on different physical hosts to each other and the caller), you are going from the caller host, to the service host, across the service boundary, to the database host, into the database, back out across to the service host, back across the service boundary, and back onto the caller host.

In order to propagate a distributed transaction from your client to the database, each intermediary at each step in the call chain must enlist in the transaction.

In order to do this:

  • MSDTC must be enabled and configured properly on each participating host,
  • Service calls must be made using a binding which supports WS-AtomicTransaction (like wsHttpBinding), and
  • Services must have been specifically built to support transactional behaviour.

So unless all this has been done, the fact you are making two services calls in a row makes no difference in this case. A single service call would fail to propagate the transaction down to the DB and back again.

Even if you are already aware of the above and have done all these steps in order to support distributed transactional support across multiple service calls, I would still not recommend you do this. Transactions are costly and encourage shared environmental dependencies to a solution. This is especially so as you are planning a microservices style approach.

Must simpler would be to have each service expose a recovery or rollback operation, so the caller can take the appropriate action and rollback any calls made previously to a failed call. Such an approach is known as a compensation pattern.

tom redfern
  • 30,562
  • 14
  • 91
  • 126
  • thanks for the answer, but unfortunately, the project needs to stick with the microservice concept :( . Even my very first thought about microservice was about the Transaction and boom, it's happening to me now, right in the face. And yes, you're right, even on a single service, it didn't work. Any suggestion? – Webster Jul 28 '16 at 10:01
  • 3
    @Webster I'm not saying the project should not stick with microservices. It's just that forcing microservices to participate in overarching transactional behaviour is an incorrect approach. I would certainly try to talk to whoever has ordained this approach to try to steer them towards a solution which is not dependent on transactional consistency, instead relying on compensatory rollback. – tom redfern Jul 28 '16 at 10:07
2

If I get your description correctly, it looks like the code above is in a 3rd service OR a shared code?

When building autonomous components, they should not share resources or code, as this will defeat the basic idea of loose coupling...

(micro)Service A (component A) should only modify and own the data in DB A and the same for B

Once A had done his task, it should raise an event B is subscribed to, and B will handle the event and do his work.

If any of them fail they can raise an event like xOperationFailed (i.e. CreatUserAccountFailed) and the interested components subscribed to that event will take action according to the business logic (roll back, suspend, send an email to the user or to an admin to take corrective actions). Same goes for success (they can raise a success event).

The hard part is to define the boundaries (responsibilities and data) of each component...

Does that make sense?

Sean Farmar
  • 2,254
  • 13
  • 10