Search code examples
c#design-patternsunit-of-workasp.net-boilerplate

Using both database operation and external method in Unit of Work


About Unit Of Work Pattern :

Unit of Work design pattern does two important things: first it maintains in-memory updates and second it sends these in-memory updates as one transaction to the database

Assume that I need to use a web service and update a DB table in same unit of work. Conventionally, I perform these steps:

  1. Open a connection.
  2. Call DB Insert method (there is no risk if it is performed, but it is not FLUSHED at that moment in UOW ).
  3. Call an external web service (an any service from other company).
  4. If web service results is 200 (OK) then call commit, else rollback.

    using (var unitOfWork = _unitOfWorkManager.Begin())
    {
        _personRepository.Insert(person);
        externalWebService.IncrementPeopleCount();
    
        unitOfWork.Complete();
    }
    

unitOfWork has one method: Complete().If web service gets error, it's not problem.Because I can throw exception, so complete() method doesn't execute. But I get error in complete() I must reverse web service?

It would work fine if Insert() method transactional is performed in DB (not commit yet).

How can I do this scenario in Unit of Work pattern? Or is it absolutely necessary to have a reverse web method DecrementPeopleCount?


Solution

  • In Unit of Work you manage transactional operations. In Unit of Work scope, you run some business logic and when all operation is ready, Complete or SaveChanges operations is called. This operations gives you a chance, all your operation finish successfully or all of your operations canceled. In this case, code does not works as a unit. There are two separete operations, insert operations and web service increment operations.

    DecrementPeopleCount operations is not a solution. Imagine something like that: IncrementPeopleCount operation return successfull, however Complete operation return error. After this point you try to call DecrementPeopleCount but webservice is too busy or network problems occur. In this way, still your code does not works as a unit.

    As a solution, you consider change your approach.

    1) WebService call operation can be wrap and converted as transactional operation. I suggest a tool which is name Hangfire. It save operations name and parameters in db and transaction complete it read db and trigger registerd functions. You can save entity and call webservice operation execution in db as one operations.

    2) You can save entity and publish user-created event or increment-user-count command. Your observer/consumer consumed that event/command and execute web api call.

    Both of solutions gives eventually consistancy.