In software architecture, a transaction represents a sequence of operations treated as a single unit. It is independent of other transactions. This ensures that all operations either succeed or fail, ensuring the data integrity is maintained. While the transaction is changing data, the data might be inconsistent, and other transactions will have to wait while the changing transaction finishes. When a transaction commits, the changes are finalized and permanently stored in a database. Committed transactions cannot be undone. This is because the data is visible to other transactions after being committed, and other transactions rely on this data.
Transaction guarantees
To ensure data integrity transactions must follow the ACID principle. It is an acronym which stands for:
- Atomicity – ensures that all the operations in the transaction are treated as a single unit, meaning either all of the operations will succeed if committed or the transaction will be rolled back
- Consistency – ensures that the transaction will leave the data in a consistent state during the execution, no matter if it is committed or rolled back
- Isolation – ensures that the changes of the ongoing transaction will not affect the data accessed by another transaction
- Durability – ensures that when a transaction is committed, all the changes are permanently stored in a database
A transaction that executes its operations on a single service is called local transaction. In local transactions ACID properties can be applied in a straightforward manner. However, in microservice architecture, the services often have their own database, and transactions span across multiple services. Those are called distributed transactions, and ACID principles cannot be applied. It would require locking databases in all the services and all the services to be available during the transaction, which would significantly decrease the performance.
An alternative approach is called BASE. It focuses on high availability and allows eventual consistency. The idea of this approach is to cut the whole distributed transaction into smaller pieces, each of them still following the ACID principle. To synchronize all the smaller, ACID transactions across the whole system, messaging is used. This means that the changes between the services will be propagated by exchanging messages through the message broker. This is an asynchronous operation and there is a delay between the messages being sent and consumed by another service, meaning that there is also a delay between the first and last ACID transaction. And that explains what eventual consistency is; the data can be temporarily inconsistent due to delays and should eventually synchronize and become consistent. The acronym BASE means:
- Basically Available, meaning the system is available all the time, even in case of failures,
- Soft state, meaning the state of the system can change even thought there are no pending updates
- Eventually consistent, meaning the data will eventually synchronize across the whole system
BASE also meets the constraints of the CAP theorem, which states that in distributed systems all three of consistency, availability and partition tolerance cannot be guaranteed at the same time. CAP stands for:
- consistency – all the microservice nodes have the same view in the data
- availability – every service can read and write data to any other service at any time
- partition tolerance – the system as a whole works despite the physical partitions between the services
Since the microservices should be physically partitioned by default, in the microservice architecture, CAP theorem states that only either consistency or availability can be guaranteed.
Transaction models
The coordination of individual transactions under the scope of an enclosing transaction is described with the transaction models. The simplest are so-called flat transactions, where the entire transaction is rolled back if one of the steps fails. In this model, the steps of the transaction are executed sequentially. In nested transactions, atomic transactions are embedded in other transactions. This model allows transactions to be nested at multiple levels, where each sub-transaction is encapsulated by the parent transaction. It also controls whether the sub-transactions will be reverted. Sagas are similar to the nested transaction. The difference is that in sagas, each transaction has additional compensating transaction which reverts changes if any of the steps fails. Last model describes chained transactions. In this model, each transaction relies on the previous one. When each transaction is executed, it commits its changes and those changes are available to other transactions. On the downside, if one of the steps fails, only the failed one can be reverted, as previous steps are committed.
Concurrency and transaction isolation control
When multiple transactions are running simultaneously and accessing the same data, we are talking about concurrency. Ongoing transactions have an effect on other transactions:
- dirty reads happen when a transaction reads data modified by another transaction that has not been committed yet
- non-repeatable read happens when a transaction reads the same data multiple times but gets different results due to changes made by another transaction
- phantom read occurs when a single transaction reads the same data multiple times and another transaction modifies some data in between the reads
One approach to manage concurrency is through transaction isolation, which defines the degree of separation between simultaneous transactions. It often includes locking mechanisms, in general the following two:
- optimistic locking – assumes that data conflicts are rare and allows multiple clients to access the same data. The updates will commit only if the original data has not been altered since the client first accessed it, and is less suitable for frequently updated data
- pessimistic locking – uses lock to prevent conflicts. For read operations, there are read locks, and for writes, write locks. Read locks are not exclusive, meaning other transactions can read data, while write locks prevent both reads and writes until the transaction is completed
Two-phase commit
One way of applying ACID principles in the microservice architecture is two-phase commit. As the name implies, it ensures updates occur in two phases:
- prepare phase – the transaction manager or coordinator checks if all the participants can execute the commit
- commit phase – the commit is carried out
In the first phase, all the participants check if they can guarantee the execution of the update and respond to the coordinator. The commit phase only happens if all the participants respond with a successful response. If any of the participants cannot proceed, the transaction is aborted.

Let’s take a look at the scenario of an e-commerce shop. A diagram below demonstrates how a two-phase commit works. A coordinator checks if an order can be carried out by asking the order service if the order can be placed and the payment service if a payment can be done. We have a successful case on the left side, both services responded with ready and in the second phase, two commit commands are set to actually execute the order and the payment. On the right side we have a case in which the order service responded with a failure. This can be due to database issues, business rule violations or service issues. In this case, the order is canceled by sending the abort/rollback command to both of the services.

Although this mechanism guarantees high data consistency, it is not recommended for microservice based systems. Two-phase commit works in a synchronous mode, meaning the resource being modified will be locked during the duration of the ongoing transaction and will not be available for other transactions. Furthermore, some modern database technologies and message brokers don’t support distributed transactions. Additionally, it has the following limitations:
- transaction manager can represent a single point of failure
- if one of the participants fails to respond, the entire transaction will be blocked
- a commit can still fail; if one participant responded that it can commit the transaction, 2PC assumes it can definitely commit the transaction, which is not the case all the time
- due to inter-service communication and dependency on the transaction manager the protocol is complex, error-prone and slow by design, which can cause scalability issues
Recommended approach to handle distributed transactions in the microservice architecture implies using asynchronous, event-based communication with local transactions. In this type of architecture, a service responsible for updating a database, will publish an event to the event bus. From there, consumer services can consume those events and update their databases in the scope of a local transaction.
Transaction boundaries
When designing distributed systems, such as microservices, we can’t look at the data storage as a one single relational database with ACID properties. Another issue with the distributed systems is unreliable network and asynchronous communication. Additionally, implementing two-phase commits across multiple services to solve distributed data ignores the autonomy of the services and causes scalability issues. Transaction boundaries refer to the smallest unit of atomicity required to maintain business rules. When designing a system, we want to make transactional boundaries as small as possible, ideally a single transaction on a single object.
References:
- Christudas, Binildas. Practical Microservices Architectural Patterns: Event-Based Java Microservices with Spring Boot and Spring Cloud, Apress, 2019
- Richardson, Chris. Microservices Patterns. Manning Publications, 2019.
- Siriwardena, Prabath, and Indrasiri, Kasun. Microservices for the Enterprise: Designing, Developing, and Deploying, Apress, 2018
- Tudose, Cătălin. Java Persistence with Spring Data and Hibernate, Manning Publications, 2023
- Posta, Christian. “The Hardest Part About Microservices: Your Data”. 2016, https://blog.christianposta.com/microservices/the-hardest-part-about-microservices-data/
- Chandrakant, Kumar. “Introduction to Transactions”. Baeldung, 2024, https://www.baeldung.com/cs/transactions-intro