In my Java application, I try to implement hexagonal architecture:
Its inner
core
package contains the pure business logic. These are in the form of plain old Java objects and interfaces.On one side of the core, I have a port package
api
that receives requests from the outside and calls the business logic's interfaces to perform the required steps, such as retrieving or creating an entity.On another side, I have an adapter package
persistency
that implements data retrieval and storage in a database. It is decoupled in hexagonal way, meaning the core provides and calls interfaces, which are implemented by classes in this package.
In a typical scenario, the api receives a REST call, calls the core, and the core calls the persistency-backed adapters for creating/reading/updating/deleting something.
Which of these components optimally creates the database transaction? More precisely, since I use Spring Boot, which package should contain the @Transactional
-annotated methods?
At first thought, my guess would have been that the core should be in control of this, as consistent rollback seems to be a key task to ensuring consistent business data. However, at second glance, transactionality seems to be more of a database detail, and a different implementation of the adapter, such as sending the data on via REST for outside storage, uses completely different schemes.
In non-hexagonal layered architectures, we usually find @Transactional on the uppermost layer, meaning the controllers' methods in the api package. This seems to be in sync with the idea that it is the REST endpoint that decides whether the request is stored completely or partially. However, it seems weird that this border package (api) should manage the details of another border package (persistency). If the core is wired to a different kind of persistency, these Transactionals would become useless.
Another idea could be to put the @Transactionals into the persistency package. But this would mean that other packages are completely unable to control the level of detail and multiple database accesses might result unintentionally in a series of transactions instead of one common one.