3

One question im struggling with is how to make linear, cross-service operations/transactions in a distributed system.

For example, I have two services: Orders and Payments. How would the Orders service call Payments, while not allowing intra-service coupling?

As I understand the Orders service would emit an event like requestPaymentfor a user and a sum, and the Payments service would react to that, process the payment and then emit something like paymentUpdated with a new status on the payment. Is this correct? Isn't this a coupling in disguise, because the events are so closely tied together?

Assuming this is the way to go, how would it look from an API consumer perspective? Would this all happen while the request was hanging?

I am not interested in Event Sourcing at this point.

5
  • Am I stupid for not doing event sourcing? Is it a de facto standard? I am not sure I have the right team for it
    – Kloar
    Commented Sep 9, 2019 at 11:21
  • 1
    See also How is transactionality implemented in DDD applications?
    – Doc Brown
    Commented Sep 9, 2019 at 11:46
  • 2
    The kind of solution you are looking for is a quimera and, it's likely it will defeat the reasons that led you to implement an MS architecture. If you need ACID transactions, consider implementing all the related domain entities within the same boundary. Review and ask your domain experts if it makes sense, we technicians are bad at designing these "boundaries" because we usually have a limited understanding of the domain we are modelling.
    – Laiv
    Commented Sep 9, 2019 at 14:09
  • 1
    @Laiv I'm worried you are correct. Back to the drawing board
    – Kloar
    Commented Sep 12, 2019 at 14:35
  • Just a note. In terms of MS, your boundary might involve several but minor sub-boundaries. In other words, your Microservice A could be composed by A' and A''. Sub-boundaries are not reachable by other services. Only MS A is available. So, if your boundary becomes "too big" to be "micro", you can still do some internal-split. Use the "transactionality" to define boundaries and sub-boundaries. Keep together "what changes together, at the same time, due to the same reasons and actors". Segregate the rest. That's "make a service as micro as you can, but no too micro" ;-)
    – Laiv
    Commented Sep 12, 2019 at 14:48

2 Answers 2

2

If you need to perform two operations from different microservices in one transaction, this means that either both succeed or non of them is performed.

If you do not want an event queue at all, this would mean that one of the microservices invokes the other in a synchronous manner. You would then have to foresee an API that enable the implementation of a two-phase commit. However this creates some coupling between the services and might not scale so much due to waiting microservices.

If you’d go for an event queue, your transaction would be performed asynchronously, leaving more flexibility for scaling-up and avoiding the master microservice to wait for the other. You could implement a two phase commit based on the queued events (you need to foresee the events that enable the two phase commits, though).

All this doesn't require event sourcing, although event sourcing would greatly simplify the management of the commit phase of the two-phase commit protocol, especially the rollback in case of an unexpected issue.

2
  • Event queues are absolutely part of the stack (we are using SQS and SNS today). I would just like to avoid Event sourcing if possible
    – Kloar
    Commented Sep 9, 2019 at 13:28
  • @Kloar I've added a para graph to clarify.
    – Christophe
    Commented Sep 9, 2019 at 16:13
0

You definitely are on the right track considering from the comments/post I feel confident in assuming this is event based middleware. The life cycle of this process would involve the two event emissions, as you stated, one to trigger the payment apps process and another once that has completed with success/failure, as far as the hanging request if your using this architecture you shouldn't be trying to run this all through an event queue then return it back to the request in one shot. Take in the request, emit the payment request and return a 200 to the client. Your order service should be subscribed to the updates on the payment request (this isn't strongly coupled, its quite alright to have to seperate services emitting events for the same topic) once it receives the failure/success event you can update the state for that payment appropriately and you can either communicate that with a websocket push or a client side request that is checking for an update on payments via an ajax request on a loop.

Not the answer you're looking for? Browse other questions tagged or ask your own question.