Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clean Architecture - how to address database transactions?

In 'clean architecture' the interactors (use cases) are responsible for defining busines logic. Most of the examples define use cases in such a way:

public MyUseCase() {

  public boolean execute(...) {
    int id = repository.insert(a)
    if(id > 0) {
      b.aId= id;
      repository.insert(b);
      ...
    }
  }
}

Interactors use mostly simple CRUD like operations or queries on repository. The above example is synchronous for the case of simplicity but you can find repos with the same approach using asynchronous solutions like callbacks or rxjava.

But what about use case inegrity. For instance, you can't be 100% sure that after inserting a it will still be there when you insert b. What if after inserting a you get some RepositoryException while inserting b.

All the repos I've seen so far don't take it into account, so my question is:

What's the solution of the above problem in clean architecture?

like image 543
Marcin Avatar asked Aug 01 '17 18:08

Marcin


People also ask

What is the point of clean architecture?

An important goal of clean architecture is to provide developers with a way to organize code in such a way that it encapsulates the business logic but keeps it separate from the delivery mechanism. The main rule of clean architecture is that code dependencies can only move from the outer levels inward.

What are adapters in clean architecture?

The adapters are the Interface Adapters from the Clean Architecture diagram. All incoming calls from Business Logic get converted into a format that the mechanisms can understand. For example, our Business Logic makes a call—against an interface—to save a Customer.


1 Answers

This answer may be kinda late, but I have struggled with the same problem and came to a conclusion that transaction management is, in fact, part of a Use Case - like, "If something goes wrong with B, revert A's state". So, it can and should be explicitly stated within your UseCase, probably with some kind of "DataManagerRepo", like this:

public MyUseCase() {

    public boolean execute(...) {
        dataManagerRepository.openTransaction()
        try {
            int id = repository.insert(a)
            if(id > 0) {
            b.aId= id;
            repository.insert(b);
            ...
        }
        catch (MyException exc) {
            dataManagerRepository.rollbackTransaction()
        }

        dataManagerRepository.commitTransaction()
    }
}

Names may vary to abstract away integrity mechanism, but the idea is the same. I hope this will help somebody.

like image 199
Mike Doudkin Avatar answered Sep 22 '22 13:09

Mike Doudkin