I have a service, lets call it MyService.cs
which exists in the business layer of my application. My application has 3 layers, business, persistence and API (regular clean architecture). A method in MyService
needs to interact with a repository in two steps. So first it takes some input from the client, sends it to the repository and saves it to Table 1. Then it takes the newly updated data, does a bit of logic, then sends that data to the repository to be saved in Table 2. Now I want to do this atomically. That is if either of the database saves fail, do a rollback. But I don't have access to the db context from the service layer so I can't create a transaction there. How can I ensure that both of these processes either fail or succeed without access to a transaction? See flow in diagram below.
CodePudding user response:
I believe you will want something like the Unit Of Work pattern. The idea is you won't save until changes have been made to both repositories and therefore they are made as a part of one transaction.
CodePudding user response:
don't have access to the db context from the service layer so I can't create a transaction there.
Well, you really should. The Service layer should be responsible for specifying data shapes (queries) and orchestrating transactions. Queries and data shapes are driven by the requirements of the service layer, and you shouldn't be modifying your repository layer to implement a change to your service layer. The only way to do that is to make your repository layer ignorant of the particular requirements of the services. Similarly when and whether different database reads and writes are a part of a single transaction is not soemthing your repository can or should know.
Just inject the DbContext into the service layer so you can orchestrate the transaction. Other layers executing in the same DI Scope will resolve the same DbContext instance. You can wrap the DbContext in a custom UnitOfWork wrapper if you think it's cleaner.
The other alternative is to use a TransactionScope in your Service Layer and rely on that to flow an ambient transaction through to your other layers. You may need to specify the TransactionScopeAsyncFlowOption to make the transaction flow.