Używając Spring Boot’a wykorzystuje się ciągle koncepcję Repository jako warstwę do komunikacji z bazą danych. Jednak na konferencjach można spotkać się z innym tworem, przeznaczonym do tego samego celu, jakim jest DAO. Pytanie jakie od razu się nasuwa to czym właściwie one się różnią? Czy tylko nazwą, a może stoi coś za tym głębszego? Przeszukałem Internet i oto co znalazłem.

Data Access Layer

DAO jak i Repository należą do warstwy Data Access Layer, w skrócie nazywanej DAL. Jest ona odpowiedzialna za dostęp do bazy danych w aplikacjach zorientowanych obiektowo. Utworzenie takiej warstwy pozwala na tworzenie modułowego kodu co sprawia, że staje się on czytelniejszy. W wcześniej opisanej architekturze warstwowej jest to właśnie warstwa DAL.

Wzorzec DAO

W aplikacjach zorientowanych na dane często zamiennie stosuje się pojęcie DAO oraz Repository. Jednak są to dwa kompletnie różne rozwiązania. Wzorzec DAO jest abstrakcją do operowania na danych znajdujących się w bazie. Dzięki niej można dodawać, aktualizować, pobierać i usuwać dane. Nie musi być tworzona, aby działać tylko na jednym typie danych. Można ją również wykorzystywać do łączenia ze sobą wielu typów danych.

Najlepszą praktyką jest jednak pisanie kodu w taki sposób, aby każda encja miała swoje dedykowane DAO. Czasami oczywiście można (a nawet należy) odstąpić od tej reguły, gdy sytuacja tego wymaga i połączyć kilka tabel w jednym DAO.

Przykład zastosowania

Na początku należy utworzyć nasz obiekt domenowy, którym może być np. Flat.

1
2
3
4
5
6
7
8
9
public class Flat {

  private Long id;
  private String landlord;
  private String tenant;
  private Double rentalPrice;

  // ... rest of class
}

Na jego podstawie kreujemy DAO. W naszym przypadku zapiszemy je pod nazwą FlatDAO. Najlepiej, aby miało ono postać interfejsu. W ten sposób uzyskujemy dodatkową warstwę abstrakcji. Do zaprezentowania przykładu wykorzystałem metody oparte na CRUD.

1
2
3
4
5
6
7
8
9
10
public interface FlatDAO {
    
  void create(Flat flat);
    
  Flat read(Long id);
    
  void update(Flat flat);
    
  void delete(Long id);
}

Oczywiście należy teraz stworzyć konkretną implementację interfejsu, która będzie zawierała w sobie mechanizm komunikacji z bazą danych. Dla każdej metody trzeba zdefiniować zapytanie, którego semantyka odpowiadała będzie wybranemu rozwiązaniu przechowywania danych np. SQL.

Wzorzec Repository

Repository, tak jak i DAO, zajmuje się obsługą pobierania danych z bazy ukrywając zapytania przed światem zewnętrznym. Jednak w odróżnieniu od DAO jest mu bliżej do logiki biznesowej. Z tego powodu wzorzec Repository może wykorzystywać DAO do pobierania danych z bazy i wypełniania nimi obiektu domenowego czy też odwrotnie, wyciągania danych z obiektu domenowego i zapisywania ich w bazie. Ważne jest w tej kwestii zdanie, które możemy znaleźć w książce Erica Evansa „Domain-Driven Design”.

“… repository is a mechanism for encapsulating storage, retrieval, and search behavior, which emulates a collection of objects.”

Ostatnie wyrazy są istotne. Repository ma naśladować zachowanie kolekcji. Nie chodzi o to, aby implementował interfejs Collection, ale aby miał metody takie jak np. add, get, remove czy contains.

Przykład zastosowania

Biorąc jako przykład klasę Flat możemy utworzyć następujące Repository.

1
2
3
4
5
6
7
8
9
10
public interface FlatRepository {
    
  void add(Flat flat);
    
  Flat get(Long id);
    
  void remove(Flat flat);
    
  boolean contains(Flat flat);
}

Nasz powstały przed chwilą interfejs wygląda praktycznie identycznie jak DAO. Jednak to DAO jest odpowiedzialne za bycie warstwą manipulującą danymi w bazie, a Repository jest idealnym kandydatem, aby realizować określone przypadki biznesowe.

Oczywiście w konkretnej klasie implementującej FlatRepository możemy dodać pole FlatDAO i korzystać z jej metod, aby pobierać dane z bazy. Nie musimy ograniczać się tylko do jednego DAO w Repository, równie dobrze możemy wykorzystać kilka.

W przypadku, gdy mamy anemiczny model, czyli taki, który nie jest zbytnio rozbudowany, możemy zaimplementować Repository jako DAO. Oznacza to, że pomijamy warstwę DAO, a Repository przejmuje jego rolę i staje się odpowiedzialny za “rozmowę” z bazą danych.

Porównanie DAO i Repository

Na koniec warto zestawić ze sobą koncepcje jakie stoją za DAO i Repository.

DAO Repository
Abstrakcja przechowywania danych Abstrakcja zbioru/kolekcji danych
Bliżej bazy danych, często zorientowany na konkretną tabele Bliżej domeny biznesowej, znajduje się w agregacie
Koncepcja niższego poziomu Koncepcja wyższego poziomu
Nie może być zaimplementowany jak Repository Może być zaimplementowany jako DAO
Ukrywa zapytania dostępu do danych Ukrywa złożoność przygotowania obiektu domenowego

Warto dodać, że metoda udpate jest jak najbardziej właściwa, aby zaimplementować ją w DAO, ale nie w Repository. W Repository zmiany w obiektach powinny być śledzone przez inne rozwiązania jak np. UnitOfWork (wzorzec śledzący wszystko co dzieje się podczas biznesowej transakcji i ma wpływa na bazę danych).

Podsumowanie

Osobiście uważam, że DAO jest w pewnym stopniu warstwą infrastruktury naszej aplikacji. Natomiast Repository stanowi część naszej dziedziny biznesowej. Warto mieć na uwadze tą różnice w semantyce, aby mieć wspólne podłoże koncepcyjne w całej branży programistycznej. Oczywiście jak ze wszystkim, tutaj też trzeba umieć zachować zdrowy rozsądek i nie debatować długo na tym czy to DAO czy Repository. Jak było widać wcześniej te dwa wzorce mogą zostać połączone w jeden w niektórych przypadkach. Jakie jest Twoje zdanie na ten temat? Podziel się opinią w komentarzu.

Źródła: