Architektura projektu - jak odejśc od klasycznego podziału

1

Zaczynamy w pracy nowy projekt i toczy się dyskusja jak powinna wyglądać architektura, jakie powinniśmy stosować wzorce, dobre praktyki. Generalnie chcielibyśmy żeby wszyscy później się tych wytycznych trzymali, co jest dobrą odmianą bo poprzednie projekty wyglądają tak, jakby każdy kawałek kodu był z innej parafi. Wszystko fajnie, ale pozostaje jeden problem - forsowana jest słynna architektura controller/service/repository. Jak przekonać ludzi do zmiany tego podejscia i zastosowania np. ports & adapters, architektury heksagonalnej?
Generalnie widzę dwa duże plusy - łatwiejsze testowanie oraz czytelniejszy kod, który ma mniejszy próg wejścia dla nowego czlonka zespołu. Co byście dorzucili? Nie pogradzę linkami, ciekawymi artami czy talkami na YT, chciałbym możliwie dobrze zgłębić temat i być może przekonać zespół do zmiany podejścia.

1

Zaletą jest na pewno to że model domenowy nie zależy od warstwy dostępu do danych, dzięki czemu łatwiej modyfikować warstwę DAO, zmieniać "dostawców danych" itp.
Dodatkowo jest mniej błędów kiedy użyje się value objectów, np. jak masz klase gdzie wrzucasz 3 Stringi pesel, email i phoneNumber to łatwo pomylić kolejność, ale jak masz obiekty Pesel, Email, PhoneNumber to już zapamiętasz kolejność. Value objecty nie są chyba bezpośrednio związane z hexagolną ale warto je dodać :)

4

forsowana jest słynna architektura controller/service/repository

Wbrew bzdurom które można czasem (nawet i tu) wyczytać, to nie jest zaden problem ani błąd, jeśli macie mikroserwisy i każdy taki serwis ma bardzo ograniczone odpowiedzialności. Inna sprawa jeśli macie duzy monolit, wtedy faktycznie robi się to mało czytelne. Przy czym ja osobiście nawet na poziomie takiego mikroserwisy zrobił z tego osobne moduły mavena -> server (czyli kontrolery, konfig IoC i generalnie stawianie aplikacji), repository (jedno lub wiecej) i domain gdzie siedzi logika biznesowa, oderwana od technikaliów w stylu frameworki, bazy danych itd. Logika opierająca się tylko na jakichś ogólnych interfejsach.

4

Mi się wydaje że w projektach występuje zjawisko ewolucji architektury, tzn. najpierw każdy zaczyna od lazanii (arch. warstwowej) bo tak najłatwiej, później gdy już projekt okrzepnie i wiadomo jest co z czym i dlaczego, wtedy przeważnie klaruje się obraz co z czym gada i taką lazanie można dzielić pionowo w ten sposób wychodzą bounded contexty / hexagony - jak to tam nazwiesz i wtedy możesz sobie z tego wyjść na mikroserwisy albo artefakty (moduł jako zależność w mavenie). Bardziej chciałem podzielić się rozkminą niż wypowiedzieć w temacie, ale na temat. ;)

Wydaje mi się że obranie na sztywno architektury na samym początku może być bolesne dlatego proponowałbym bardziej ewolucyjne podejście o ile zespół jest gotowy na potencjalne zmiany i nie reprezentuje podejścia "jest jak jest, c**** ale stabilnie". Dla uściślenia: Pisząc architektura mam na myśli: to na jakie pakiety podzielony jest kod źródłowy.

Co do wzorców: wystarczy chyba wpisać w google: java top 10 fancy desing patterns of 2019.

0
MrMadMatt napisał(a):

Wydaje mi się że obranie na sztywno architektury na samym początku może być bolesne dlatego proponowałbym bardziej ewolucyjne podejście o ile zespół jest gotowy na potencjalne zmiany i nie reprezentuje podejścia "jest jak jest, c**** ale stabilnie". Dla uściślenia: Pisząc architektura mam na myśli: to na jakie pakiety podzielony jest kod źródłowy.

Niestety ale właśnie tak to wygląda u nas, jak coś jest c****, ale działa, to nie ruszaj. Refaktoryzacja tego jest bolesna i kosztowna, bo wszystko gada ze wszystkim, masz klasy po 25 pół @Autowired, itp, itd. Dlatego też w nowym projekcie chcemy od początku wprowadzić jakieś zasady których się będziemy stosować + możliwe że się uda zmienić to klasyczne podejście warstwowe (lasagne?) na coś nieco innego, łatwiejszego w utrzymaniu, wprowadzaniu zmian czy wprowadzaniu nowych osób do projektu. Jednak do tego potrzebuje jak najwięcej argumentów, bo mimo wszystko klasyczne podejście było u nas stosowane od X czasu i ciężko może być je zmienić.

scibi92 napisał(a):

Dodatkowo jest mniej błędów kiedy użyje się value objectów, np. jak masz klase gdzie wrzucasz 3 Stringi pesel, email i phoneNumber to łatwo pomylić kolejność, ale jak masz obiekty Pesel, Email, PhoneNumber to już zapamiętasz kolejność. Value objecty nie są chyba bezpośrednio związane z hexagolną ale warto je dodać :)

Stosujemy nieco inne podejście do modelu danych, zazwyczaj jest on nam dostarczany w postaci XSD od analityków, a następnie generujemy Mavenem klasy Javowe. Patrząc na to jak duży zazwyczaj jest ten model danych, nie wyobrażam sobie przepisywania tego w Javie na value obiekty.

3

Podział aplikacji na warstwy, jakkolwiek nie pozbawiony wad nie jest błędem. Powszechność takiego podejścia wynika z prostego faktu, że ono się sprawdza. Nie znaczy to, że jest najlepsze możliwe. Jeżeli nikt w zespole nie ma dogłębnej wiedzy jak ma wyglądać i jakie korzyści ma przynieść podejście heksagonalne, to może nie warto iść w tym kierunku? Wchodzenie na siłę w jakieś rozwiązanie, tylko dlatego, że akurat stało się modne, a nikt nie ma do końca pojęcia jak to ma wyglądać i doświadczenia w implementacji przyniesie więcej problemów niż potencjalnych korzyści.
Dla mnie, głównym problemem na który odpowiada architektura heksagonalna jest separacja funkcjonalnych modułów od siebie wzajemnie, co za tym idzie łatwość w przekształcaniu złożonego projektu np. na mikroserwisy. Oczywiście tak długo, jak nikt nie wpadnie jakieś pilne zadanie i ktoś nie skróci sobie drogi jakimś wynalazkiem w stylu pola statycznego do komunikacji pomiędzy heksami.
Z mojego punktu widzenia, nie ma czegoś takiego jak najlepsza architektura. Każde podejście ma swoje wady i zalety. Sztuką jest takie dobranie wzorca do problemu, żeby zalety były maksymalnie użyteczne do jego rozwiązania, a wady oddziaływały minimalnie. Innej architektury warto użyć w przypadku w pełni zdefiniowanego projektu rozwijanego dla zewnętrznego klienta (zaprojektować-zrobić-zarobić-zapomnieć), a inne podejście sprawdzi się w złożonym projekcie, gdzie na początku developmentu znamy 10% problemów, które pojawią się w projekcie na przestrzeni następnych 10 lat.
I ostatecznie pytanie: dlaczego zakładasz, że warto użyć heksów, zamiast "encja na twarz i pchasz"?

1

Jeśli rzeczywiście mikroserwis jest mały to wtedy podejście controller service repository może miec sens. Ale duzo mikroserwisów jednakch takich małych nie jest i mogą mieć "subdomeny". Nie widzę powodów żeby nie miec heksagonalnej architektury w dużym mikroserwisie.

2
AngryProgrammer napisał(a):

Niestety ale właśnie tak to wygląda u nas, jak coś jest c****, ale działa, to nie ruszaj. Refaktoryzacja tego jest bolesna i kosztowna, bo wszystko gada ze wszystkim, masz klasy po 25 pół @Autowired, itp, itd. Dlatego też w nowym projekcie chcemy od początku wprowadzić jakieś zasady których się będziemy stosować + możliwe że się uda zmienić to klasyczne podejście warstwowe (lasagne?) na coś nieco innego, łatwiejszego w utrzymaniu, wprowadzaniu zmian czy wprowadzaniu nowych osób do projektu. Jednak do tego potrzebuje jak najwięcej argumentów, bo mimo wszystko klasyczne podejście było u nas stosowane od X czasu i ciężko może być je zmienić.

Co do tych 25 pól z @Autowired, wydaje mi się że takie tematy jak ilość argumentów konstruktora/metod/brak @Autowired na polu powinien wam załatwić Sonar / IDE. Mój pro trip jest taki aby kod testów pisać równolegle z kodem aplikacji - takie podejście IMHO zmniejsza ryzyko że nabruździsz w kodzie bo pisanie testów będzie bolesne i jak wyjdzie Ci sytuacja że w teście masz 30 mocków to "something is fucking wrong". ;) Serio, weźcie TDD, SOLID'a, wzorce projektowe i po prostu traktujcie je poważnie, z moich obserwacji wynika że duża część problemów jest nimi rozwiązana a niestety, powyższe "buzzwordy" traktowane są jako bat na rekrutacji a nie jako narzędzie codziennej pracy. Tyle i aż tyle.

Stosujemy nieco inne podejście do modelu danych, zazwyczaj jest on nam dostarczany w postaci XSD od analityków, a następnie generujemy Mavenem klasy Javowe. Patrząc na to jak duży zazwyczaj jest ten model danych, nie wyobrażam sobie przepisywania tego w Javie na value obiekty.

Jednak ja bym się skłaniał aby robić ValueObjecty/klasy wewnętrzne biznesowe niezależne od XSD analityków bo Ci potrafią zmieniać zdanie 4 razy wciągu godziny. Użyjcie jakiegoś ModelMappera i pozdro.

2

Jak sobie podzielisz aplikację domenowo, to każdy z takich modułów będziesz mógł ogarnąć inną architekturą, dopasowaną do problemu. Polecam tutaj

Z drugiej strony teoria to jedna, a praktyka to drugie - wyjście z nędzy nie jest jest usłane różami i jest to proces. Kiedyś refaktoryzowałem "do DDD" system legacy (statici, brak DI, overmocking, PowerMock) - niestety bez wsparcia "góry" i kontraktu w zespole jest to walka z wiatrakami. Jeśli chodzi o technikę, to poczytaj o https://docs.microsoft.com/pl-pl/azure/architecture/patterns/anti-corruption-layer

1

@Charles_Ray: dokładnie. Dla tego nigdy nie należy bagatelizować faktu że DDD to nie tylko kwestie techniczne, ale również (a może przede wszystkim) filozofia pracy, procesów i komunikacji z osobami nie-technicznymi w projekcie.

1
AngryProgrammer napisał(a):

Niestety ale właśnie tak to wygląda u nas, jak coś jest c****, ale działa, to nie ruszaj. Refaktoryzacja tego jest bolesna i kosztowna, bo wszystko gada ze wszystkim, masz klasy po 25 pół @Autowired, itp, itd. Dlatego też w nowym projekcie chcemy od początku wprowadzić jakieś zasady których się będziemy stosować + możliwe że się uda zmienić to klasyczne podejście warstwowe (lasagne?) na coś nieco innego, łatwiejszego w utrzymaniu, wprowadzaniu zmian czy wprowadzaniu nowych osób do projektu. Jednak do tego potrzebuje jak najwięcej argumentów, bo mimo wszystko klasyczne podejście było u nas stosowane od X czasu i ciężko może być je zmienić.

25 wstrzykiwanych pól to błąd na poziomie implementacji, nie architektury. Czyli takie spaghetti zrobione na odczep, usprawiedliwione "mamy IoC", którego zresztą na ogół też nie ma. Jeżeli da się rozwalić. Jakiej byście architektury nie wymyślili, to implementację zawsze da się zrąbać po całości. W heksagonach też da się nie zachować izolacji poszczególnych heksów.

0
Charles_Ray napisał(a):

Jak sobie podzielisz aplikację domenowo, to każdy z takich modułów będziesz mógł ogarnąć inną architekturą, dopasowaną do problemu. Polecam tutaj

Z drugiej strony teoria to jedna, a praktyka to drugie - wyjście z nędzy nie jest jest usłane różami i jest to proces. Kiedyś refaktoryzowałem "do DDD" system legacy (statici, brak DI, overmocking, PowerMock) - niestety bez wsparcia "góry" i kontraktu w zespole jest to walka z wiatrakami. Jeśli chodzi o technikę, to poczytaj o https://docs.microsoft.com/pl-pl/azure/architecture/patterns/anti-corruption-layer

Miałem w sumie inne podejście, tzn. cała aplikacja musi wyglądać identycznie, ale ten filmik dał mi nieco do myślenia, że może to nie najlepszy sposób. Taki prosty CQRS mógłby być u nas rozwiązaniem, gdzie potrzeba to starać się wprowadzić hexy, czy przynajmniej wytyczne mówiące jak ma taki moduł wyglądać, a jeżeli dany fragment ma za zadanie np. tylko strzelić do zewnętrznego systemu po słowniki i je cacheować, to nie ma sensu bawić się w rozbudowaną domenę.

@mar-ek1
Co do modelu danych to kolejny temat, pracujemy nad aplikacją dla banku, której model to ponad 100 encji. Jest to ustalony kontrakt między naszą aplikacją a aplikacją bankową, innymi słowy, generujemy z tego API RESTowe / SOAPowe, którym komunikujemy się z Klientem. Wewnątrz aplikacji co prawda nie zawsze potrzebujemy całości tego modelu - sugerujesz tutaj, żeby wydzielić małe encje domenowe per use case, napisać je samemu i potem mapować z/do wygenerowanych klas?

MrMadMatt napisał(a):

Jednak ja bym się skłaniał aby robić ValueObjecty/klasy wewnętrzne biznesowe niezależne od XSD analityków bo Ci potrafią zmieniać zdanie 4 razy wciągu godziny. Użyjcie jakiegoś ModelMappera i pozdro.

Gdy ten model dostanie błogosławieństwo od biznesu, analityków, architektów, po naszej i Klienta stronie, to nie ma możliwości by go ruszyć. Zazwyczaj jest on później używany w niezmienionej postaci przez wiele aplikacji/serwisów i zmiana tego modelu jest zbyt kosztowna.

piotrpo napisał(a):

I ostatecznie pytanie: dlaczego zakładasz, że warto użyć heksów, zamiast "encja na twarz i pchasz"?

Brak z góry narzuconych reguł + architektura warstwowa = spaghetti.
Miałem okres "skakania" po różnych projektach, mój projekt się skończył i miałem lekki przestój, więc na tydzień wpadłem do projektu X zrobić jakiegoś CRa, na dwa tygodnie do projektu Y żeby pomóc z dojechaniem z zakresem release, do tego po prostu zacząłem oglądać firmowego gita i wyciągnąłem takie wnioski, że jest jedna cześć wspólna - controller/service/repository i wszystko ze wszystkim @Autowired, ale za to każdy commit to inny styl, inne wzorce, albo inne antywzorce, w jednym miejscu czysta Java 1.2, a w drugim przeinżynierowanie, 5 poziomów abstrakcji, nad streamami na 15 linijek. Tak jakby wziąć 4 aplikacje, stworzone na przestrzeni 15 lat, wymieszać je ze sobą i wrzucić do jednego wora.

Pomyślałem, że na pewno warto wprowadzić w życie rzeczy jak SOLID, TDD, w końcu i tak o to pytają na rozmowie więc wszyscy powinni je znać ;) Ale może zmiana podejścia do architektury aplikacji też by się przydała, może narzucenie nie tyle reguł, a wytycznych i egzekwowanie ich na code review, sprawi że ten kod będzie lepszej jakości?

1

sugerujesz tutaj, żeby wydzielić małe encje domenowe per use case, napisać je samemu i potem mapować z/do wygenerowanych klas?

Zawsze przecież powinno się tak robić, chyba że piszesz generic cruda. Nawet więcej, bo dto na wejściu/wyjściu to jedno, model domenowy to drugie a model danych to trzecie.

1
AngryProgrammer napisał(a):

Zaczynamy w pracy nowy projekt i toczy się dyskusja jak powinna wyglądać architektura, jakie powinniśmy stosować wzorce, dobre praktyki. Generalnie chcielibyśmy żeby wszyscy później się tych wytycznych trzymali, co jest dobrą odmianą bo poprzednie projekty wyglądają tak, jakby każdy kawałek kodu był z innej parafi. (...) forsowana jest słynna architektura X Jak przekonać ludzi do zmiany tego podejscia i zastosowania np. Y?
(...)
Niestety ale właśnie tak to wygląda u nas, jak coś jest c****, ale działa, to nie ruszaj. Refaktoryzacja tego jest bolesna i kosztowna, bo wszystko gada ze wszystkim (...) Dlatego też w nowym projekcie chcemy od początku wprowadzić jakieś zasady których się będziemy stosować
(...)
Jednak do tego potrzebuje jak najwięcej argumentów, bo mimo wszystko klasyczne podejście było u nas stosowane od X czasu i ciężko może być je zmienić.

@AngryProgrammer

To, co piszesz, wygląda jak ilustracja prawa Conway'a:
https://en.wikipedia.org/wiki/Conway%27s_law

Szczerze mówiąc nie sądzę, żebyście cokolwiek sensownego wymyślili, bo zespół macie, jaki macie. Pewnie podumacie, jaka ma być architektura (albo pewnie już to zrobiliście, bo minął ponad miesiąc), potem będzie robienie, robienie, robienie i wszystko będzie fajnie, dopóki znowu nie traficie na ścianę. I wtedy znowu jak coś jest chujowe, ale działa, to nie ruszaj..

Czyli jak ma być dobrze, skoro blokowany jest jakikolwiek refaktor, a nad architekturą dużego projektu debatuje sobie przed projektem kilka niezbyt zgodnych ze sobą programistów, którym się wydaje, że jak przed projektem ustalą, jakie wzorce wrzucą do projektu, to potem już wszystko będzie git?

No ale tak wygląda demokracja w tworzeniu projektów - projekt jest posuwany na przód siłą argumentacji słownych (kto przekona większość do danego podejścia). Czyli wygrywa i tak zwykle albo przeciętność (najbardziej mainstreamowe podejście), albo po prostu ktoś z największą charyzmą czy szacunkiem grupy ustala zasady, bo będzie w stanie najskuteczniej przekonać grupę.

Oczywiście jakby nad architekturą czuwał jakiś określony senior-dyktator, to mogłoby być też tak samo bezsensownie (szczególnie jeśli byłby to "senior" tylko z nazwy).

Myślę, że najlepszy sposób projektowania architektury to taki, który polega na faktycznym projektowaniu architektury (na podstawie wcześniejszych doświadczeń i na przewidywaniu tego, co może być potrzebne, co może być zaletą, wadą danego rozwiązania), czyli merytoryczne podejście, a nie takie, które polega głównie na walce o forsowania swojego zdania w grupie czy kłótni o to, który wzorzec jest fajniejszy. No i jednak warto stosować podejście ewolucyjne, które nie boi się refaktoru czy zmiany wadliwych rzeczy w dotychczasowym podejściu.

0

Ktoś chciałby się podzielić linkiem do githuba z gotową DOBRĄ implementacją mikroserwisu w architekturze hexagonalnej? Te które ja znajduję mnie jakoś nie przekonują :)

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