19 mar 2009

Repozytoria w DDD

W czasie prezentacji o DDD (albo zaraz potem) zadał mi ktoś pytanie o realizację Repozytoriów w javie. Jeśli traktować Repozytoria jako klasyczny wzorzec DAO, to realizacja jest prosta. Robimy generyczne DAO, który obsługuje wszystkie zwyczajne przypadki (CRUD) a tam, gdzie potrzebujemy jakichś szczególnych (bardziej biznesowych) metod, doimplementowujemy to w klasie dziedziczącej po tymże DAO. DAO można zrobić np. tak jak w przykładzie na stronach Hibernate. To rozwiązanie niestety wymaga podawania explicite tego DAO, wszczepiania go tam gdzie chcemy go użyć itp. Oddzielność tej warstwy jest ewidentna (można dyskutować czy to dobrze czy to źle, ale to głównie estetyka). Niektórzy wolą więc zarządzanie obiektami dziedziny w sposób a'la Grails/GORM:
public class RepositoryInsideDomain {

@Test
public void checkSaveFindDelete () {
Author author = new Author();
author.setName("Pawel");

author.save();
assertTrue(Author.isStored(author));

assertNotNull(Author.findById("Pawel"));
assertEquals(author, Author.findById("Pawel"));

author.delete();
assertFalse(Author.isStored(author));
}
}
Co niestety wymaga albo pobrudzenia obiektów dziedziny dodaniem odpowiednich metod z DAO i delegacji do nich (trywialne, nieutrzymywalne i nieładne, więc nawet nie będę pokazywał, bo ktoś powie że to promuję :)),  albo pobawieniem się aspektami. I to rozwiązanie może być ciekawe, więc je tu pokażę (zrealizowane za pomocą aspectj'a). Do tego potrzebujemy zrobić Repozytorium (tu strywializowane, ale oczywiście może być dowolnie bardziej złożone):
public class Repository {
static Map storage = new HashMap();

public Repository() {
}

public static Object findById (Object id) {
return storage.get(id);
}

public void save () {
storage.put(this.toString(), this);
}

public void delete () {
storage.remove(this.toString());
}

public static boolean isStored (Object o) {
return storage.containsKey(o.toString());
}
}
i jeszcze aspekt który pomiksuje to wszystko razem:
public aspect RepositoryInjector {
declare parents: com.lipinski.domaininjection.domain.* extends Repository;
}
Tak niewiele trzeba, by odpowiednie metody repozytorium były dostępne bezpośrednio z klas dziedziny. Dzięki temu nie musimy wszczepiać nigdzie repozytoriów, operujemy wyłącznie na klasach domenowych. Z drugiej strony na poziomie kodu mamy ładną separację dziedziny od infrastruktury. jedyna wada tego rozwiązania, to że nie jest to pure java, i póki się nie zajrzy do tego aspektu to nijak nie wiadomo skąd te metody się znalazły. Ale jeśli to podejście stosujemy konsekwentnie w całej aplikacji, to nie powinien być problem.

2 komentarze:

Unknown pisze...

Nie powinno być public class DefaultRepository()? Konstruktor na to wskazuje.

Jacek
Notatnik Projektanta Java EE

Paweł Lipiński pisze...

Tak to jest jak się robi refactoring nazw w blogu a nie w edytorze... Oczywiście, że konstruktor rąbnięty. Dzięki za zwrócenie uwagi!