Jako początkujący programista mam wybór architektury do nowego projektu.

1

Mam pewien problem.
Opisze lakonicznie swoją sytuacje projektowa.

Wiecie jak to jest w firmach. Ludzie odchodzą, projekty zostają.
Jestem na poziomie junior/mid (w pracy dostałem mida, chociaż te przedrostki tez często nic nie znaczą)
Dowiedziałem się, ze będę głównym odpowiedzialnym za nowy projekt, wraz z jego architektura. Apka restowa.

U nas zawsze robiło się dość słaba architekturę, czasem miałem nawet wrażenie, ze jej nie ma, w starszych projektach klasy po kilkaset linii, brak testow i inne rzeczy dające się często usłyszeć na prelekcjach o złym kodzie.

Ostatnio ze starszym kolega staraliśmy się to wszystko zmieniać, wdrażając jakieś dobre praktyki, czysty kod, małe funkcje, dobre nazewnictwo i testy, a nie pisząc coś by tylko działało.

Po jego odejściu jestem ja i dwójka młodych świeżo po studiach. Nie mam żadnego doświadczenia w kwestiach wyboru architektury.

Chciałbym przygotować ta aplikacje na dalszy jej rozwój, tak by nie tylko urodzić dziecko a jeszcze żeby urosło fajnie, czy jakoś tak.

Aplikacja komunikacja się z bazą lub bazami, wystawiająca restpointy na zewnątrz. Nic nadzwyczajnego, miliony takich powstało.

Chce uciec od anemicznego podejścia, ale również chce zrobic to po to by nie wpaść w błoto i nie wybrać czegoś co mnie zje. Czytałem i słuchałem o podejściu cqrs, wiem ze jest jego wiele wariantów.
Za chwile będę robił POCa z jakimś wariantem cqrs.

Moje pytania to:

  1. Jak bardzo cqrswa apka może być na pierwszy raz. Tzn, od którego momentu zacząć podział. Chciałbym by została jedna baza, ale również chciałbym cos więcej niż tylko podział restow.

  2. Na czym się skupić najbardziej, czy sa jakieś mocno newralgiczne miejsca w tej architekturze?

  3. Czy ma ktos albo wie gdzie szukać jakiś większy fajny projekt z Javy oparty o cqrs?

5

Ja bym zaczął od tego żeby zrobić dobrą separacje warstw, w przypadku javy np. podzielić aplikacje od razu na odseparowane moduły (np. maven modules). Taki najbardziej podstawowy podział to odcięcie API od logiki domenowej i np. repozytoriów danych. Komunikacja między tymi klockami za pomocą interfejsów tak, że np. zmiana repozytorium z SQLa na cokolwiek innego nie powoduje żadnej zmiany w innych miejscach, bo np. domena polega tylko na jakimś XYZRepository i nie ma znaczenia jak to jest zaimplementowane pod spodem.

14

Odnośnie CQRS, zwróć uwagę na sekcje kiedy stosować: https://martinfowler.com/bliki/CQRS.html

Jeśli nie masz z tym doświadczenia ani nikogo kto w ten sposób realizuje projekty to nie rób tego. Zrobisz w ten sposób z projektu eksperyment, który podnosi ryzyko oraz koszty utrzymania. Ty sobie odejdziesz z firmy jak coś, a inni będą musieli się z tym użerać.

Pomyśl o sobie jakbyś to Ty zlecał tego typu projekt zewnętrznej ekipie. Czy chciałbyś w prostym kodzie oglądać ddd, wzorce/techniki o jakich mówi się na konferencjach? One są sexy przy złożonych problemach, ale przy banałach to przez te techniki jedynie wpompujesz złożoność do prostego projektu, tak podkręca się koszty i to jest słabe.

Jeśli projekt jest trywialny to w komercyjnym projekcie dbałym wyłącznie o:

  • poznaj wymagania, spróbuj je zrozumieć, uprościć przed pisaniem kodu
  • pokrycie testami rzeczy do których nie masz pewności (jak zaczniesz pisać testy poczujesz na własnej skórze co należy w kodzie zmienić)
  • utrzymanie względnego porządku, aby nie łamać dobrych zasad bez potrzeby (np. mutowanie argumentów)
  • dokumentowanie zmian w git (drobne komity, dobrze opisane)
  • utrzymywanie spójnej dokumentacji (tak aby nowa osoba mogła szybciej się wdrożyć)
  • ostrożne dokładanie zależności (mniej znaczy więcej)

i jeśli starczy Ci mocy to:

  • przywiąż większą uwagę do obsługi transakcji, bo często są źle obsługiwane
  • doprecyzuj opisy w jira tak, by Ci dwaj juniorzy lepiej się orientowali w kodzie

Chciałbym przygotować ta aplikacje na dalszy jej rozwój, tak by nie tylko urodzić dziecko a jeszcze żeby urosło fajnie, czy jakoś tak.

Zobacz jak zaczniesz dokladać techniki to juniorom skomplikujesz pracę, jeśli i oni będą małpować to co robisz to w niecały rok stworzycie potwora, ktory wszyscy kolejni programiści będą wyklinać. Także jeśli nie ma żadnego konkretnego problemu do rozwiązania, który by jednoznacznie wskazywał, że cqrs jest wyjściem to nie rób tego, zwłaszcza jesli nie znasz negatywnych skutków. Nie ma technik bez wad.

Zwykle jeżeli jesteś najmądrzejszą osobą w pokoju to znaczy, że jesteś w złym pomieszczeniu. Także jeśli chciałbyś podejmować się ciekawszych projektów np. z cqrs to powinieneś pracy szukać w ciekawszych firmach.

2

@pan_krewetek:

Uprzedziłeś mnie, przy czym napisałeś super, o wiele lepiej niż bym marzył.

Chciałem napisać, żeby nie kierować się modą, np nie żenić mikoserwisów "bo się o tym mówi" a nie ma ku temu uzasadnienia.

0

Dzięki za odpowiedź.

@pan_krewetek:

Zwykle jeżeli jesteś najmądrzejszą osobą w pokoju to znaczy, że jesteś w złym pomieszczeniu. Także jeśli chciałbyś podejmować się ciekawszych projektów np. z cqrs to powinieneś pracy szukać w ciekawszych firmach.

Zgadzam się.** Chyba, że** pokój ten umożliwia tobie zyskanie doświadczenia, którego nie zyskałbyś w firmie, gdzie byłoby więcej mądrzejszych. Nie chce się zasiedzieć, ale doświadczyć jak najwięcej się da a pozniej wyjść.

Ogólnie poruszyłeś sedno problemu. Tzn. szukam balansu między bardziej zaawansowanym podejściem do tworzenia apki a biznesem. CQRS jakby nie patrzeć polega na oddzieleniu query od command, pytanie w którym miejscu ten tort można pokroić bezpiecznie.
Czuje, że nie ma uniwersalnej odpowiedzi, wręcz przeciwnie, łatwo przejść w zależyzm.
Zakładam, że aplikacja musi być skalowana, ale projekt również nie bedzie bardzo bardzo złożony, więc chyba zdecyduje się na upośledzony cqrs.

Napisałeś:

utrzymywanie spójnej dokumentacji (tak aby nowa osoba mogła szybciej się wdrożyć)

Dobrze by kod testowy był wystarczający, ale nie myśląc życzeniowo. Wprowadziłem:

Diagram komponentowy obejmuje całą architekturę Czyli co z jakiej bazy gdzie i po co.
Każdy nowy fjuczer jest dopisywany do diagramu funkcjonalnośći wymagań, które są odseparowane na domeny biznesowe, jakoś je lubie, szczególnie gdy mam nową apkę i widze w niej blackboxa, to diagram funkcj wymagan, pozwala bardzo szybko dowiedzieć sie co ona robi.
Do corowych funkcji dopisze diagram sekwencji (sick, nienawidzę go robić).

utrzymanie względnego porządku, aby nie łamać dobrych zasad bez potrzeby (np. mutowanie argumentów)

Chodziło ci o to, że przekazywana referencja ma być wzorem z którego możemy jedynie czerpać, a nie możemy jej zmieniać, ponieważ istnieje prawdopodobieństwo, że jest wykorzystywana przez inne rzeczy?

Dodatkowo zastanawiałem się żeby wrzucić jakiś system metryk, by inrzynierysjko w liczbach widzieć ile dany commit pogorszył. Ktoś zna jakiś system godny polecenia?

1

Zgadzam się. Chyba, że pokój ten umożliwia tobie zyskanie doświadczenia, którego nie zyskałbyś w firmie, gdzie byłoby więcej mądrzejszych. Nie chce się zasiedzieć, ale doświadczyć jak najwięcej się da a pozniej wyjść.

A post wcześniej piszesz:

Chciałbym przygotować ta aplikacje na dalszy jej rozwój, tak by nie tylko urodzić dziecko a jeszcze żeby urosło fajnie, czy jakoś tak.

Nie wiem jak inni forumowicze to widzą, ale projekty w których ludzie składali podobne deklaracje kończyły się śmierdzącą kupą i tak się dzieje, gdy ludzie są zapatrzeni w siebie, a nie cele które są przed nimi stawiane.

Ogólnie poruszyłeś sedno problemu. Tzn. szukam balansu między bardziej zaawansowanym podejściem do tworzenia apki a biznesem. CQRS jakby nie patrzeć polega na oddzieleniu query od command, > pytanie w którym miejscu ten tort można pokroić bezpiecznie.
Czuje, że nie ma uniwersalnej odpowiedzi, wręcz przeciwnie, łatwo przejść w zależyzm.

Jak masz mniej expa to pisz na miarę potrzeb i nie wyprzedzaj przyszłości. Pisanie na miarę potrzeb pozwoli Ci później łatwiej wprowadzić zmiany do problemów o których jeszcze mało wiesz. Jeśli natomiast wyprzedzisz projekt, będziesz próbował projekt uelastycznić tam gdzie Ci się to wydaje to taka improwizacja podkręci tylko złożoność projektu. Później gdy zrozumiesz realne wymagania oraz stawiane przed Tobą problemy to zrozumiesz, że czas się wycofować z tych "inrzynieryjnych" pomysłów. Nie będzie to proste, stąd nastaw się na szukanie nowej rozwojowej pracy albo na nadgodziny, bo jedno z nich niebawem Cię spotka.

0
DKN napisał(a):

Zakładam, że aplikacja musi być skalowana, ale projekt również nie bedzie bardzo bardzo złożony, więc chyba zdecyduje się na upośledzony cqrs.

rysunek

O ile w pełni rozumiem CQRS gdy w obu pionach użyte są odmienne technologie (kolejki, bazy NOSQL, append logi, whatever), i zwraca nam się przez np większą responsywność / wydajność / spłaszczenie szczytów obciążenia systemu - tak tutaj, gdy chodzi o obciążenie tej samej bazy SQL, mi się wydaje wyłącznie komplikacją, tzn zysków nie widzę
Ale zaznaczę się nie znam, nie posadziliśmy nigdy CQRS na produkcji.

7
DKN napisał(a):

Po jego odejściu jestem ja i dwójka młodych świeżo po studiach. Nie mam żadnego doświadczenia w kwestiach wyboru architektury.

No to będziesz robił słabą architekturę. Coś w tym złego?

Wiele programistów oczekuje zbytniej perfekcji, a prawda jest taka, że możesz zrobić tylko taką architekturę, na ile ci pozwala twoje doświadczenie. Więc jeśli jest niewielkie, to architektura będzie słaba. Przy czym trzymanie się zdrowego rozsądku (np. zasad SOLID, enkapsulacji) czy stosowanie norm estetycznych np. "małe pliki, czytelne nazwy, prostota" itp. itd.) pozwala jakoś uniknąć zbytniego burdelu.

Próby pisania kodu "lepiej niż umiemy w danym momencie" kończą się przeinżynierowanymi potworkami czy cargo cultem, gdzie próbujesz wbić się w jakiś schemat (choćby to CQRS), ale nie umiesz tego dobrze zaimplementować, dostosowując do potrzeb projektu, więc nic ci to nie daje, przez perfekcjonizm wtłaczasz tylko kolejne porcje przeinżynierowanego spaghetti do projektu.

Lepiej nabierać powoli doświadczenie, iterować. Im więcej widziałeś/robiłeś projektów, tym lepiej możesz przewidzieć "co pójdzie nie tak" i lepiej zaprojektować architekturę.

Żeby szybciej iterować (=szybciej zdobywać doświadczenie), możesz robić swoje projekty po pracy, wtedy sam sobie wypróbujesz różne podejścia. Możesz też popatrzeć na różne projekty open source i się inspirować albo (co równie ważne) uczyć na błędach (np. jeśli ktoś zakłada issue na GH z prostym feature requestem, a maintainer mu odpowiada, że przy obecnej architekturze projektu to jest niemożliwe do zaimplementowania - to masz już wskazówkę, że coś poszło nie tak).

Ogólnie nie napiszesz dobrego kodu z powietrza, nie mając wcześniejszego doświadczenia.

wyboru architektury.

Architektury się nie "wybiera" tylko ją tworzy i modyfikuje w trakcie rozwoju projektu.

Ew. jeśli jesteś w stanie, to możesz ją zaprojektować już na samym początku, podjąć pewne kluczowe założenia (ale znowu - jeśli masz małe doświadczenie, to kiepsko to zaprojektujesz, więc i tak w trakcie rozwoju projektu będziesz musiał zmienić wiele założeń, bo okaże się, że o wielu rzeczach nie pomyślałeś).

0

... zapytam z ciekawości, z jakich architektur głównie korzystałeś? — DKN 2020-12-18 16:29

z architektury bez specjalnej nazwy 1). Warstwy, dobre odpowiedzialności i nazwy klas. Przemyślane pakiety. Kilka rzemieślniczych reguł.
Warstwa danych (zwał jak zwał: repozytoria, DAO, whatever), usługi, warstwa wyższa kontrolerów/widoków (gdy uprawiam Apache Wicket, jemu najbliżej do MVVM)

Brak CQRS nie jest brakiem architektury ... im więcej myślę, wg mnie CQRS nie jest nawet architekturą, a pewnego rodzaju (implementacyjnym?) wzorcem inżynierskim.
Tzn można mieć CQRS a projekt mieć w stanie zwyczajowo łączonym z opisem "bez architektury" czyli wolnoamerykanka, burdel.

  1. urobiłem to zdanie na kolanie, trochę nawiązując do "chrześcijan bez osobliwego wyznania"
1

Z racji małego doświadczenia możesz nie do końca rozumieć uzasadnienie niektórych konceptów, niemniej polecam Ci na szybko ogarnąć książeczki Clean Architecture i Fundamentals of Software Architecture.

CQRS na całą aplikację to prawie na pewno przeinżynierowanie i proszenie się o problemy - podziel całość domenowo i zaaplikuj CQRS tam, gdzie to ma sens - na przykład ktoś inny wprowadza dane, a ktoś inny je czyta (sprzedający i kupujący, piszący i czytający, adminki i wyszukiwarki etc). Kierowałbym się kwestiami skalowalności i spójności danych (ewentualna konsystencja*). Realizacja techniczna może być na różnym poziomie złożoności - od jednej tabelki po dedykowane usługi.

(*) - tak to żart, chodzi o eventual consistency ;)

2

Zacząłbym naprawdę od najprostszych rzeczy (listuję od mniej do bardziej skomplikowanych):

  1. Najpierw rozdzielasz funkcjonalności (np. funkcje) na pliki, czyli jak mowa o bazie danych to pliki z nazwami (przykładowo) insert, select itp - by coś odwzierciedlały
    Skupiasz się na tym, by w pliku insert, który przykładowo odpowiada za logikę wpisu do bazy danych, nie fruwały jakieś funkcjonalności pasujące do select
    Na tym etapie skupiasz się, aby działało.

  2. Jak pliki się zbytnio namnażają, to wtedy stosujesz katalogi grupujące te pliki z funkcjami

  3. Tworząc funkcje, staraj się tworzyć proste funkcje, które przyjmują i zwracają absolutnie generyczne typy danych w Twoim języku programowania.
    Przykład: funkcja zwracająca wyniki zapytania, niech zwraca "generyczny" typ danych w Twoim języku (z wynikami zapytania), a nie jakiś skomplikowany kursor/uchwyt czy coś do drivera bazy, który gdzieś tam później migruje między warstwami i niemal każda funkcja może się go potem "spodziewać", czyli musi wiedzieć coś o jakimś skomplikowanym typie danych "z zewnątrz". Później ten skomplikowany typ danych będzie sprawiał problemy w testach (nierzadko potrzeba czegoś ekstra "na zewnątrz", by działał)

  4. Tworząc specyficzną funkcję (nie taką, która jest "współdzielona" w obrębie całej apki) sprawdzaj w dobrym IDE, czy jak testowo taką funkcję usuniesz/zakomentujesz, to zaświeci Ci się na czerwono tylko miejsce jej użycia (optymalnie) lub zaświeci się również w innych funkcjach (zazwyczaj źle).
    Przykład: masz funkcję, która zapisuje dane do pliku. Gdy ją usuniesz IDE powinno Ci wskazać błąd w tych ogólnych miejscach, w który była ona wołana do zapisu, jak Ci wyskakują odwołania w funkcjach typu odczytDanych, to znaczy, że w poprzednich punktach coś pomieszano.

    Kod, który pozwala w miarę bezboleśnie usuwać swoje kawałki/funkcje, jest już raczej dobrze rozplanowany.

  5. Jak funkcje mające podobny zakres odpowiedzialności lub "dziedzinę", również ostro namnażają w katalogach - rozważ stosowanie interfejsów czy tam klas (lub ekwiwalentu w danym języku programowania), aby je podzielić pod tym względem.

Jak jesteś na etapie po punkcie 5) - winszuje gratulacje, świat stoi otworem, masz teraz znacznie ułatwione zadanie. Teraz dopiero możesz się zastanowić czy potrzebujesz DDD, CQRSów, ESów, mikroserwisów czy innych konceptów, bo dopiero teraz będzie widać, czy ich zastosowanie ma jakiś sens w Twojej aplikacji. Jeśli zdecydujesz się pójść którąś z takich dróg, wówczas odpowiednio przestrzegając punktów 1-5 będzie Ci znacznie łatwiej.

6

@DKN:
Polecam również prezentacje

Dodatkowo zalecam stosowanie niemutowalnych obiektów i zastąpienie java.util.collection Vavrem

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