Metoda toDto vs wystawienie getterów w agregacie.

0

Hej, które rozwiązanie uważacie za bardziej słuszne?

Mamy sobie jakiś agregat domenowy dobrze skrojony pod przypadek biznesowy. Akurat tak się składa, że zawiera on wszystkie dane potrzebne na widoku.

Generalnie sugerujecie zrobić metodę toDto, czy lepiej wystawić gettery na takim agregacie i w konwerterkach sobie napisać mapowania na różne dtosy?

1

Jutro moze się okazać że potrzebujesz drugi endpoint który wypluwa inne DTO, albo w ogóle potrzebujesz do tego DTO na front dodać coś jeszcze, wiec średnio to widzę. Mógłbyś ewentualnie mieć jakieś toDto ale takie które "spłyca" ci ten obiekt domenowy i robi jakieś dto, ale nie związane bezpośrednio z tym co idzie na front (czyli potem to przemapowujesz).

0

Ja sugeruję zrobić query na DB do tej tabelki z agregatem i zmapować wynik do DTO, a do agregatu nic nie dodawać. Bedzie to takie bieda Q z CQRS'a ale za to tanie (czasowo). Dzisiaj podgląd, jutro edycja jak to mówią...

0

@Shalom: @0xmarcin
W przypadku, kiedy potrzebuje inne dtosy, które dodatkowo potrzebują inne dane to mogę zrobić jakiś read model dedykowany w JPA albo nawet widok SQL.

Chodzi o proste przypadki, które mają zwrócić skrojone dane z danego agregatu.

Chodzi generlanie o to, czy wystawianie getterów na agregacie to dobre rozwiązanie czy jednak lepiej korzystać z alternatyw jak metody toDto dedykowane read modele lub widoki sql.

3

Nie robić, obiekt domenowy nie powinien zawierać żadnych zależności z warstwy webowej, z bazodanowej zresztą też.

1

Możesz mieć różnych „interesariuszy” danych enkapsulowanych w takim obiekcie domenowym, a co za tym idzie każdy może potrzebować innych danych albo wręcz danych z innych obiektów domenowych. To raz. Dwa: Ciekawie się robi jak nie masz getterów, a trzeba zrobić DTO :) Ja bym zrobił osobny model do odczytu (pakujesz do DTO wynik SQL query bez żadnych obiektów domenowych po drodze), ale to zależy od rozmiaru aplikacji - jak jest coś małego albo CRUD, to nie ma sensu bawić się w 2 modele IMHO

0

@Charles_Ray: piszę tu a nie w komentarzu bo tam nie wstawię kodu. Mamy prosty case:

Jest sobie encja domenowa User, która ma metodę toDto, która jak to ładnie ująłeś spłaszcza tego Usera do struktury danych UserDto.
W fasadzie jest sobie zatem coś w stylu

UserDto createUser(final NewUserDto newUser);

Tak się składa, że rest kontrolerowi to wystarczy i robi sobie po prostu:

ResposneEntity.created().body(facade.createUser(newUser));

Teraz dodatkowo wystawiam sobie moduł klienta, który rest templatą albo innym http clientem uderza do moich endpointów, Tutaj też muszę znać dtosy zwracane i wpadające, żeby otypować to jakoś.

I teraz mamy tak...

z jednej strony UserDto i NewUser jest częścią kontraktu, czyli leży w domenie, bo jak wywalę resty i wstawię konsolę to użyję tego samego.
Z drugiej strony UserDto i NewUser jest używany w warstwie kontrolerów oraz w module klienta.

Chyba, że chcesz tutaj robić mapowania 1 do 1 i byty w stylu UserRestDto i `NewUserRestDto czy coś w tym stylu.

Gdzie u Ciebie w takim razie leżałyby dtosy UserDto i NewUserDto z mojego przykładu? Są one bez wątpienia częścią domeny.

Czy client powinien mieć zależność na dtosy z domeny czy domena na klienta i tam umieszczać dto?

1

Klient nie powinien mieć żadnych zależności, przecież to nie musi być apka Javowa ani nawet klient HTTP. Owszem można sobie ułatwić życie kiedy 99% klientów to jednak Java po HTTP i dać do zassania gotowego klienta, ale wtedy dedykowane dtosy. To jest kontrakt Twoja aplikacja - świat zewnętrzny, a nie pomiędzy Twoimi własnymi klasami.

Gdzie żyją dtosy to rzecz wtórna, jak to mówi Sobótka, żaden projekt się przez to nie wywalił. Aby zachować odpowiednie zależności między warstwami zapewne tak jak piszesz w domenie. Zatem tym bardziej nie powinny być częścią API :)

Jeszcze jedna błahostka, ale zobacz jaka różnica - jak masz DTO aplikacyjne, to warto używać wrapperów typu UserId, aby uniknąć stringologii. Natomiast w API chciałbym mieć po prostu stringa, co pokazuje, że to są 2 rozłącznie ewoluujące modele.

0

@Charles_Ray:

W sumie jeszcze jedno mnie zastanawia jeśli chodzi o chronienie swojej domeny.
Mamy sobie repozytorium z jakąś metodą User save(final User user);

User jako agregat jest package private.

Repo to musimy gdzieś zaimplementować w warstwie infry. No i pojawia się zonk, bo z innego miejsca nie mamy dostępu do tego obiektu User.

Robi się to jakimiś trikami z nazywaniem pakietów?

0

Nie znam tricków i szczerze zlewam ten temat :) lata wojen religijnych nt. package-private, a potem przychodzi taki Kotlin i tak jest pozamiatane. Bardziej reguł chronilbym np. ArchUnitem niż pakietami Javowymi.

On top - jeśli ten User zawiera w sobie jakieś inne klasy, to może je warto chronić pakietowo, a nie samego Usera.

0

w warstwie infry

Repozytorium które zapisuje/wczytuje obiekty domenowe ma być w warstwie infrastruktury? o_O Poza tym ja bym się w ogóle nie bawił w takie głupoty jak package private, tylko robił multi-module project i separacje zapewniał modułami.

1

@Shalom:
Repozytorium to interfejs no nie? On leży w domenie.

Jego implementacja natomiast powinna być w warstwie infry zdaje się :D

0

o_O
interfejs to powinien leżeć w jakimś interfaces, tak żeby domena mogła od niego zależeć, a jednocześnie żeby można było go gdzieś zaimplementować bez konieczności posiadania zależności do domeny (która to zależnośc może nie mieć sensu). Nie wiem czemu implementacja domenowego repozytorium miałaby być w infrastrukturze. Infrastruktura to jest np. RESTowe endpointy, konwersja z/na DTO, ustalanie http response code.
Oczywiście jeśli repozytorium ma sie zajmować tworzeniem obiektów domenowych, to zależność do domeny mieć musi, ale to raczej chyba nikogo nie dziwi...

0

Z tego co wiem Repozytorium to obiekt odczytujący i zapisujący dane, czyli np jakieś repo.save(user); User to agregat.

Jego implementacja może być sqlowa, na mongo, do pliku itd.

Jeśli mamy domenę i infrastrukturę to gdzie powinno leżeć repo a gdzie jego implementacja oO?

0

Jeśli mamy domenę i infrastrukturę to gdzie powinno leżeć repo a gdzie jego implementacja oO?

Ja zawsze mam jednak więcej niż 2 moduły i repozytoria lądują w jednym z nich :)

1

Nie wiem jak to dokładnie wygląda w Javie na poziomie technicznym- dobrze rozumiem że moduł to taki odpowiednik projektu/class library w .Net? Piszę to dla tego że w takim przypadku DTO powinno się znajdować w oddzielnym "module" o którym warstwa domeny nawet nie powinna wiedzieć, co za tym idzie agregat nie powinien mieć możliwości mapowania do DTO o którego istnieniu nie ma pojęcia. Poza tym agregaty to granica transakcyjności, i mapowanie nie leży w ich sferze odpowiedzialności. Do mapowania powinno służyć coś co jest odpowiedzialne za... no właśnie, mapowanie.

Pozostaje zagadnienie samego DTO- jest tu również pewien błąd założeniowy bo nawet przyjmując że agregat technicznie może to zrobić, to o jakim DTO mowa? Co jeśli będzie trzeba zmapować na jakieś nowe DTO, np. na potrzeby konkretnego widoku? Dodamy kolejną metodę do agregatu zamieniając go w jeden wielki mapper?

Piszę to wszystko oczywiście zakładając że mowa o sytuacji gdzie stosowanie agregatów czy ogólnie DDD ma sens.

1

@Shalom:

Chcę tylko wyjaśnić pewną rzecz i być może moje zrozumienie kodu domenowego.

Z tego co czytałem ksiażki i blogi oraz oglądałem konfy i co też wydaje się jak najbardziej logiczne to kod corowy, biznesowy, domenowy (zwał jak zwał) to ten, który wykonuje logikę Twojej aplikacji i jest niezależny od wszystkich zewnętrznych rzeczy. Rozumiem to tak, że jak napiszesz sobie logikę załóżmy jakiegoś kółko i krzyżyk, zamkniesz ją w module mavenowym to samej w sobie jej nie uruchomisz, bo nie masz podłączonych warstwy prezentacji, perstytencji i innych potrzebnych końcówek.

Natomiast możesz zrobić sobie drugi moduł mavenowy z prezentacją w konsoli i persystencją w pliku.
Możesz zrobić sobie kolejny moduł z prezentacją za pomocą końcówek restowych i persytencją gdzieś w SQL.
Możesz zrobić milion innych modułów gdzie zmieniasz tylko infrę, a domena, czyli pierwszy moduł pozostaje bez zmian.

Czy tu się zgadzamy?

To jak poukładasz sobie foldery wewnątrz Twojego modułu corowego (domenowego/biznesowego) wydaje mi się, że jest mniej ważną sprawą i tam możesz sobie mieć jakieś interfaces, ports - kto jak chce.

Tu tak chociażby ma J.Nabrdalik:
(27:55)

albo chociażby u Vernona
https://github.com/VaughnVernon/IDDD_Samples/tree/master/iddd_collaboration/src/main/java/com/saasovation/collaboration/domain/model/calendar

Z czym się nie zgadzasz?

0

Z czym się nie zgadzasz?

Z tym że musisz mieć dwa moduły :) Patrz: https://github.com/Pharisaeus/almost-s3
Nie wiem czy w ogóle udało mi się kiedyś zejść poniżej 4 (domain, server, client, test), bo nie wiem z którego miałbym zrezygnować :)

client jest potrzebny do testów i do tego zeby ktoś inny mógł używać danego serwisu i musi być osobnym jarem, najlepiej bez zależności
test to testy integracyjne całej aplikacji, więc nie mogą być w którymś konkretnym module (no chyba że w server)
zwykle w praktyce modułów mam dużo więcej, bo np. zależę od jakiegoś zewnętrznego serwisu i w domenie ukrywa mi to jakiś mój interfejs z modułu interfaces plus mam osobny moduł który ten interfejs implementuje i wrappuje dostęp do tego zewnętrznego serwisu.

Jasne, można uznać że masz dwa moduły a potem kombinować z jakimś package private albo innymi trikami, żeby ograniczać widoczność, ale ja osobiście wolę zrobić N modułów i mieć explicite zależności pomiędzy nimi, bo wtedy nagle te wszystkie dziwne problemy z dostępem znikają.

No i ciekawi mnie gdzie wrzucisz programowego klienta do swojego serwisu, skoro masz tylko te 2 moduły. Jako że taki klient, z oczywistych względów, musi wiedzieć o endpointach i o DTO to rozumiem że upchasz go w twoim infrastructure? I potem ktoś kto chce używać twojego serwisu musi łyknąć sobie jara z twoimi kontrolerami (i springiem na przykład xD), repozytoriami etc. tylko dlatego że potrzebna mu jest jedna klasa MyServiceClient która wrappuje klienta http plus kilka DTO żeby zmapować request/response? Jak to ma niby wyglądać? o_O

0
Shalom napisał(a):

No i ciekawi mnie gdzie wrzucisz programowego klienta do swojego serwisu, skoro masz tylko te 2 moduły. Jako że taki klient, z oczywistych względów, musi wiedzieć o endpointach i o DTO to rozumiem że upchasz go w twoim infrastructure? I potem ktoś kto chce używać twojego serwisu musi łyknąć sobie jara z twoimi kontrolerami (i springiem na przykład xD), repozytoriami etc. tylko dlatego że potrzebna mu jest jedna klasa MyServiceClient która wrappuje klienta http plus kilka DTO żeby zmapować request/response? Jak to ma niby wyglądać? o_O

Takie rzeczy zależą od organizacji, w której pracujesz jeżeli masz serwis/aplikację, od której nikt inny nie zależy to nie ma problemu z tym, że ktoś inny zaciąga twojego springa. Oczywiście teraz może nie istnieć taki serwis, ale wiadomo że może zaistnieć potrzeba na niego w przyszłości to wciąż dla mnie wygodniej by było trzymać kod aplikacji razem,a jako osobny moduł wystawić API. Przy integracji po HTTPie to tak naprawdę jedyne co inny serwis/aplikacja mógłby potrzebować to DTOsy, ale to zakładając że obydwie aplikacje są napisane w tym samym języku (albo kompatybilnych).

0

jeżeli masz serwis/aplikację, od której nikt inny nie zależy to nie ma problemu z tym, że ktoś inny zaciąga twojego springa

Co? o_O Przepraszam ale to jakiś bełkot. client który ma jakieś zalezności w postaci springa (w konkretnej wersji) jest zupełnie bezużyteczny i nie ma absolutnie sensu, bo w 99% przypadków nie będzie się go dało użyć. Już widze jak ktoś dodaje sobie jako zależność całą twoją aplikację (bo przecież do tego się to sprowadza) żeby móc się z tą aplikacją komunikować xD Nie muszę pytać nawet czy napisałeś w życiu jakiś mikroserwis bo widze że nie.

Oczywiście teraz może nie istnieć taki serwis, ale wiadomo że może zaistnieć potrzeba na niego w przyszłości to wciąż dla mnie wygodniej by było trzymać kod aplikacji razem

Ale programowy klient to nie jest kod aplikacji. To jest kod który pozwala komunikować się z twoją aplikacją. Co więcej I TAK MUSISZ mieć taki kod, żeby napisać testy integracyjne. Różnica jest taka, ze jak zrobisz to w osobnym module, to ktoś kto będzie chciał korzystać z twojej aplikacji moze to zrobić w prosty sposób, a nie re-implementować to co już zostało napisane.
No i nie rozumiem co to dla ciebie znaczy trzymać kod aplikacji razem. Przecież to będzie jeden projekt, tylko osobny moduł. Patrz repo które podlinkowałem wyżej.

Przy integracji po HTTPie to tak naprawdę jedyne co inny serwis/aplikacja mógłby potrzebować to DTOsy

Nie, nie jedyne. Musi też znać endpointy, obsłużyć auth itd. Zaręczam ci ze dużo wygodniej jak możesz dodać sobie zależność a potem w kodzie zrobić:

ServiceClient client = new ServiceClient(host, port);
SomeResult result = client.invokeSomeBusinessAction(someData);

Zamiast siedzieć i rozkminiać pod jaki endpoint trzeba wysłać, jak przygotować input i jak parsować output. Co więcej, jak coś się zmieni to tylko podbijasz wersję klienta i nadal wszystko działa, a nie siedzisz i poprawiasz.

0

Tak widziałem twoje repo, niestety popełniłem kilka mikroserwisów i podejście, o któym pisałem mi się sprawdza. Kiedy cała serwis to jeden moduł, jeden projekt. Jeżeli chodzi o zależności to ogrywam to osobnymi pakietami i poziomem dostępów, gdy część domeny z czasem się rozrasta i zasługuje na wydzielenie to ten pakiet staje się osobnym projektem. Nie rozumiem natomiast o co chodzi z

ktoś kto będzie chciał korzystać z twojej aplikacji moze to zrobić w prosty sposób

Jak ktoś będzie chciał skorzystać z mojej aplikacji to niech zostawi ją w spokoju i się zintegruje po httpie czy jakimś MQ.

Musi też znać endpointy, obsłużyć auth itd.

No tak, musi znać endpointy i generalnie kontrakt serwisu, wypadałoby żeby nie zrobić kuku sobie lub mi. Wypadałoby nawet porozmawiać chwilę, bo moze się okazać, że są inne narzędzia/serwisy/whatever które bedą lepsze dla jego potrzeb lub np. mój serwis nie jest gotowy na obsługę ruchu, jaki ktoś wygeneruje.

Zaręczam ci ze dużo wygodniej jak możesz dodać sobie zależność a potem w kodzie zrobić:

Tak, to prawda można udostępnić taki kod, gdzie ja jako właściciel serwisuje przygotuję kod kliencki, ale jeżeli serwisy się komunikują po HTTPie to ktoś wtedy może dostać w gratisie zależność, której nie potrzebuje np. inny klient HTTP. Zakładając, że 'klient' mojego serwisu może skorzystać z mojego kodu klienckiego, bo mój serwis jest napisany w kotlinie, a jego w pythonie. Generalnie to nie twierdzę, że moje podejście jest jedyne i słuszne, tylko zależy od organizacji i tego jak ludzie się dogadali, żeby pracowac.

0

Jak ktoś będzie chciał skorzystać z mojej aplikacji to niech zostawi ją w spokoju i się zintegruje po httpie czy jakimś MQ.

o_O Jezu jakbym słyszał jednego idiotę u mnie w robocie xD Ale wiesz ze programowy klient to jest integracja po http? :D To nie jest jakiś magiczny dostęp na lewo. To jest zwyczajnie implementacja warstwy komunikacji z serwisem. Myk polega na tym że piszesz ją raz i jeszcze spinasz z kodem samej aplikacji, tak ze zawsze jest spójne, a potem N innych osób moze z tego kodu korzystać, zamiast pisać dokładnie to samo N razy. No ale może płacą ci od linijki kodu i lubisz sobie to samo napisać wiele razy...

ale jeżeli serwisy się komunikują po HTTPie to ktoś wtedy może dostać w gratisie zależność, której nie potrzebuje np. inny klient HTTP

Tak, jeśli ktoś jest takim geniuszem jak sugerowałeś wyżej że do tego client ładuje springa xD U mnie client to jest właśnie taki moduł bez zależności, ma DTO plus klasę która implementuje klienta i tyle. Dzięki temu wspomniany problem w zasadzie nie istnieje.

mój serwis jest napisany w kotlinie, a jego w pythonie

Pewnych rzeczy się nie przeskoczy, ale nie jest niespotykaną praktyką udostępnianie klientów w różnych językach w takiej sytuacji. Integrowałeś się kiedyś z usługami Google albo AWSa? Pisałeś swojego własnego klienta rozkminiając ich API, czy może jednak wziąłeś gotową bibliotekę z klientem?

1

@Shalom
Akurat uważam, że binarny klient to zwykle zło.

  1. Jak jest spięty z resztą serwisu - tak jak np. w https://github.com/Pharisaeus/almost-s3 to pierwszy problem, który następuje to wersjonowanie,
    nawet jak zmienia się coś w części serwera to dostajemy informację, że jest nowy client - i co teraz. (ignore ? brać - cholera wie). W dużych firmach takie zależności mogą powodować, że zależymy od 100 projektów i jak robimy jakieś release to jest zabawnie - bo zanim się skończy, to jakiś team zdąży wypuścić nową wersję (i CI startuje od nowa).
  2. Akurat u Ciebie mało jest mało zależności - vavr - średnie zło, ale komuś może przeszkadzać (np, konfliktować z jego wersją vavr). Często są tu jakieś Springi, commonsy.
  3. Blokujące api to kolejny problem - nie każdemu pasuje - mi średnio.
  4. Sama java ... - wersja javy kolejny problem.

O wiele lepiej jak to jest wystawione jako jakiś Swagger. Oczywiście w teorii klient binarny nie przeszkadza i można potencjalnie z niego skorzystać. W praktyce nauczyłem się jak unikać takich sytuacji jeśli jest wybór. Zwykle w końcu robi się tragedia. Oczywiście - wiele zalęży od tego czy np. tak naprawdę api i tak nie jest w tym samym zespole robione. Jeśli to mój zespół robi i mam 100% możliwośc zawsze pozmieniać kod takiego serwea (i binarnego klienta) to problemu nie ma.

Już nie pamiętam dlaczego, ale nawet nie przychodzi mi do głowy brać klientów od googla (jakiś uraz psychiczny z dawnych czasów) - biorę API i trzaskam requesty jak dziadowie.

1

@jarekr000000

  1. To już kwestia umowy na temat wersjonowania. Jeśli zakładamy że jesteśmy backward compatible (więc jakieś /v1 /v2 itd) to nie musisz brać nowej wersji, o ile nie potrzebujesz nowych ficzerów, jak z każdą inną biblioteką.
  2. Cały myk polega właśnie na tym żeby zależności nie mieć. Tam jest vavr w zasadzie tylko i wyłącznie dla Either, jakby Java dorobiła się swojego Either to i tej zależności by tam nie było. Ot gołe DTO i klasa która wrappuje endpointy i zajmuje sie translacją json<->dto
  3. To znów kwestia raczej samego serwisu, bo jeśli serwis oferuje jakieś reactive stream, to klient powinien to umożliwiać. Ale jeśli serwis jest blokujący, to nic nie stoi na przeszkodzie żeby sam sobie to wrappował jakimś CompletableFuture czy innymi asyncami. Zresztą tak jak pisałem wyżej, uważam że i tak we własnym kodzie bazuje tylko na własnych interfejsach i mam osobny moduł który taki interfejs implementuje i wrappuje mi dostęp do tego zewnętrznego serwisu, więc spokojnie jest tam miejsce na jakieś ansynce.

W praktyce nauczyłem się jak unikać takich sytuacji jeśli jest wybór

No to jeszcze raz ponawiam pytanie: integrowałeś się kiedyś z usługami Google albo AWSem? I robisz to za pomocą ich SDK/klientów czy piszesz własne? :) Pokaż mi proszę swojego ręcznie wydzierganego klienta do obsługi S3 albo EC2... Ale idźmy nawet do czegoś łatwiejszego, piszesz własnego klienta do Kafki, Cassandry czy Elasticsearcha? Bardzo w to wątpię, ale chętnie zobaczę twojego ręcznie naklepanego klienta do tych usług.

0

@Shalom:
ad 3. To nie ma nic wspólnego z serwisem. Serwis to sobie może być anty reaktywny - i jeszcze na EJB 2.0 albo gorzej. Mogę (ze względu na to, że np. korzystam z serwera non blocking) potrzebować nie blokującego klienta i CompletableFuture nie jest tu najlepszym rozwiązaniem ( bo efektywnie zablokuje mi jakiś wątek - lub pulę), a ja może nie chciałbym tak wątkami szafować (bo mam np. cały jeden i zależy mi, żeby był jeden).

Co do Google - generalnie od dawna sam sobie robię klienty na podstawie API - z tym, że dużo nie korzystam (Oauth/OpenID - ale to proste api i na pewno tu się kiedyś zawiodłem na kliencie javowym - ale nie pamiętam o co poszło) - po prawdzie to korzystałem przeważnie 3rd party bibliotek do oauth. Wcześniej healthcare api - całkiem powalone, ale nawet wydaje mi się, że wtedy klienta do teg nie było.

A co do elastica i kafki - masz rację - korzystam z tego co widzę z podanego klienta.

0

@Shalom:

Pewnych rzeczy się nie przeskoczy, ale nie jest niespotykaną praktyką udostępnianie klientów w różnych językach w takiej sytuacji. Integrowałeś się kiedyś z usługami Google albo AWSa? Pisałeś swojego własnego klienta rozkminiając ich API, czy może jednak wziąłeś gotową bibliotekę z klientem?

No tak, ale Google API w założeniu ma nieskończoną liczbę klientów, więc pisanie klienta do ich API w różnych językach jest spoko, natomiast w jednej organizacji już niekoniecznie. Tym bardziej, że niektóre serwisy domenowe z założenia są na końcu łancucha pokarmowego i służą jakiemuś BFF. Ale przyznaję rację, gdybym pisał serwis który w założeniu będzie miał wielu klientów to nie byłoby złe przygotować mu jakiś mały moduł, z którego inni mogliby korzystać.

1

Ale przyznaję rację, gdybym pisał serwis który w założeniu będzie miał wielu klientów to nie byłoby złe przygotować mu jakiś mały moduł, z którego inni mogliby korzystać.

A wiesz ze ten kod i tak musisz napisać, zeby np. zrobić testy integracyjne? Bo przecież taki test integracyjny potrzebuje stuknąć w API, odczytać odpowiedź i porobić asercje. Nawet jak masz zero konsumentów swojego serwisu! Wystarczy zamiast hardkodować to w testach, zrobić w osobnym module i masz dwie pieczenie na jednym ogniu! Bardzo dziwi mnie że dla kogoś jest to coś dziwnego... Oczywiście mówie tu o module z klientem dla tego samego języka/ technologii. Robienie klientów w innych językach zaczyna mieć sens dopiero kiedy faktycznie mamy wielu konsumentów, ale ten pierwszy natywny klient jest niejako za darmo. To nie jest żaden dodatkowy kod, bo i tak trzeba ten kod napisać.

1

Jeśli przygotujesz Swaggera do API to ponieką masz ciasteczko i jesz jeż ciasteczko.
Nie musisz nic pisać - bo klienta sobie wygenerujesz (na jeden z wielu dostępnych styli) - do testów też (przy okazji to dobra dodatkowa walidacja swaggera).
Twoi konsumenci sobie też klienta wygenerują w praktycznie dowolnym języku.
Do tego masz ładne stronki do testowania.
Poświęcasz czas na dokumentowanie i optymalizację samego API, a nie klienta, który być może będzie użyty.

Koszt to oczywiście komplikacja i czas buildu. Ale wolę to jako mniejszcze zło (szczególnie przy współpracy z innymi zespołami / dostawcami).
Jedną z zalet - jest łatwiejsze udowodnienie komuś, że oczywiście działa - lub oczywiście nie działa - wchodzimy na stronkę do testowania swaggera i klikamy.

1

@Aventus:

Nie wiem jak to dokładnie wygląda w Javie na poziomie technicznym- dobrze rozumiem że moduł to taki odpowiednik projektu/class library w .Net? Piszę to dla tego że w takim przypadku DTO powinno się znajdować w oddzielnym "module" o którym warstwa domeny nawet nie powinna wiedzieć, co za tym idzie agregat nie powinien mieć możliwości mapowania do DTO o którego istnieniu nie ma pojęcia.

To czy DTOsy są w osobnym module mavenowym czy pakiecie nie ma różnicy, bo to kwestia organizacji. Zastanawiam się dlaczego piszesz, że warstwa domeny nie powinna o nim wiedzieć? W końcu do domeny musi byc jakiś punkt wejścia (fasada w moim przypadku), która musi zwracać publicznie dostępne API (z owego modułu bądź pakietu) tak żeby klienci nie mieli zależności do 'bebechów' domeny. Taką przynamniej organizacje stostuje i znam.

2

@hcubyc:

Zastanawiam się dlaczego piszesz, że warstwa domeny nie powinna o nim wiedzieć? W końcu do domeny musi byc jakiś punkt wejścia (fasada w moim przypadku), która musi zwracać publicznie dostępne API (z owego modułu bądź pakietu) tak żeby klienci nie mieli zależności do 'bebechów' domeny.

Warstwa domenowa powinna być odpowiedzialna za- jak sama nazwa wskazuje- logikę domenową, obsługę procesów biznesowych i egzekwowanie zasad biznesowych. Mając referencje do rzeczy takich jak DTOs odwracasz kierunek zależności gdzie warstwa domenowa jest bezpośrednio zależna od rzeczy w innych warstwach, podczas kiedy kierunek zależności powinien iść w drugim kierunku- dokładnie tak jak to jest promowane w architekturach typu onion czy hexagonal. Warstwa domenowa powinna być w centrum, i operować na abstrakcjach zamiast na zewnętrznych zależnościach- te powinny implementować te abstrakcje.

Wracając do mojego pierwszego zdania- skoro warstwa domenowa powinna zajmować się kwestiami ściśle domenowymi, to odpowiedzialność za to jak te dane końcowe pochodzące z procesów biznesowych są dalej używane nie leży w gestii tej warstwy. W przypadku tej dyskusji dalej są mapowane na jakieś konkretne DTO, które może służyć do obsługi jakiegoś widoku lub integracji z innym systemem.

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