Czy DTOsy powinny byc wspoldzielone miedzy serwerem a frontem?

0

Czy majac czesc serwerowa i frontendowa napisana w Javie, DTO'sy moga byc wspoldzione - wyniesione do jakiejs wspolnej paczki? Czy lepsza praktyka jest, zeby po dwoch stronach DTO'sy byly "wlasne"? Czy dla tego problemu jest jakas jedna dobra praktyka czy "to zalezy"?

3

Nie wiem co to jest "frontend w javie" ale generalnie jeśli mam 2 serwisy, gdzie jeden korzysta z drugiego (więc siłą rzeczy jeden wystawia pewne DTO a drugi je odbiera) to normalnym podejściem jest zrobienie osobnego modułu "client" który zawiera DTO + klasę która pozwala komunikować się z tym serwisem za pomocą Javowego api (tzn masz obiekt myServiceClient na którym wołasz .getCośtam() i dostajesz CośatmDTO nie zastanawiając się jaki jest URL albo jak zmapować odpowiedź).
Publikujesz taki artefakt w jakimś repo a potem serwisy "korzystające" biorąc to sobie jako zależność. Dzięki temu wszystko zawsze jest "spójne" (o ile podbijasz wersje) i nie trzeba pisać tego samego kodu 15 razy kiedy korzystasz z tego w 15 różnych serwisach.

3

U mnie takie rzeczy geneneruje się z OpenAPI i AsyncAPi. Mamy różne - wersjonowanne - paczki dla różnych języków.

2

Jeśli masz możliwość takiego współdzielenia to jak najbardziej. Dla przykładu w .Net jedną z zalet Blazor jest to że można teraz używać przez aplikację webową DTOs które są dostarczane przez serwer.

3

Jeśli masz taką możliwość (frontend w gwt np) to imho tak. Uważałbym tylko jakie zależności lądują do dto - żeby nie obdażyć np lombokiem nikogo kto nie chce.

0

Ekstra - dzieki za odpowiedzi.

Nie chcialbym zakladac kolejnego tematu, pociagne temat dalej.

Czy DTO'sy moga byc niemutowalne?

Kontekst:
Nasze DTO'sy sa faktycznie "wspolne" dla czesci serwerowej i frontendowej.

Mamy taki problem, ze nasze DTO'sy wchodza do domeny, glowna logika aplikacji sie na nich opiera. Sa "troszke" rozwalone, dla przykladu: DTO's Person ma dwa pola: String name i Boolean isNameValid. Kazde z nich ma swoj getter i setter. Za kazdym razem kiedy zawola sie setName(...) trzeba tez pamietac, zeby zaktualizowac isNameValid. Jak to madrze naprawic?

  1. Zamiana tego na DO'sa == przeoranie projektu. Wszedzie mamy DTO'sy, mocno zagniedzone, takich problemow jest wiecej.
  2. Zamiana na immutable by naprawiła problem, tyle, że nasz frontend potrzebuje getterów i setterów, bo działa przez refleksje :D
  3. Niech front ma swoje DTO'sy?
  4. Usuniecie pola 'isNameValid', a dotychczasowy getter niech ma logike, ktora to sprawdzi? - nie mozna tego wyciagnac do zadnego utila, ma byc w srodku i koniec. :D
  5. Rzucic projekt?
  6. Inne opcje?
2

Mamy taki problem, ze nasze DTO'sy wchodza do domeny

To źle, bo nie powinny.

glowna logika aplikacji sie na nich opiera

Logika powinna opierać się na obiektach domenowych, potencjalnie takich zbudowanych na bazie takiego DTO. Tylko opcja 1 ma sens. Zrobiliście fuszerkę to teraz trzeba poprawić i tyle. Bo to co teraz macie to nic innego niż encja na twarz i pchasz - macie jeden model którego używacie wszędzie, co nie ma sensu. Założe sie, że robicie cyrki w stylu nullowanie niektórych pól obiektu zanim wypchniecie go z kontrolera, bo niektórych informacji (które są potrzebne na poziomie logiki domenowej) nie chcecie publikować w jsonie który idzie z API.

Niemutowalne DTO pogryzą się raczej z frameworkami/libkami do translacji między dto a jsonem i raczej bym tak nie cudował.

2

Jak ze wszystkim w tej branży- to zależy ;)

Idealnie DTO powinno być niemutowalne, bo raz utworzone raczej nie powinno się zmieniać. Nowe DTO to nowy request itp. Ale tutaj dużo zależy od konkretnego języka i na co on pozwala. Np. nowy C# wprowadził record types, dzięki którym można tworzyć niemutowalne obiekty i z nich tworzyć nowe jeśli jest taka potrzeba:

record Person(string Name, string Surname);

var person = new Person("Test", "Tester"); // Test Tester
var newPerson = person with {  Name = "New" }; // New Tester

Jeśli zaś chodzi o przekazywanie DTO do warstwy domeny/biznesowej to ja uważam że to nie jest dobry pomysł, chociażby z powodów które wymieniłeś. DTO używany na potrzeby widoku może zawierać dodatkowe metody, pola czy atrybuty które nie mają nic wspólnego z logiką biznesową jako taką- np. walidacja wymaganego pola.Uważam że lepiej to wydzielić do odpowiednich warstw, i jeśli jest taka potrzeba to w każdej warstwie mieć oddzielne DTO. Oczywiście ma to swoją wadę bo mamy więcej mapowania do zrobienia, ale zaletą jest to że mamy jasno oddzielone warstwy i nic nam nie przecieka do innych warstw. W praktyce ja zazwyczaj spotykam się z tym że mamy 3 warstwy takiego mapowania, czyli właściwe DTO przesyłanego z widoku do serwera, następnie "DTO" w postaci polecenia (command) przesyłanego do warstwy domenowej i już w warstwie domenowej właściwy model domenowy (np. encja). Czy 3 takie warstwy to dużo? Jak już pisałem na wstępie, to zależy.

0

Jeśli DTO jest częścią API front-backend, to czemu nie.

Po tym co czytam obawiam się jednak, że chcecie wystawić do frontu obiekty domenowe, co wskazuje na brak przemyślanego designu.

1
Shalom napisał(a):

niektórych informacji (które są potrzebne na poziomie logiki domenowej) nie chcecie publikować w jsonie który idzie z API.

Niemutowalne DTO pogryzą się raczej z frameworkami/libkami do translacji między dto a jsonem i raczej bym tak nie cudował.

To akurat nieprawda. Jackson bardzo dobrze sobie radzi z niemutowalnymi DTOsami, nawet jak korzysta się z VAVR (listy/optionale), radzi sobie również z kotlinem itp.
Nie wiem jakie tam jeszcze inne frameworki są - bo poza jacksonem to raczej scalowych używałem.

0

@jarekr000000: to w takim razie sa jakies minusy niemutowalnych dto'sow? czy moze lepiej jednak dla swietego spokoju nie swirowac? juz niekoniecznie w kontekscie problemu opisanego wyzej, tylko ogolnie

2

@kek: nie pamiętam kiedy ostatnio robiłem mutowalne DTOsy - nie wiem na grzyba w ogóle taki pomysł.

Z niemutowalnymi problemów nie mam.

1

nie pamiętam kiedy ostatnio robiłem mutowalne DTOsy - nie wiem na grzyba w ogóle taki pomysł.

U mnie DTOsy są tylko na "wejściu" z jakiegoś innego serwisu albo na "wyjściu" zaraz przed wrzuceniem do kontrolera, więc siłą rzeczy nie ma zadnego znaczenia ich "mutowalność", bo w ogóle nikt ich nawet nie próbuje mutować i nawet za bardzo nie ma "gdzie" tego zrobić. Pojawiają się tylko na chwile, wygenerowane z poziomu logiki domenowej, albo są od razu mapowane na jakieś obiekty domenowe, kiedy do nas przychodzą. Możliwe że nowe wersje bibliotek sobie z tym ładnie radzą dziś - pamiętam czasy kiedy taki jakson wymagał bezparametrowego konstruktora i settrów na kazde pole. Tylko ze to wcale nie rozwiązuje problemu OPa, który używa tych DTOsów jako obiektów w warstwie logiki.

0

Jak chcesz mieć spójne kontrakty to może jakiś Protol Buffers? GRPC?
Generalnie to współdzielenie dtosów jest średnim rozwiązaniem bo przynosi małą wartość, a tworzy mocne powiązanie między systemami.

2

Współdzielenie kodu miedzy frontem, a backendem. jest spoko.
Współdzielenie DTO ogranicza błędy przy refaktoringach.
Można współdzielić jeszcze walidacje, całe endpointy (definicje, ale to nie za bardzo w javie)
Jeszcze pewnie parę rzeczy by się znalazło.

Nie za bardzo ma to sens javie, chyba, że front to Swing albo Android w Javie.

2

@Burdzi0: jeśli DTO ma się dostarczone w lekkich paczkach to nie widzę w jaki sposób mogą one wiązać systemy bardziej niż protobuf. Idea jest praktycznie ta sama, inne jest tylko wykonanie.

Poza tym ostatnio z ciekawości zacząłem bardziej zagłębiać temat gRPC bo coraz częściej pojawiały się sugestie aby go używać, nawet tutaj na forum. W rzeczywistości brzmi to jak przysłowiowy młotek i gwoździe. Ludzie chyba to tak polecają bo słyszą buzzword, a większość rzetelnych materiałów na ten temat wymienia wiele wad tego rozwiązania (jak i wiele zalet oczywiście) i wprost mówi że na dzień dzisiejszy gRPC nadaje się do ograniczonych przypadków. gRPC to nie jest jakaś domyślna alternatywa dla klasycznej komunikacji HTTP.

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