Wątek przeniesiony 2021-09-03 20:20 z Java przez furious programming.

Java, mikroserwisy, DRY

0

W ramach jednego mikroserwisu powinniśmy unikać duplikacji kodu. Ale w ramach kilku, nie powinniśmy tworzyć współdzielonych bibliotek między kilkoma mikroserwisami.

Czyli co:

  • powinniśmy wtedy duplikować kod
  • Zastanowić się czy te mikroserwisy nie powinny być jednym
  • czy jeszcze jakoś inaczej?
2

Tak na sucho, bez konkretnego kontekstu, objaśnienia domeny imho można napisać tylko to zależy.
Duplikacja kodu to nie jest największe zło świata jeżeli ma ona swoje powody ale też to czy n mikroserwisów nie powinno być jednym to już kwestia której nie da się rozstrzygnąć (tak jak ogólnie wyznaczania granic między mikroserwisami) bez znajomości domeny.

2
konradino898 napisał(a):

nie powinniśmy tworzyć współdzielonych bibliotek między kilkoma mikroserwisami.

O! A dlaczego? Prawie zawsze miałem współdzielone biblioteki między mikroserwisami. Albo z jakimiś utilsami, albo firmową nakładkę na generyczny framework

6

Sprawa jest prosta.
Oczywiście można, warto, a nawet należy współdzielić kod miedzy mikroserwisami.
Jakkolwiek powinno być to tak zrobione, aby działanie systemu nie było uzależnione od tego czy kod jest współdzielony. (Czyli, że w każdej chwili ktoś w jakimś serwisie może powiedzieć - mam gdzieś wasz kod, przepisuje wszystko na COBOLa i nie powinno to być problemem, jak długo nowy kod jest kompatybilny z zadanym API).
Samo API - dość częsty punkt programu - można publikować np. jako OpenAPI, proto itp. - w tym sensie, że współdzielimy specyfikację i każdy serwis - klient sobie na podstawie tego api generuje stuby do swojej technologii. (IMO przeważnie lepsze niż popularne wrzucanie client - jara, które nie dość, że narzuca javę, to jeszcze często konkretną technologię, zwykle zrypaną).

7

Ale w ramach kilku, nie powinniśmy tworzyć współdzielonych bibliotek między kilkoma mikroserwisami.

Całkowita nieprawda. Jeśli w każdym mikroserwisie masz validacje PESEL i NIP to powinieneś zrobic bibliotekę i pobierać ja przez jakieś lokalne repo Mavena.

0
jarekr000000 napisał(a):

Jakkolwiek powinno być to tak zrobione, aby działanie systemu nie było uzależnione od tego czy kod jest współdzielony. (Czyli, że w każdej chwili ktoś w jakimś serwisie może powiedzieć - mam gdzieś wasz kod, przepisuje wszystko na COBOLa i nie powinno to być problemem, jak długo nowy kod jest kompatybilny z zadanym API).

Czy to w praktyce nie ogranicza współdzielenia do jakichś prostych przypadków? Niby spoko jechać tym samym autobusem (współdzielony kod), ale do czasu, aż ktoś nie puści bąka (buga). Nie wszyscy oberwą, ale Ci na których trafi nie będą szczęśliwi.

3
yarel napisał(a):
jarekr000000 napisał(a):

Jakkolwiek powinno być to tak zrobione, aby działanie systemu nie było uzależnione od tego czy kod jest współdzielony. (Czyli, że w każdej chwili ktoś w jakimś serwisie może powiedzieć - mam gdzieś wasz kod, przepisuje wszystko na COBOLa i nie powinno to być problemem, jak długo nowy kod jest kompatybilny z zadanym API).

Czy to w praktyce nie ogranicza współdzielenia do jakichś prostych przypadków? Niby spoko jechać tym samym autobusem (współdzielony kod), ale do czasu, aż ktoś nie puści bąka (buga). Nie wszyscy oberwą, ale Ci na których trafi nie będą szczęśliwi.

To chyba najdziwniejsze uzasadnienie przeciwko wspólnemu kodowi jakie słyszałem. Przecież nikt nie używa tego kodu wspólnego dla własnego widzimisi tylko dlatego że i takjest potrzebny.
Załóżmy scenkę rodzajową, masz 10 mikroserwisów potrzebujących tej samej logiki, np walidacji:

  • Jeśli masz wspólny kod jest on napisany raz i 10 razy przetestowany w 10 mikroserwisach
  • Jeśli nie masz wspólnego kodu w każdym mikroserwisie masz osobną implementację przetestowaną tylko raz

Który przypadek jest bezpieczniejszy?
Który przypadek będzie łatwiej łatać i utrzymywać?
A jak masz 100 mikroserwisów?

2

@yarel:

Czy to w praktyce nie ogranicza współdzielenia do jakichś prostych przypadków? Niby spoko jechać tym samym autobusem (współdzielony kod), ale do czasu, aż ktoś nie puści bąka (buga). Nie wszyscy oberwą, ale Ci na których trafi nie będą szczęśliwi.

Taka historia: w moim pierwszym januszsofcie miałem ostrą scysje z programistami. Odkryłem, że wszystkie systemy sa zrobione tak, że jest dziesiątki plików JSP (skrypty javy), każdy zaczyna się od kilku tysięcy prawie tych samych skopiowanych funkcji bazowych (np, połączenie do bazy itp.). W ramach jednego systemu, wielokrotnie copy pastowany kod.
Zaproponowałem, żeby to wydzielić chociaż do jednego includowanego jsp i włączać do wszystkich plików. No i argument mamutów był nie do odparcia - przecież jak się ktoś pomyli w tym bazowym skrypcie to wszystkie strony serwisu nie będą działać... a przy copy-paste tylko kilka. Koniec bajki pierwszej.

W microserwisach jest problem raczej mentalny - ktoś kiedyś stwierdza, że to fajnie by wydzielić jakiś powtarzany kod - np. security, bo więcej serwisów z tego korzysta. OK.
Potem są jakieś zmiany w security, ale że jest jeden moduł, z którego wszyscy korzystają to nie ma problemu, są tam zmiany wrzucane i jest git.
Pewnego pięknego dnia chcesz coś zrobić w innej technologii i nagle się okazuje, że nikt już w zasadzie nie wie jak to zrobić, bo kluczowe elementy związane z security mają totalnie nieaktualną dokumentację. Przecież wszystko jest w jarze. Jar zmusza cię do korzystania z javy, springa i to jeszcze konkretnej wersji... koniec bajki drugiej.

0

Odpowiedzi w sumie tyle ile osób :P

Ja zgadzam się z tą teza zacytowana z książki w temacie. Jakiekolwiek współdzielenie powoduje wzrost zależności, sprzężenia, spada spójność.

Ale wiemy, że w życiu nigdy nie jest tak jak w idealnym świecie.

Według @jarekr000000 , jeśli dobrze Cię zrozumiałem, w mikroserwisach, pomiędzy nimi, możemy współdzielić drobe utilsy, czy jakiś kod wygenerowany na podstawie wsdla klienta itp. Coś co nie ma stanu i generalnie nie robi nic istotnego, popraw mnie proszę jak się mylę:)

Co do jakiś serwisów walidujacych PESEL itp. To raczej nie powinniśmy tego współdzielić, co nie znaczy że wiele osób nie robi tego nagminnie

0
jarekr000000 napisał(a):

W microserwisach jest problem raczej mentalny - ktoś kiedyś stwierdza, że to fajnie by wydzielić jakiś powtarzany kod - np. security, bo więcej serwisów z tego korzysta. OK.
Potem są jakieś zmiany w security, ale że jest jeden moduł, z którego wszyscy korzystają to nie ma problemu, są tam zmiany wrzucane i jest git.
Pewnego pięknego dnia chcesz coś zrobić w innej technologii i nagle się okazuje, że nikt już w zasadzie nie wie jak to zrobić, bo kluczowe elementy związane z security mają totalnie nieaktualną dokumentację. Przecież wszystko jest w jarze. Jar zmusza cię do korzystania z javy, springa i to jeszcze konkretnej wersji... koniec bajki drugiej.

Bajka pierwsza brzmi trochę jak niemiecki sen :-)

Co do kodu typu security, logowanie, connection poole, czy strzelanie po różnych technicznych interfejsach, to chyba nikt takiego kodu w dzisiejszych czasach nie klepie od 0, tylko korzysta z gotowców (biblioteki / skonteneryzowane zestawy klocków). Co do funkcjonalności wspólnej, to można wydzielić do osobnego mikroserwisu.

Może tę pozostałą funkcjonalność nie każdy chce wydzielać do osobnego mikroserwisu? Ale jak ma 100 mikroserwisów, to czemu miałby nie chcieć?

1

Mikroserwisy a mikroserwisy to 2 różne rzeczy, niektórzy robią nanoserwisy, inni grube mikroserwisy. U jednych jest monorepo u innych nie. Każda firma zdaje się mieć własną interpretacje wzorca.

Nie widzę problemów żeby robić małe biblioteczki i współdzielić je między mikroserwisami. Do tego jest też coś co nazywa się chassis (https://microservices.io/patterns/microservice-chassis.html), czyli taki wspólny zestaw bibliotek i kodu dla każdej z usług.

Problem zaczyna się wtedy gdybyś logikę biznesową zaczął dystrybuować pomiędzy serwisami jako bibliotekę. Bo wtedy widać że masz źle podzielone microserwisy (nie według bounded-contextów).

0

Są przynajmniej 4 drogi:

  1. Duplikacja kodu - niestety w praktyce naturalnie się to dzieje w momencie kiedy jest >1 zespół.
  2. Współdzielenie libek - piekło w momencie, kiedy chcemy wszędzie podbić wersje - długi czas adopcji. Jeśli mamy 3 serwisy na krzyż kontrolowane przez jeden zespół, to nie jest to problem.
  3. Wspólny kod w dedykowanym serwisie - możliwość autonomicznej zmiany, ale płacimy koszt wywołania sieciowego itd.
  4. Wspólny „framework”, np. starter Spring Bootowy (czyli gotowe autokonfiguracje) zawierający tylko niezbędne infrastrukturalne rzeczy.

Więcej w temacie mówił Piotrek Betkier

4

Co do jakiś serwisów walidujacych PESEL itp. To raczej nie powinniśmy tego współdzielić, co nie znaczy że wiele osób nie robi tego nagminnie

Czyli nie bo nie?
Rozumiem że Guave czy Apache Commons też przepisujesz żeby nie było wspólnych zależności?

0

To zależy co się duplikuje. Ma nie być jednym. Ta najlepiej narypać 1-klasowego singletona dla api , który robi wszystko.

2

Co do kodu typu security, logowanie, connection poole, czy strzelanie po różnych technicznych interfejsach, to chyba nikt takiego kodu w dzisiejszych czasach nie klepie od 0, tylko korzysta z gotowców (biblioteki / skonteneryzowane zestawy klocków).

Mam takie wrażenie, że nigdy się nie musiałem tyle kodu security naklepać co jak korzystałem z gotowców Springa. Kodu dużo, magii dużo, frustracji jeszcze więcej - gdy to samo (Oauth + JWT), bez springa ktorzystając z bibliotek to http/json robi się od strzału. (Ale prawdą jest też, że miałem przeważnie wyrafinowane wymagania).

1

Nie mam problemu z współdzieleniem bibliotek pomiędzy mikroserwisami pod warunkiem że są to biblioteki a nie jakieś przypadkowe kawałki kodu. W sensie mają jasną odpowiedzialność, jasno określone API, które ma szansę pozostać niezmienione. Niedopuszczalne dla mnie jest, jeżeli 2 serwisy (A, B) współdzielą jakąś bibliotekę, w serwisie A wymagana jest zmiana, więc trzeba zmienić bibliotekę, a to wymusza zmianę w serwisie B. Najczęściej dotyczy to komunikacji, więc tak jak już powiedziano wyżej - lepiej mieć jakąś definicję OpenAPI.

Jeżeli masz funkcjonalność, która robi np. tę walidację numeru PESEL, to może być wykorzystana zarówno w twoim, jak i w innych projektach. Co jest złego w wyciągnięciu tego do zewnętrznej biblioteki? Jak masz jakiś kawałek do sprawdzania wewnętrznych tokenów JWT, to też nie mam nic przeciwko zrobieniu biblioteki. Gorzej jak wyciągniesz jako zewnętrzną bibliotekę np. kawałek repozytorium.

2

Gorzej jak wyciągniesz jako zewnętrzną bibliotekę np. kawałek repozytorium.

No tak.

Niedopuszczalne dla mnie jest, jeżeli 2 serwisy (A, B) współdzielą jakąś bibliotekę, w serwisie A wymagana jest zmiana, więc trzeba zmienić bibliotekę, a to wymusza zmianę w serwisie B.

Podaje hasło: wersjonowanie. Zazwyczaj są to takie 3 liczby oddzielone kropkami np. 1.98.5 (#PDK)

0

@ProgScibi: Wiem co to wersjonowanie. Przykład nieco z czapy - załóżmy, że masz własny format przesyłania danych pomiędzy serwisami bo z jakiegoś powodu xml, json, protobuff i coś tam jeszcze ci nie pasują. Robisz sobie bibliotekę do mapowania na takie obiekty i z powrotem. Teraz jeżeli masz 2 serwisy, które się ze sobą komunikują w ten sposób i korzystają z tej samej biblioteki. Zmieniasz bibliotekę pod wpływem potrzeb A, ale wtedy wychodzi, że trzeba zmienić też B, bo przestały się ze sobą dogadywać. Oczywiście jeżeli masz gdzieś opis protokołu i się go trzymasz to nie ma problemu.

0
jarekr000000 napisał(a):

Mam takie wrażenie, że nigdy się nie musiałem tyle kodu security naklepać co jak korzystałem z gotowców Springa. Kodu dużo, magii dużo, frustracji jeszcze więcej - gdy to samo (Oauth + JWT), bez springa ktorzystając z bibliotek to http/json robi się od strzału. (Ale prawdą jest też, że miałem przeważnie wyrafinowane wymagania).

Dodatkowo warto dorzucić, że z frameworków do security korzysta się wtedy, gdy nie istnieje odpowiednia infrastruktura, tj. gdy nie masz oddzielnie postawionej bramki filtrującej requesty, ani powydzielanych stref dla mikroserwisów. Wtedy faktycznie żonglowanie filtrami i sprawdzanie, czy request jest dozwolony czy tylko za taki chce uchodzić to katorga.

0

Nawet jeżeli masz taką bramkę, to sprawdzenie roli użytkownika może być na każdym z serwisów. Uwierzytelnianie możesz mieć na bramce, możesz też przekształcić jakieś tam zewnętrzne tożsamości we własne, dopisać do nich role, ale skąd bramka ma wiedzieć, że Kowalski ma mieć dostęp konkretnej encji zarządzanej przez jakiś schowany mikroserwis?

Pytanie tylko - gdzie leży wiedza o tym, kto do jakiej encji ma dostęp? Jeśli jest ona zaszyta w bazie danych aplikacji to nie jest to problem RESTowy - po prostu usługa zwraca 403 gdy człowiek o danym ID próbuje wykonać operację na wybranej encji. Jeśli jest to jakoś skonfigurowane po stronie RESTa to nie widzę powodu, dlaczego bramka miałaby tego nie pilnować

Uslugi powinny być niezależne i nie wiedzieć o sobie zbyt wiele poza API. Bramka potwierdza tożsamość użytkownika, dopisuje do niego role i przekierowuje żądanie dalej. Usługa do której należy wykonanie konkretnego zadania sprawdza, czy dana rola ma dostęp do jakiejś operacji i faktycznie może zwrócić 403 jeżeli brakuje uprawnień. Gateway nie powinien moim zdaniem wiedzieć, że użytkownik ma dostęp do konkretnej encji. Wspólna baza danych dla mikroserwisów to herezja.

0

Biblioteki to technikalia, szerokopojęte rzeczy związane z infrastrukturą.
Kod, który jest napisany raz, przetestowany i raczej nie powinien się zmieniać.
Zdecydowanie nie powinien zawierać modelu domenowego czy też jakichś reguł biznesowych,
które mogą się zmieniać - ewoluować w miarę dokładania kolejnych funkcjonalności - to powinno być w mikroserwisie X, który enkapsuluje te reguły.

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