We have three layers in our application. Service layer to provide an external API. BO layer for our business logic, and a DAO layer for our database connection.
Let's say every time we update a File, we also want to change something in the Folder, for example 'last modified date'. This needs to be done in a transaction. Either it succeeds and both File and Folder are edited. Or there's a failure and the transaction gets rolled back so both objects are in the previous state.
The "Edit a folder when a file get edited"-action is purely business logic. So this would mean it belongs in the BO-layer. However, we use Objectify for our Database, so to start a transaction we need to call ofy().transact(...). If we call this function in the BO layer, this breaks our design as there will be Database specific calls (Objectify) in our Business layer.
What would be a clean solution for this problem?
2 Answers 2
How you cut your transactions is indeed business logic. So let your DAO layer provide a db framework independent API for the transact
method you mentioned (and probably for things like commit
and rollback
). Then you can use it from your BO layer without making it dependent from your database or your db framework.
Looks like Objectify is designed for atomic-like transactions (Google Application Engine Transactions). It will demand to you to develop your own abstraction of the Transaction Management.
In this case. the abstraction goes on How do I delegate the transaction management to upper layers?
@DocBrown approach looks the faster and cleaner solution for the given architecture (layered). It also seems the safest given the little info we have about the context.
I suggest checking out UnitOfWork design pattern for the business layer because it suits with the transaction management purposed by Objectify.
Briefly summarised, the pattern aims to encapsulate business operations as a whole (unit of work or business transaction). It contextualizes the changes that our business does in the state of the data.
Applied to the given architecture, would look like:
FileService -> FileBO : new EditFileTransaction().execute()
|-> ofy().transact(...)
|--> FileDAO.actionA()
|--> FolderDAO.actionA()
|-> [ofy().commit(...)|ofy().rollback()]
The example above assumes that FileDAO.actionA()
and FolderDAO.actionA()
make changes in the database, hence the ofy().transaction()
at the beginning of the business transaction. Not modifying operations, as for example validations, may take place before beginning the transaction so in case of inconsistencies the business transaction (UoW) can be aborted safely.
-
What can I do if I have to check a business rule inside the transaction? Like if file size greater than 15mb abort transaction.brainoverflow98– brainoverflow982020年05月09日 16:55:41 +00:00Commented May 9, 2020 at 16:55
-
The simplest implementation is, as u say, aborting the transaction. For example, you could rise a business exception. Within the UoW, you might decide not to begin the transaction until all the policies (file size constraint) are checked so that in case of aborting the unit of work, there's no need change to rollback in the data source.Laiv– Laiv2020年05月19日 09:22:54 +00:00Commented May 19, 2020 at 9:22
Explore related questions
See similar questions with these tags.
FileBO
callFolderBO.edit(newDate)
because of the transaction problem ?