Prečo Spring Repository nie je DDD Repository

Zdeno Jašek

24.05.2021

Knižnica Spring poskytuje rozhranie Repository, ktoré kedysi o sebe hlásalo, že ide od Repository v zmysle Domain-Driven Design. V nových verziách Springu sa už toto zavádzajúce tvrdenie v dokumentácii nevyskytuje. Dôvod je jednoduchý: Spring rozhranie Repository nie je Repository podľa Domain-Driven Design.

Repository podľa Domain-Driven Design

Účelom Repository v DDD je poskytnúť prístup k takzvaným „agregátom“. Agregát je množina súvisiacich objektov, ktoré má zmysel načítavať a zapisovať v jedinej transakcii. Napríklad osoba môže mať adresu, kontakty, údaje o narodení – to všetko tvorí jeden agregát zložený z viacerých tried.

Dôležitou súčasťou repository je obmedzenie zapisovania a čítania objektov na niekoľko operácií, ktorých potreba vychádza priamo z domény.

Ak máme objekt Person, ktorý reprezentuje osobu v doméne evidencie osôb, môžeme požadovať tieto operácie:

  • vytvor osobu
  • zmeň údaje osoby
  • vyhľadaj osobu podľa mena a priezviska
  • vyhľadaj osobu podľa rodného čísla

Ale už nepotrebujeme napríklad:

  • vymaž osobu
  • vyhľadaj osobu podľa kombinácie rôznych atribútov (bydlisko, pohlavie, dátum narodenia …)

Takto navrhnuté rozhranie odráža v doméne potrebu pre narábanie s objektom Person. Dôležitou súčasťou rozhrania je nezávislosť na technologickej implementácii. Nie je rozhodujúce, či je osoba Person uložená v databáze, alebo v CSV súbore – z pohľadu domény sú to implementačné detaily. Model domény je nezávislý na technológii, čo je súčasťou DDD prístupu a hexagonálnej architektúry.

V aplikáciách sa častokrát vyskytujú aj požiadavky na ľubovoľné vyhľadávanie danej entity. Napríklad osobu môžeme chcieť vyhľadávať podľa bydliska, kontaktných údajov, počtu zmlúv, fakturácie, poistenia, dátumu narodenia atď. Takéto požiadavky však nesmerujú do repository, ale riešia sa pomocou CQRS patternu. Ďalej ich teda nebudeme uvažovať.

Repository podľa Springu

Spring poskytuje veľmi širokú paletu pre prístup k objektom pomocou JpaRepository. Stačí definovať:

Rozhranie PersonRepositorySpring takto pokrýva oveľa širšiu množinu operácií ako PersonRepository z DDD. Navyše má napríklad metódy ako:

  • saveAll
  • findAll
  • delete
  • deleteAllInBatch
  • atď.

Do doménového modelu sa tak dostanú metódy, ktoré v danej doméne byť nesmú (delete), alebo ich použitie môže viesť k problémom s výkonom systému (findAll).

Vlk sýty

V záujme efektívnosti sa môžeme rozhodnúť, že JpaRepository má síce isté muchy, ale pri rozumnom používaní budeme ťažiť z výhod rýchlej implementácie. DDD Repository to síce nie je, ale ak nebudeme takí puristi, ušetríme kopec času.

Nevýhodou prístupu je, že pri veľkom softvéri je ťažké udržať rozumné používanie. Keď vývojár nájde možnosť zavolať metódu PersonRepository.findAll(), použije ju. Lebo veď „nebudeme takí puristi“. Metóda PersonRepository.findAll() môže mesiace celkom dobre fungovať nad testovacími dátami, keď bude v databáze vygenerovaných sto až tisíc záznamov. Až výkonnostné testy nám ukážu, že to nebol dobrý nápad.

Priamočiary prístup je síce rýchly, ale zároveň vytvára v projekte riziká. „Čistota kódu“ je na veľkom projekte investícia, ktorá sa vyplatí.

Vlk sýty aj koza celá

Našťastie, oba prístupy k riešeniu repository sa dajú dobre skombinovať. Do doménového modelu vieme zaviesť Repository presne podľa predstáv Domain-Driven Designu a zároveň napísať jeho implementáciu pomocou JpaRepository.

V našom príklade by to vyzeralo takto:

V doménovom modeli teda ostalo rozhranie PersonRepository, ktoré neposkytuje metódy ako „delete“ alebo „findAll“. Pri takejto implementácii je veľmi vhodné definovať rozhranie PersonRepositorySpring ako „package visibile“, t.j. zrušiť kľúčové slovo „public“ v jeho definícii. Vďaka tomu nebude môcť žiaden vývojár pristupovať k objektom typu Person inak než cez definované DDD Repository.

Výhodou je aj využitie šikovného rozhrania JpaRepository od Springu, vďaka čomu ušetríme prácu pri písaní kódu nad JPA pre implementáciu jednotlivých find-metód.

Nevýhoda je zrejmá – jedna trieda navyše, ktorá obaľuje Springovú knižnicu JpaRepository tak, aby vyhovovala rozhraniu PersonRepository. Znalci design patternov práve prevrátili oči a zašepkali niečo o adaptéroch:

Zhrnutie

Nadpráca pri implementácii Domain-Driven Repository pomocou JpaRepository nie je veľká. Vzhľadom na to, že väčšinu triedy vygeneruje nástroj (deklarácie metód), celá „nadpráca“ pozostáva z dopísania pár jednoduchých riadkov kódu.

Vďaka použitiu adaptéra, ktorý implementuje DDD Repository pomocou JpaRepository, je možné získať výhody čistého doménového prístupu k implementácii a súčasne zrýchliť implementáciu použitím knižníc frameworku Spring.

Ďalšie blog posty, čo by ťa mohli zaujímať