vendredi 11 août 2023

Is it a good idea to introduce additional layer between repository and service?

I don't know if it's a good idea to decouple repository from service by introducing an additional layer.

I see these advantages:

  • Easy to add some features such load-balancing, picking a best repository, add cache and other.

  • Repository and service does not coupled to each other

Disadvantages:

  • Code harder to understand, because a lot of generic types
  • Code harder to write

It's simple example, but it describes what I created.

Entity as interface

interface Book {
    Long getId();

    String getName();
    // other fields related to book entity
}

Jpa implementation:

@Entity
class JpaBook implements Book {
    @Id     
    Long id;
     // Implement interface methods
}

Basic persistent operations that used by repository and additional layer

interface BasicBookPersistentOperations<T extends Book> {
    void save(T book);
    T findById(Long id);
// other methods
}

basic repository interface that used to call the database or use in memory database

interface BookRepository<T extends Book> extends BasicBookPersistentOperations<T> {
    String getRepositoryType(); // String for simplicity, can be JPA, IN_MEMORY, REDIS, whatever
}

Jpa book repository that use jpa spec to call the database

interface JpaBookRepository extends BookRepository<JoaBook>, JpaRepository<JpaBook, Long> {
    
}

And additional layer between repo and service is storage:

// PersistableBook is used as wrapper that does not depend on specific Book implementation
class PersistableBook implements Book {
    // Implement methods from interface
}
interface BookStorage extends BasicBookPersistentOperations<PersistableBook> {}

And then I implement BookStorage, for example, simple impl that delegates to repository

class RepositoryDelegateBookStorage implements BookStorage {
    final BookRepository<Book> delegate;
 
    public RepositoryDelegateBookStorage(BookRepository<? extends Book> delegate) {
        this.delegate = (BookRepository<Book>) delegate; // I don't think it's a good idea to cast it, but if don't do this code will become more complicated
    }

    @Override
    void save(PersistableBook persistableBook ) {
          Book book = convertBookToSpecificEntityImpl(persistableBook);
          delegate.save(book);
    }
    
    @Override
    public PersistableBook findById(Long id) {
        Book book = delegate.findById(id);
        return bookToPersistableBook(book); // Instead of internal methods can be creadted 
        // EntityConverter, but for simplicity I use internal methods
    }
}

I don't know if it's a good or no, can be this refactored to become better? Or it is a complex and should be avoided? Should entities be described using interface and then implemented by different database providers or I should use single class for every database, for example, Redis, Postgres and In memory? What patterns I can use to make code better and easy to extend?

If my question is not clear I can provide more info, thanks!

Aucun commentaire:

Enregistrer un commentaire