Logika w konstruktorze, walidacja, wyjątki.

0

W którym miejscu powinna się odbywać walidacja danych obiektu? Np. mam klasę Description (opis), która ma jedno pole text. Mam też fabrykę, która jeśli długość textu się zgadza, to zwraca new Description(text) a jeśli nie to rzuca wyjątek i obiekt nie jest tworzony.

1

Jeśli chcesz pewność że Description jest zawsze poprawny to konstruktor powinien być prywatny, żeby mogła go używać tylko metoda statyczna do tworzenia obiektu. Inaczej każdy może ominąć sobie walidacje i zwyczajnie wywołać new Description(text).
Można też dać walidację do konstruktora, ale tego nie lubię. Konstruktor powinien być głupi.
Gorzej jeśli Walidacja zależy od zmiennego świata zewnętrznego (np od bazy danych, systemu plików)

0

Ja radzę unikać kodu w konstruktorach.

Ja więc życ?

  • Przyjmuj już poprawne obiekty, np. DateTime zamiast string'a w konstruktorze, enum zamiast integera, ValueObject który może być tylko poprawny zamiast DTO
  • Trzeba walidować np. value object zipCode tworzony ze string'a - nie uciekniesz. Zrób prywatny konstruktor + statyczną metodę która zrobi walidację i wywoła ten konstruktor:
ZipCode code = ZipCode.of("08-256");

lub funkcyjnie:

Either<ZipCode, Error> code = ZipCode.tryParse("08-256");
  • W bardziej skomplikowanych przypadkach można walidować w fabryce (np. walidacja wymaga sięgnięcia do bazy danych). Ale nie powinno się to często zdarzać.

Pewne rzeczy np. długości pól - imho nie powinny być w domenie w ogóle walidowane, a jedynie w portach (API, Baza). Długość max. imienia nie ma zazwyczaj żadnego znaczenia w domenie i jest związana tylko i wyłącznie z ograniczeniami technologii.

0

Builder?

2

@Edelner: zapraszam do wątku
Value objecty - walidacja
Co prawda tam jest o VO a nie o encjach ale oj tam oj tam

0

Ja miałem w robocie case, że description musiał być zwalidowany przez osobny mikroserwis, który akurat posiadał wiedzę, żeby zrobić bardziej skomplikowaną walidację.

Najpierw ktoś to zrobił jako prymitywny string i walidował w serwisie.

Zrefactorowałem to tak, że zrobiłem Value Object Description z prywatnym konstruktorem i metodą fabrykującą: static Description validated(String text, DescriptionTester tester);, która miała zwykłego ifa i tworzyła obiekt, jeśli walidacja była ok.

Gdzie DescriptionTester to funkcyjny interfejs (port) z metodą bololean isSafe(string text);

W kodzie produkcyjnym utworzyłem adapter czyli implementacje interfejsu który uderzał do tego osobnego mikroserwisu, a w testach jednostkowych utworzyłem prosty walidator implementujący ten interfejs. Też zyskałem elastyczność, bo gdyby walidacja kiedyś została w osobnym mikroserwisie zaorana i przeniesiona gdzie indziej (albo jednak robiona lokalnie) to kodu domeny nie zmieniam - jedynie adapter.

Miałem dzieki temu pewność, że nikt nie utworzy mi obiektu Description bez walidacji i w nieodpowiedni sposób.

0

@Bambo:
Mam bardzo podobnie, wyjątkiem tego, że zamiast mikroserwisu mam bazę danych tylko widzę pewien problem w twoim kodzie. U ciebie, żeby stworzyć Description, trzeba podać tego testera. Wszystko ok tylko czy nie powinieneś ukryć tego testera przed potencjalnym clientem, żeby ktoś nie zrobił czegoś takiego:
Description.validated("Tekst całego "Pana Tadeusza" czy inny długi string",(text) -> text >= 1)? Nie lepiej w tej metodzie fabrykującej dostarczyć przez jakiegoś providera implementację tego interfejs funkcyjnego?

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