I have a simple use-case: Sync a product from a remote system into the local application.
An excerpt of the buildingblocks implemented:
- SyncProductCommand + associated SyncProductCommandHandler (wrapped in a transaction, because it’s suggested by clean architecture and makes sense too)
- RemoteProductRepositoryInterface (Domain) + RemoteProductRepository (Infrastructure)
The RemoteProductRepository fetches the RemoteProduct via REST-Interface from the remote system. Authenticating using OAuth token (with refresh token). The Repo (in detail an abstracted service / client) decides if the token has to be refreshed. If yes, it will refresh the token with the refresh-token, and saves both (new token and new refresh-token) to the database.
The flow would be something like that (in a very simplified representation):
SyncProductCommandHandler {
handle(productId) {
try {
con.beginTransaction();
remoteProduct = remoteProductRepository.get(productId); // this will maybe refresh the token and save it to the database too
someService.createOrUpdateLocalProduct(remoteProduct); // will throw an exception
con.commit();
} catch (Exception e) {
// this not only rollback the changes made to the local product
// it will also rollback updates to the token and refresh-token
con.rollback();
}
}
}
You can imagine what the problem is… a rollback will also throw away my new oAuth-Token and refreshToken which means that I can no longer connect.
- Clean architecture suggests that every application-command should be in one transaction. Which makes totally sense. Should i break this “law” in that case?
- should i take the opportunity to refresh the token away from the RemoteProductRepository and create a dedicated command RefreshTokenCommand + handler? Doesn’t look good to me, because i create a use-case for a technical detail…
- create a dedicated connection and transaction only for storing tokens, so that a rollback will not affect this?
I think the problem basically exists because there is no rollback for refreshToken.