DDD - obiekty domeny zależne od czasu systemowego

0

Witam serdecznie,
ostatnimi czasy projektując prosty model biblioteki umożliwiającej rezerwacje książek natknąłem się na następujący problem - wiele obiektów domeny potrzebowało aktualnego czasu systemowego.

Stosowanie metod typu LocalDate.now() nie wchodzi w grę ze względu na ograniczoną możliwość sensownego testowania. Wstrzykiwanie zegaru systemowego też odpada z tego względu, że obiekty domeny powinny być hermetyczne

Jednym z najbardziej podstawowych założeń obiektów jest hermetyzacja danych wraz z operującą na nich logiką.
~ Eric Evans

Przekazywanie zegaru na poziomie metod też nie wydaje się ładnym rozwiązaniem. Do tej pory udało mi się unikać zależności w obiektach domeny, jak w tym przypadku poradzić sobie z zależnością od czasu?

2

Musisz to sobie owrapować w jakiś swój interfejs i klasę wewnętrznie wywołującą LocalDate.now().

1

Trochę reklama ale potrzebowałem kiedyś podobnej rzeczy i mam z tego małą bibliotekę do javy. Potrzebujesz interface który zapewnia aktualną godzine. W testach podajesz implementacje która zwraca np zawsze 0 albo jakąś datę, a przypadku normalnym masz tam jakieś LocalDateTime.now()

0
danek napisał(a):

Trochę reklama ale potrzebowałem kiedyś podobnej rzeczy i mam z tego małą bibliotekę do javy.

Nie TimeSource tylko TimeSourceable, bo interfejs musi kończyć się na "able".

4

Od kiedy interfejsy muszą kończyć się na "able"?

0

@somekind @danek
Jeśli dobrze rozumiem to przy stosowaniu tego typu podejścia dalej uzależniam obiekty domeny od usługi udostępniającej czas w warstwie wyżej? Chodziło mi bardziej o sam "moment styku" czasu lub zegaru systemowego z obiektami domeny. Zamiast wymienionego podejścia mógłbym równie dobrze zdefiniować ziarno zwracające zegar systemowy i mockować je na potrzeby testów, ale jak napisałem uzależniam wtedy obiekty domeny od warstwy wyżej.

@Bean
public Clock clock() {
    return Clock.systemDefaultZone();
}
1

Chcesz mieć ciastko, zjeść ciastko i jeszcze nie daj Boże nie złamać jakiejś "złotej" zasady. Skoro musisz mieć możliwość jakiegoś sensownego testowania swojej logiki, to wychodzi na to że jednak wypadło by wstrzyknąć zależność w postaci "dostarczyciela" czasu systemowego. Ewentualnie ustaw czas na etapie tworzenia polecenia, zanim zostanie ono wysłane do agregatu. Oczywiście to się nie sprawdzi jeśli w agregacie masz dodatkową logikę operującą na czasie, a zapewne tak jest.

EDIT: Za bardzo to rozbijasz na czynniki pierwsze. Poza tym ten argument z hermetyzacją nie jest jednoznaczny. Reasumując- trzeba zachować zdrowy rozsądek a nie ślepo podążać za dogmatami. Jak zawsze...

0
anon napisał(a):

@somekind @danek
Jeśli dobrze rozumiem to przy stosowaniu tego typu podejścia dalej uzależniam obiekty domeny od usługi udostępniającej czas w warstwie wyżej? Chodziło mi bardziej o sam "moment styku" czasu lub zegaru systemowego z obiektami domeny. Zamiast wymienionego podejścia mógłbym równie dobrze zdefiniować ziarno zwracające zegar systemowy i mockować je na potrzeby testów, ale jak napisałem uzależniam wtedy obiekty domeny od warstwy wyżej.

@Bean
public Clock clock() {
    return Clock.systemDefaultZone();
}

Od obiektu warstwy wyższej? W takim razie rozumiem nie będziesz stosował kolekcji, Stringów itd bo nie są częścia obiektu domenowego? :D
Nie widze dużej róznicy między użyciem klasy Clock a LocalDate/LocalDateTime. Zreszta to dla mnie pokazuje jak bardzo DDD jest dziwaczne, ja bym uzył po prostu architektury hexagonalnej :)

0

Zreszta to dla mnie pokazuje jak bardzo DDD jest dziwaczne(...)

Napisałem klasę singleton i okazało się że nie mogę tworzyć jej wielu instancji. Musiałem użyć do tego refleksji. To dla mnie pokazuje jak bardzo singleton jest dziwaczny...

0
anon napisał(a):

@somekind @danek
Jeśli dobrze rozumiem to przy stosowaniu tego typu podejścia dalej uzależniam obiekty domeny od usługi udostępniającej czas w warstwie wyżej?

No jak masz interfejs, to nie uzależniasz.
Pseudokod dla pokazania idei:

interface TimeMachineable {
    LocalDateTime now();
    Boolean isBefore(LocalDateTime toCompare);
}

class TimeMachine implements TImeMachineable {
    public LocalDateTime  now() { 
       return LocalDateTime.now();
    }

    public  Boolean isBefore(LocalDateTime toCompare) { 
       return LocalDate.now() < toCompare;
    }
}

class Alcohol {
   private ITimeMachineable _timeMachine;

   public class Buy() {
       if (_timeMachine.isBefore(13:00) {
          throw new Exception("Za wcześnie!");
       }
       // dalszy kod
   }
}
0

@Aventus @scibi92
Masz rację być może zbyt przejmuje się złotymi zasadami, które niekonieczne mają przewidziane rozwiązanie na każdy problem.

Jednak przemyślałem sobie wszystko jeszcze raz i wydaje mi się, że w przypadku kiedy duża część logiki biznesowej
jest uzależniona od czasu wtedy jakaś jego reprezentacja np. Clock może stanowić rzeczywisty element domeny.
W tym przypadku zegar byłbym przekazywany poprzez metody obiektów domeny w miejscach gdzie jest potrzebny z warstwy aplikacji,
która ma w założeniu zarządzać elementami domeny. Co o tym myślicie?

1 użytkowników online, w tym zalogowanych: 0, gości: 1