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:
- https://ducmanhphan.github.io/2019-04-28-Repository-pattern/#the-difference-between-repository-pattern-and-dao-pattern
- https://bykowski.pl/dao-czy-repository-jaka-warstwe-dostepu-do-danych-wykorzystywac/
- https://www.baeldung.com/java-dao-vs-repository
- https://newbedev.com/what-is-the-difference-between-dao-and-repository-patterns
- https://stackoverflow.com/questions/8550124/what-is-the-difference-between-dao-and-repository-patterns