I think leaving for a distributed transaction is the last case using micro services.
I understand that certain activities of a solution are isolated and can
be an easy microservice. But others seem to me too integrated
to be separated.
It is likely, then, that they should not be separated.
What is more, what may seem to be great in isolation, but
then it looks like a case that needs an integration.
For this reason, every micro-service needs to have a well-defined and isolated purpose from others. When a micro service needs one another very much, it’s probably because they should be one single micro service.
For these and others I am a supporter of Monolyth First: only after you meet well the business rules and some domains of your application, the team will be able to make the separation in a quiet and unannounced way. We have to abandon the idea that a monolith is a dated architecture because in many circumstances (in most, I would say) it is not.
However, exceptions must be made. For example, if you are working with a large team (more than 10-15 people) and the team has a good command of the business rule, a monolith in this situation can be a mistake, as there are many people working on the same code base. In this case, going straight to micro services can be a good strategy, where people can be divided into smaller teams and each team can focus on a different micro service.
Specifically I think of transactions. What is the solution when you do
a sale and must update several tables of data?
If you are talking about several tables in different databases, the idea with micro services is that each micro service has its own database and not shared with other micro services, each micro service is able to converse in a non-transitory way with the other micro services. The last thing you want in a "micro services architecture" is a distributed transaction.
This technique is known as eventual consistency. Your micro services need to be prepared to be in an inconsistent state for some time and eventually will be consistent.
An example
You want to save one person and its access permissions. The person is saved in a micro service ms-pessoa
and permissions on micro service ms-permissao
.
Instead of saving a person if your permissions are also saved in the other micro service, you will save pessoa
and warn the micro service permissão
to save permissions. The ms-pesssoa
will return that everything is "ok" for the user while saving the person even without an answer that it went all right with ms-permissao
.
The idea then is that the ms-pessoa
access your permissions in ms-permissao
, prepared to find (or not) the permissions and know how to handle it in your own way.
Of course something can go wrong when saving permissions. Here comes one of the difficulties when using a micro-service architecture. In these cases the solution (or prevention) presents itself in different options:
- Solve the problem in
ms-permissao
and try to reprocess the message with permissions.
- Give a "rollback" in the save person upon realizing the problem. This action may be to simply remove the person or change some kind of status that she has.
- Mark the person with an "inactive" status until you’re sure it’s all right on
ms-permissao
.
- Manually intervene in messages to solve the problem.
- Send a message synchronous: also known as PRC (Remote Procedure Call), it can also be done via messages, by a request HTTP (taking the proper care), etc. The call occurs to the permissions service and awaits its response before continuing the creation of the person. It’s not the expected way to communicate with micro services, but it can help you in some cases that certain micro service needs to do something for you before continuing its processing and you don’t want to deal with the eventual consistency. PRC abuse is usually a symptom that your micro services are more separate than they should be.
- If the micro permissions service
ms-permissao
belong to another company/system, in which you have no control and do not feel enough confidence, it may be necessary to adopt the concept of Poison Message Processing to deal with possible integration problems.
And so on.
Many of the above solutions are only viable with the implementation also of a DLQ queue (Dead Letter Queue) for the queue that presents any problem in the delivery of the message. Thus, problematic messages go to this queue and are waiting for a decision to be made: send the message back to the queue, fix something in the message and send the message to the main queue, use the troublesome message itself to do the rollback previously mentioned, etc.
This example of user and permissions is very critical, maybe they should not be separated. But it is interesting to show a more complicated scenario.
Everything has to be in a single microservice?
Then we would always be doomed to having monolithic systems :).
Alternative: as Business Transactions
As Business Transactions is a technique to create a chain of events that includes flows with events to undo actions. Such as rollback, but implemented manually.
Let’s go to a new example. Imagine you have 3 services: ms-pedido
, ms-estoque
and ms-pagamentos
.
User makes a new request and event PEDIDO_CRIADO_EVENTO is sent. The ms-estoque
and ms-pagamentos
process this event. Everything happens well in the ms-pagamentos
, but the ms-estoque
check that it does not have the product in stock. In this case, the rollback could occur this way:
- The
ms-estoque
sends the event PRODUTO_INDISPONIVEL_EVENTO,
- The
ms-pedido
and ms-payments read the new event
- The
ms-pedido
will cancel the order
- The
ms-pagamentos
will extort the payment
At the end of the day, I only have two options?
Developers often think of extremes: a big monolith vs a lot of micro services exchanging messages with each other. This is a mistake. We don’t have to sell ourselves to any of these extremes.
We can have larger applications in the same system to take care of some transactional problems that we don’t have the time/money to deal with otherwise and, whenever it fits, independent micro services with well-defined functions.
Something important to avoid the perpetuation of the monolith is to have, from the beginning, an infrastructure prepared to deal with micro service. If you want to use in the future an architecture that includes micro services, using Docker, Kubernetes and etc, you and your company will already be prepared for this.
What would be
CAP
?– Dherik
@Dherik actually doesn’t have a question about it here on the site, it would be useful.
– Maniero
Hello @Maniero, I added one more point: synchronous message. This ensures a bit of consistency between micro services when you don’t want to deal, at that point, with eventual consistency.
– Dherik