Pytania dotyczące ValueObjectów

0

Cześć,

Dopiero wchodzę w tą tematykę, i mam pewne pytania które blokują mnie przed pójściem dalej, a mianowicie:

  • Czy ValueObject'y mogą/powinny być częścią encji ? Mam tutaj na myśli sytuację w której zamiast ID mamy np. UserId który jest ValueObject'em z adnotacją @EmbeddedId czy jakiś Pesel i tego typu inne obiekty.
  • Jeśli np. chciałbym później wyświetlić response w api, to taki DTO powinien zawierać właśnie wyżej wspomniany ValueObject w odpowiedzi czy może to już być zwykły Long (Wyciągnięta wartość z ValueObject'u podczas końcowego mapowania Encja->Dto, które podejście jest bardziej poprawne, i dlaczego ?)
  • Jeśli już bym korzystał z ValueObjectów, weźmy na celownik taki Pesel, to podczas request'a rozumiem że pole powinno być Stringiem a dopiero później w trakcie mapowania DTO->Encja powinno zostać zamienione na VO ? Chyba, że istnieje jakiś sposób aby Spring automatycznie mapował sobie jakieś pole w VO przy adnotacji @RequestBody ?.

Z góry dziękuję za podzielenie się wiedzą :)

2

No tak, jak najbardziej value obiekty mogą być częścią encji tylko warto się zastanowić czy ma to sens czy wystarczyłby zwykły prymityw. Ogólnie value objecty służą do opisywania pojęć, których nie da się wyrazić za pomocą zwykłego stringa czy doubla, np. cena. 100, ale czego? PLN, dolarów? Dlatego często robi się VO Money z polami value i currency itp.
Na front wysyłasz proste dtosy, na które mapujesz encje, żeby nie było couplingu między domeną a jakąś warstwą prezentacji np. restem. Dodatkowo możesz nie chcieć przekazywać wszystkich pól, albo chcieć wyświetlić je w innym formacie niż w encji.
Tak, dostajesz surowego stringa a potem w apcę masz np. jakaś klasę Pesel i do niej przekazujesz tego stringa, którego w niej walidujesz.

1
Sekito napisał(a):
  • Czy ValueObject'y mogą/powinny być częścią encji ? Mam tutaj na myśli sytuację w której zamiast ID mamy np. UserId który jest ValueObject'em z adnotacją @EmbeddedId czy jakiś Pesel i tego typu inne obiekty.

Mogą być częścią encji, natomiast należy się zastanowić czy muszą.

  • Jeśli np. chciałbym później wyświetlić response w api, to taki DTO powinien zawierać właśnie wyżej wspomniany ValueObject w odpowiedzi czy może to już być zwykły Long (Wyciągnięta wartość z ValueObject'u podczas końcowego mapowania Encja->Dto, które podejście jest bardziej poprawne, i dlaczego ?)

Załóżmy, że masz takie klasy:

class MyDto {
  private MyValueObjectA first;
  private MyValueObjectB second;

  // ...
}

class MyValueObjectA {
  private int valueA;
   
  // ...
}

class MyValueObjectB {
  private int valueB;

  // ...
}

Teraz - za pomocą magii adnotacji można zrobić tak, żeby Spring wysyłał to jako:

{
  "valueA": 1,
  "valueB": 2
}

Co więcej, powinno to działać w dwie strony - jeśli dostaniesz taką wiadomość to przy dobrym uadnotowieniu Spring przetłumaczy ci to na obiekt MyDto. Jeśli używasz Jacksona (najpopularniejsza chyba biblioteka do serializacji/deserializacji) - to tutaj masz link: https://www.baeldung.com/jackson-annotations do prezentacji możliwości adnotacji. Ciebie interesuje @JsonUnwrapped, ale polecam przeczytać cały dokument żeby mieć mniej-więcej pojęcie co można ogarnąć za pomocą tej "magii".

  • Jeśli już bym korzystał z ValueObjectów, weźmy na celownik taki Pesel, to podczas request'a rozumiem że pole powinno być Stringiem

W przypadku PESEL - wartość numeryczna to bardzo zły pomysł. To wynika z prostej przyczyny - mianowicie taki PESEL: 00262151324 zostanie zapisany w formacie numerycznym 262151324 - co po prostu będzie nieczytelne. Natomiast jeśli masz np. faktury numerowane w formacie yyyymmdd##### (np. 202201011333) to takie pole może być już typu Long.

a dopiero później w trakcie mapowania DTO->Encja powinno zostać zamienione na VO ? Chyba, że istnieje jakiś sposób aby Spring automatycznie mapował sobie jakieś pole w VO przy adnotacji @RequestBody ?.

Patrz dwa punkty wcześniej.

Ogólnie to dotykasz dosyć ciekawego tematu znanego jako "jak serializować/deserializować" obiekty. Są dwie skrajne szkoły, tj.:

  • albo robimy wszystko ręcznie
  • albo używamy jakiegoś supermądrego frameworka

i oczywiście jest metoda kombinowana - tj. czasem robimy coś ręcznie, czasem delegujemy to na framework. Sam jestem zwolennikiem oszczędności nerwów i energii, tj. jeśli coś jest proste to dorzucam adnotacje, natomiast jeśli coś jest skomplikowane to robię to ręcznie (bo łatwiej to debugować i ogólnie utrzymać). Nie jest to rozwiązanie idealne w każdym przypadku, ale w większości jest wystarczające.

1

@Sekito:

Czy ValueObject'y mogą/powinny być częścią encji ?

Tak, wyobraź sobie, że masz klasę Zamówienie i tam masz pole TotalValue, które jest wyrażone nie tylko za pomocą wartości, ale też np waluty. Albo cena, które tak samo jest wyrażone za pomocą więcej niż jednej wartości.

Jeśli np. chciałbym później wyświetlić response w api, to taki DTO powinien zawierać właśnie wyżej wspomniany ValueObject w odpowiedzi czy może to już być zwykły Long

To zależy. Jeżeli VO zawiera jedną wartość np. zamodelowałeś adres email jako VO, to mógłby to myć typ prosty - dla emaila string. Natomiast w przypadku VO jak wyżej gdzie TotalValue jest typem złożonym, to mógłbyś mieć odpowiadające mu DTO. Generalnie wystawianie modelu domenowego poza domenę to nie jest najlepszy pomysł bo ujawniasz swoje szczegóły implementacyjne. Dlatego do komunikacji domena <-> cała reszta używamy DTO/ViewModeli.

Jeśli już bym korzystał z ValueObjectów, weźmy na celownik taki Pesel, to podczas request'a rozumiem że pole powinno być Stringiem

Mogłoby być. Na pewno trzeba uważać z typami liczbowymi jak napisał wartek01

w trakcie mapowania DTO->Encja powinno zostać zamienione na VO ?

Tak, zwykle do tego używa się gotowych bibliotek i frameworków, które robią to automagicznie (nie znam ekosystemu Javy więc tu nie pomogę).

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