Modyfikatory dostepu i moduly- az tak zle w Javie?

2

Witam,

Na wstepnie zaznacze ze z Java i JVM mialem doczynienia na uczelni gdy uczylem sie podstaw programowania a nastepnie liznalem troche Scali w wolnym czasie. Ogladalem wlasnie prezetancje z WJUG'a pod tytulem Modularity and hexagonal architecture in real life: Jakub Nabrdalik i jestem w szoku ze przez pierwsze prawie 20 minut autor wychwala sens pakowania wszystkiego i wystawiania jedynie publicznych "interfejsow" (nie w sensie doslownym). Czy w Javie i w ogole ekosystemie JVM jest z tym az tak zle ze trzeba uswiadamiac i zachecac ludzi? Jesli tak to jak to wyglada na codzien- ladujecie wszystko do jednego worka i wystawiacie jako publiczne?

Zawsze wydawalo mi sie ze rzeczy takie jak projekty i namespace'y i ich nie-dotnetowe odpowiedniki to standard we wspolczesnych jezykach. Mylilem sie?

Z gory dzieki i zachecam do dyskusji :)

3

Hmmm, z mojego doświadczenia wynika, że przeciętny programista Javy "zna" tylko public i private. Oczywiście private jest używane dla pól, które mają publiczne gettery i settery. Ku chwale enkapsulacji.

Jeżeli chodzi o ekosystem, to nie. Jest multum dobrych bibliotek, które dbają o enkapsulację i robią to z sensem.

package private to też takie trochę oszukańcze bezpieczeństwo, bo jak zechcę to i bez magii refleksji dobiję się do klas. Dopiero Java 9 wprowdziła moduły, których i tak pewnie 99% projektów nie zaadapotowała. W Javie brakuje moim zdaniem przede wszystkim modifykatora internal.

0

C# nie ma package private, więc nie da się użyć paczek jako kontroli dostępu. W Javie package private jest używane głównie przez biblioteki czy frameworki.

Java 9 wprowadziła moduły, które są alternatywą dla modyfikatora dostępu internal, ale ma krótki okres wsparcia. Podobnie Java 10 - można to zobaczyć np na https://en.wikipedia.org/wiki/Java_version_history . Dopiero Java 11 będzie LTSem, czyli wersją o przedłużonym wsparciu i prawdopodobnie po wyjściu Javy 11 programiści Javy się na nią liczniej przeniosą i zaczną wykorzystywać jej funkcjonalności.

Modne są teraz mikroserwisy wystawiające końcówki RESTowe. W takiej sytuacji publiczne API mikroserwisu to jedynie ścieżki RESTowe oraz DTOsy. To dość mocna hermetyzacja, zwłaszcza biorąc pod uwagę to, że nie da się użyć refleksji do dostania się do nieupublicznionych funkcjonalności mikroserwisu (refleksja nie działa po RESTu na zdalne procesy :] ).

0
Wibowit napisał(a):

C# nie ma package private, więc nie da się użyć paczek jako kontroli dostępu(...)

Mozesz rozwinac o co Ci chodzi? Szczerze mowiac nie rozumiem do czego sie odnosisz. Ja mowiac o paczkach, modulach itp. mam na mysli jakikolwiek mechanizm- czyli np. wlasnie projekty/biblioteki w .Net. Nie pytam czy C# ma paczki (logicznie mozna powiedziec ma, ale nie technicznie).

Modne są teraz mikroserwisy wystawiające końcówki RESTowe. W takiej sytuacji publiczne API mikroserwisu to jedynie ścieżki RESTowe oraz DTOsy. To dość mocna hermetyzacja, zwłaszcza biorąc pod uwagę to, że nie da się użyć refleksji do dostania się do nieupublicznionych funkcjonalności mikroserwisu (refleksja nie działa po RESTu na zdalne procesy :] ).

No tak ale pytam raczej o typowe aplikacje monolitowe, w tym pozytywnym znaczeniu tego slowa- modularne i/lub odpowiednio powarstwowane. Przeciez mikroserwisow w Javie nie robi sie po to zeby osiagnac to co w .Net osiaga sie projektami :)

A jeszcze dodam- refleksja rowniez za bardzo nie ma tutaj znaczenia gdyz pytam glownie o ograniczanie dostepu w kodzie jednej aplikacji (ktory moze byc rozbity po kilku repozytoriach). A wiec kod do ktorego jeden programista moze dowolnie zajrzec i gdyby bardzo chcial to moglby zmienic akcesory.

2
Aventus napisał(a):

Czy w Javie i w ogole ekosystemie JVM jest z tym az tak zle ze trzeba uswiadamiac i zachecac ludzi? Jesli tak to jak to wyglada na codzien- ladujecie wszystko do jednego worka i wystawiacie jako publiczne?

Niestety tak. Tzn, nie my, tylko cała reszta tych złych i niedouczonych programistów :) Generalnie problem wynika głównie stąd, że jeżeli czytasz ogłoszenia o javę to większość z nich (przynajmniej kiedys, chociaz podejrzewam że aż tak wiele się nie zmieniło) zawiera słowa kluczowe spring i hibernate. Teraz wyobraź sobie, że jesteś studentem więc chcesz trafić w zapotrzebowanie na rynku, bo przeciez sprawdziłeś czego wymagają i tego sie uczysz. IMHO często dużo ludzi woli uczyć się Springa niż programowania jako tako. Szukasz tutoriali i w tutorialu dotyczącym np. wykorzystaniu frameworku spring mvc masz wszystko publiczne i w kilku pakietach bez górnolotnej architektury. No i wtedy widzisz, że ktoś tak napisał, więc też tak piszesz i stąd tak jak kolega pisał wyżej, public, private i protected. Można pisać w kotlinie, który ma internal, ale chcąc budowac moduły w jednym projekcie nie ogranicza on dostepu do klas pomiędzy modułami, co wg. mnie nie jest idealnym rozwiązaniem, bo na poziomie kompilacji nie wychwycisz, że ktoś wykorzystał klasę z modułu A w module B, która nie była publiczna. Do tego w kotlinie domyslnym modyfikatorem dostępu jest public, tak samo jak w groovym i o ile w groovym jestem w stanie to zrozumieć, bo to język rzadko stosowany do logiki biznesowej.

A tak à propos domyslnym modyfikatorem dostepu w intellij i eclipsie też jest public ;) dlatego nie dziwie się, że ktoś chce otworzyc oczy co po niektórym, że da się inaczej

2

Oglądałem tą prezentację, i od tamtego momentu usunąłem public z defaultowego template tworzenia klasy w IntelliJ. Nabrdalik miał rację - wtedy faktycznie trzeba pomyśleć, kiedy dać klasę publiczną (zazwyczaj fasada i dtosy). Jak dla mnie package private jest dobrym defaultowym modyfikatorem dostępu - gdyby jeszcze programiści o tym pamiętali, i nie dawali wszędzie "na pałę" public...

0

@Aventus: czy w C# robisz dla każdego aggregate roota osobny projekt? Bo chyba tak by to trzeba było robić, żeby odtworzyć pokazane przez Jakuba rozwiązanie z Javy. Nigdy się z tym nie spotkałem i wydaje mi się to dziwne :) Ewentualnie można ukrywać wszystko w zagnieżdżonych klasach w fasadzie.

0

Mozesz rozwinac o co Ci chodzi? Szczerze mowiac nie rozumiem do czego sie odnosisz. Ja mowiac o paczkach, modulach itp. mam na mysli jakikolwiek mechanizm- czyli np. wlasnie projekty/biblioteki w .Net. Nie pytam czy C# ma paczki (logicznie mozna powiedziec ma, ale nie technicznie).

Ale napisałeś, że jesteś zdziwiony że widziałeś gościa który wychwala konkretnie package private, a nie żaden inny mechanizm. Dlatego wspomniałem iż C# package private nie posiada, więc wprost się sytuacji nie porówna. Organizowanie kodu za pomocą paczek to zupełnie co innego niż organizowanie kodu za pomocą modułów/ projektów/ solucji (WTF?)/ zwał jak zwał.

No tak ale pytam raczej o typowe aplikacje monolitowe, w tym pozytywnym znaczeniu tego slowa- modularne i/lub odpowiednio powarstwowane. Przeciez mikroserwisow w Javie nie robi sie po to zeby osiagnac to co w .Net osiaga sie projektami :)

Słusznie. Mikroserwisy różnią się od monolitów wieloma rzeczami, nie tylko dostępem do klas. Mikroserwisy można na przykład stawiać na różnych maszynach, ale nie o tym mowa w tym wątku. Widoczność klas między modułami można ustalić za pomocą wprowadzonego w Javie 9 mechanizmu modułów (Jigsaw), jak tu już zresztą zostało ze dwa razy napisane. Jigsaw nie służy tylko do określania widoczności klas, ale także np do zarządzania zależnościami. W module-info.java możesz określić co dany moduł eksportuje, których modułów wymaga, do czego pozwala na dostęp refleksją, jakich usług dostarcza, itd https://www.oracle.com/corporate/features/understanding-java-9-modules.html

Wracając jednak do mikroserwisów moim zdaniem jednak sprawdzają się znacznie lepiej niż hierarchie modułów. Mikroserwisy mają to do siebie, że nie ma w nich hierarchii, każdy mikroserwis może gadać i zależeć od dowolnego innego. Nie ma problemu z tym, by serwis A wołał serwis B kiedy trzeba i jednocześnie serwis B wołał serwis A kiedy mu potrzeba. Dowolny graf zależności jest prosty do zaimplementowania. Jednak mikroserwisy i tak wymuszają sporą dozę dyscypliny, bo wystawianie funkcjonalności RESTem wymaga pewnego planu i jest zdecydowanie bardziej czasochłonne niż przestawienie modyfikatora dostępu z protected/ package private/ internal/ etc na np public.

W przypadku hierarchii modułów też się da zrobić tak, by moduł Parent wołał moduł Child kiedy chce (moduł Child zależy od modułu Parent), ale trzeba się nieco nagimnastykować. Moduł Parent definiuje ChildInterface (zawierający to co Parent chce od Childa), moduł Child definiuje klasę ChildImplementation rozszerzającą ChildInterface i podrzuca instancję modułowi Parent. Trochę gimnastyki, ale pozwala to na wołanie zarówno Childa z poziomu Parenta jak i naturalnie Parenta z poziomu Childa.

1
Wibowit napisał(a):

Ale napisałeś, że jesteś zdziwiony że widziałeś gościa który wychwala konkretnie package private, a nie żaden inny mechanizm. Dlatego wspomniałem iż C# package private nie posiada, więc wprost się sytuacji nie porówna. Organizowanie kodu za pomocą paczek to zupełnie co innego niż organizowanie kodu za pomocą modułów/ projektów/ solucji (WTF?)/ zwał jak zwał.

Tak jak napisalem wyzej, chodzilo mi o ogolne "pakowanie" funkcjonalnosci i wystawianie publicznego API. Chyba najbardziej uniwersalnym sformulowaniem bedzie modul.

Wracając jednak do mikroserwisów moim zdaniem jednak sprawdzają się znacznie lepiej niż hierarchie modułów(...)

Musze sie tutaj nie zgodzic- rozumiem do czego zmierzasz, ale w ogole nie mieszalbym mikroserwisow do dyskusji. To ze mikroserwisy w naturalny sposob zapewniaja ograniczony dostep to tylko i wylaczanie skutek, nie przyczyna uzycia takiej architektury. Ktos czytajacy co piszesz moglby pomyslec ze jesli chce lepiej porozdzielac odpowiedzialnosci w swojej aplikacji to "wystarczy" walnac system oparty o mikroserwisy. To jak mikroserwisy w skutek swojej architektury zapewniaja ograniczony dostep mozna osiagnac bez nich uzywajac wlasnie modulow i jasno defniujac puliczne API w tychze. Zreszta np. Martin Folwer jest zwolennikiem podejscia monolith-first o czym zapewne wiesz. Jesli ktos nie potrafi dobrze zaprojektowac monolitu to tym bardziej nie poradzi sobie z mikroserwisami.

Kwestie konfliktow zaleznosci (np. NuGet w .Net) mozna rozwiazac na kilka sposobow. A jesli zaleznosci jest tak wiele ze rozwiazywanie konfliktow staje sie koszmarem to jest to jasny znak ze mamy problem z architektura ogolnie, bo mamy spaghetti na najwyzszym poziomie architektonicznym. A jesli z jakiegos powodu system musi byc oparty o tyle zaleznosci to powinno sie pomyslec nad zmiana architektury (komunikacja oparta o Message Bus i/lub mikroserwisy) jednak bynajmniej nie z tylko z powodu zaleznosci (czyli referencji) per se a z tego ze na takim etapie ciezko mi uwierzyc ze system nie ma innych problemow (skalowanie wertykalne itp.).

0

No i moduły w obrębie jednego projektu są na tym samym poziomie tj. nie ma relacji parent-child. Mając dobrze zmodularyzowany monolit nie ma większego problemu żeby przejść na architekturę mikroserwisów czy też po prostu podzielić monolit na dwa inne. Tak samo moduły w javie 9 nie udostępniają jawnie hierarchizowania modułów.

0

ZTCW to nie da się zrobić cyklicznej zależności modułów w Mavenie, np: https://stackoverflow.com/q/16468525 Nie da się też tego zrobić w Scalowym SBT.

Mikroserwis składa się z dwóch modułów: API i implementacji. Mikroserwis A może zależeć od API mikroserwisu B, a mikroserwis B może zależeć od API mikroserwisu A i nie ma tutaj cyklu. Tą architekturę można przenieść na moduły monolitu. Wtedy np moduł-A-API jest używany przez każdego chętnego, a moduł-A-impl jest używany tylko przez moduł spinający aplikację w całość. Taka płaska hierarchia modułów byłaby dla mnie zdecydowanie bardziej przekonująca niż typowa hierarchia modułów, gdzie nie ma osobnych modułów z publicznym API.

0
dargenn napisał(a):

Oglądałem tą prezentację, i od tamtego momentu usunąłem public z defaultowego template tworzenia klasy w IntelliJ. Nabrdalik miał rację - wtedy faktycznie trzeba pomyśleć, kiedy dać klasę publiczną (zazwyczaj fasada i dtosy). Jak dla mnie package private jest dobrym defaultowym modyfikatorem dostępu - gdyby jeszcze programiści o tym pamiętali, i nie dawali wszędzie "na pałę" public...

No ja wolę jak pisze się na pałę public niż pisanie na pałę private.
Programowanie obiektowe to też tworzenie złożonych zachowań poprzez kompozycję prostych klas. Pisanie wszędzie private to uniemożliwienie tworzenia nowych zachowań w oparciu o istniejące klasy. Pozostaje tylko złożona logika podzielona na wiele plików.
Zdaję sobie sprawę, że są klasy, fragmenty kodu, które nie mają sensu w innym kontekście niż niż użyte i jak najbardziej powinny być private.

1

Pakiety można traktować jako takie Duże obiekty czyli coś jak obiekt tylko na wyższym poziomie abstrakcji. Wtedy jak ktoś chce ktoś wykorzystać z modułu to wchodzi tylko w publiczne klasy i nie musi przeglądać całości. (Jako moduł można tu traktować jakąś autonomiczną część aplikacji, może mieć swoje endpointy, swoją baze danych itp)

0
Wibowit napisał(a):

Mikroserwis składa się z dwóch modułów: API i implementacji. Mikroserwis A może zależeć od API mikroserwisu B, a mikroserwis B może zależeć od API mikroserwisu A i nie ma tutaj cyklu.

Możesz wytłumaczyć? Nie bardzo rozumiem, bo jeżeli mikroserwis A zależy od mikroserwisu B i na odwrót to każdym razem gdy robisz zmianę w jednym z nich (mam na myśli zmiany łamiące obecny kontrakt) to musisz też zrobić zmiany w drugim. To brzmi właśnie jak cykl, bo gdy będziesz musiał coś zmienić w jednym, to musisz zmienić w drugim i wtedy musisz też równolegle je wdrażać albo wprowadzić zmianę w jednym z nich w taki sposób, żeby drugi mógł nadal działać czyli wydłużyć ogólny czas, który potrzebujesz na daną funkcjonalność.

2

Jeśli zrobię zmianę łamiącą kontrakt to tak czy siak muszę zmienić kod po obu stronach - zarówno ten wystawiający API według kontraktu jak i ten korzystający z API. Niespecjalnie widzę jak cykl ma tu coś zmieniać.

Może rozpiszę co mam na myśli pisząc o typowej dla monolitów hierarchii modułów i hierarchii typowej dla mikroserwisów. Najpierw trzeba jeszcze podać założenie, że nie może być wprost cykli w zależnościach, tzn jeśli moduł A zależy od B, to moduł B nie może zależeć od A.

W hierarchii monolitowej nie ma rozdziału na moduł z API i moduł implementujący to API. Stąd nie da się zrobić cykli w ogóle. Ponadto hierarchia często jest dość głęboka. Dla przykładu możemy mieć:

  • moduł common niezależący od niczego
  • moduł core zależący od modułu common
  • moduł orders zależący od modułu core
  • moduł trading zależący od modułu orders
  • itd

Zależności są przechodnie, więc moduł trading tak naprawdę zależy nie tylko od modułu orders ale także common i core, więc ma dostęp do klas z tych modułów. Taka architektura skutkuje pchaniem funkcjonalności do modułów najwyżej w hierarchii, tam gdzie będą najszerzej dostępne (to ułatwia sprawy na krótką metę, tzn jest to chodzenie po linii najmniejszego oporu). Stąd w monolicie będą dominować moduły common i core.

W hierarchii mikroserwisowej każdy serwis składa się z dwóch modułów - moduł-api i moduł-impl. moduł-api nie zależy od żadnego innego modułu, a moduł-impl zależy od dowolnej ilości modułów API. Dzięki temu nie ma cyklów w hierarchii modułów, a sama hierarchia modułów jest płaska. Nie ma centralnego modułu ze współdzielonymi funkcjonalnościami, więc nie ma rozpychania centralnego modułu. Przykładowa hierarchia może wyglądać tak:

  • moduł orders-api bez zależności
  • moduł trading-api bez zależności
  • moduł accounts-api bez zależności
  • moduł orders-impl zależący od orders-api i np accounts-api
  • moduł trading-impl zależący od trading-api i np accounts-api
  • moduł accounts-impl zależący od accounts-api i np orders-api

Jak widać jest faktyczny cykl między accounts i orders, ale dzięki rozbiciu na moduły API i impl nie ma tego cyklu w hierarchii modułów. Na koniec trzeba dorzucić moduł który spina całą resztę do kupy. Nazwijmy go wiring. Moduł ten zależy od wszystkich innych. Uruchamia wszystkie moduły typu impl, wyciąga z nich obiekty implementujące API i przekazuje te obiekty do modułów zależnych od tych API. Czyli w naszym przypadku moduł wiring:

  • stawia orders-impl, trading-impl i accounts-impl i wyciąga z nich obiekty implementujące odpowiednio orders-api, trading-api i accounts-api
  • obiekt implementujący orders-api jest podrzucany do modułu accounts-impl
  • obiekt implementujący accounts-api jest podrzucany do modułów orders-impl i trading-impl

Mikroserwisowa hierarchia modułów trochę podobnych wad i zalet co prawdziwe mikroserwisy. Zaletą jest jak już wspomniałem możliwość bezproblemowego robienia cyklicznych zależności oraz brak tendencji upychania funkcjonalności do wspólnego modułu (gdyż takiego nie ma). Wadą jest to, że np moduły muszą być odporne na chwilowy brak zależności. Np przy starcie moduły typu impl nie widzą innych modułów, dopiero po wystartowaniu moduł wiring podrzuca implementacje API do konkretnych modułów korzystających z nich. Jest to taka sama sytuacja jak w rzeczywistych mikroserwisach. Rzeczywiste mikroserwisy startują w dowolnej kolejności, więc jeśli mikroserwis A zależy od mikroserwisu B, ale mikroserwis A wystartował przed mikroserwisem B, to mikroserwis A musi trochę poczekać zanim zacznie korzystać z mikroserwisu B. Wadą mikroserwisowej hierarchii modułów jest też to, że wygląda dość sztucznie, nietypowo, a ja nie widziałem takiej w praktyce w monolicie.

1

Jeśli zrobię zmianę łamiącą kontrakt to tak czy siak muszę zmienić kod po obu stronach - zarówno ten wystawiający API według kontraktu jak i ten korzystający z API. Niespecjalnie widzę jak cykl ma tu coś zmieniać.

Tak to prawda, ale nie mając cyklu to działa tylko dla jednego z przypadków, gdy A zależy od B i zmieniasz B to zmieniasz obydwa, gdy zmieniasz A to zmieniasz jeden.

Teraz rozumiem o co ci chodzi, ale to to samo co moduły, które mają API publiczne (fasada/interfejs, dtosy, wyjątki), a całą resztę ukrytą wewnątrz modułu. Problem jest taki, że mimo wszystko wciąz tam jest cykl i jeżeli byś chciał testować moduły orders i accounts to i tak musisz je postawić razem albo całą aplikację, chyba że dla testów postawisz orders z stubem/mockiem accounts i na odwrót, co moim zdaniem mija się z celem. Tak samo gdybyś z tego monolitu chciał wyciągnąć orders jako faktyczny mikroserwis to wciąż zmieniasz dwa moduły, wiec IMHO pytanie czy to nie powinien być jeden moduł skoro są od siebie zależne lub czy np. nie dałoby się złamać tego cyklu wprowadzając komunikację eventami.

0

Teraz rozumiem o co ci chodzi, ale to to samo co moduły, które mają API publiczne (fasada/interfejs, dtosy, wyjątki), a całą resztę ukrytą wewnątrz modułu.

Nie to samo, bo bez podziału na -api i -impl nie jesteś w stanie zrobić cyklu w zależnościach.

Problem jest taki, że mimo wszystko wciąz tam jest cykl i to jest problem, bo jeżeli byś chciał testować moduły orders i accounts to i tak musisz je postawić razem albo całą aplikację, chyba że dla testów postawisz orders z stubem/mockiem accounts i na odwrót, co moim zdaniem mija się z celem.

A bez cyklu nie muszę stawiać ich razem?

Tak samo gdybyś z tego monolitu chciał wyciągnąć orders jako faktyczny mikroserwis to wciąż zmieniasz dwa moduły

Gdzie i co mam zmienić? Jeśli wyciągnę moduł orders-impl jako faktyczny mikroserwis to zależność od orders-api pozostaje taka sama, ale teraz moduły np accounts-impl muszą komunikować się z orders-impl przez RESTa, a nie przez np Javowy interfejs.

wiec IMHO pytanie czy to nie powinien być jeden moduł skoro są od siebie zależne lub czy np. nie dałoby się złamać tego cyklu wprowadzając komunikację eventami.

Zależności między mikroserwisami nie muszą tworzyć grafu acyklicznego, więc po co się ograniczać? I tak musisz obsłużyć sytuację, w które mikroserwisy są stawiane w różnej kolejności i w różnej kolejności np restartowane. Natomiast czasami taka zależność w obie strony się przydaje. Np serwis R zajmuje się parsowaniem raportów, a serwis G wrzucaniem ich do swojego cache, łączeniem ich z innymi danymi i pokazywaniem. Serwig G po restarcie dobija się do serwisu R, by zaktualizować własny cache. Ale z drugiej strony, jeśli przyjdą nowe raporty to serwis R wrzuca nowe raporty do serwisu G. Jak widać mamy komunikację dwustronną dlatego, że cache jest w innym serwisie niż parser, ale czy to powód by zmieniać lokalizację cache i sposób powiadamiania o zmianach?

0

Nie to samo, bo bez podziału na -api i -impl nie jesteś w stanie zrobić cyklu w zależnościach.

Czemu nie? Jedyne czego potrzebujesz to fasady innego modułu.

A bez cyklu nie muszę stawiać ich razem?

W sumie celna uwaga, majac zależność w jednym kierunku mockuje/stubbuje, dziwnie by to wyglądało gdyby były dwa moduły z dwoma mockami, ale to tylko moje poczucie gustu, więc faktycznie argument bez sensu.

Jeśli wyciągnę moduł orders-impl jako faktyczny mikroserwis to zależność od orders-api pozostaje taka sama, ale teraz moduły np accounts-impl muszą komunikować się z orders-impl przez RESTa, a nie przez np Javowy interfejs.

To prawda, to nie wiele do zmiany, ale podchodząc do tematu czysto ideologicznie to wciąż zostaje problem, że cokolwiek trzeba dłubnąć, z praktycznego punktu widzenia nie ma to znaczenia.

Zależności między mikroserwisami nie muszą tworzyć grafu acyklicznego, więc po co się ograniczać?

Traktuję tu mikroserwisy jako biznesowe komponenty/moduły systemu i wg mnie powinny tworzyć graf acykliczny, Przykład, który podałeś nie opisuje silnej zależności biznesowej logiki, więc w takim przypadku nie widzę w tym nic złego, tak samo w przypadkach gdy mikroserwis rozwiązuje inny problem technologiczny tym bardziej, jeżeli jest to na zasadzie napisz, zapomnij, najbliższa zmiana będzie za pół roku

1

@hcubyc: Ja bym sie wycofal z dyskusji bo moim zdaniem jest bez sensu- mikroserwisy to nie jest rozwiazanie problemow ktorych dotyczy temat. Owsze, jako skutek zmian moga po czesci przysluzyc sie rozwiazaniu problemu w temacie, ale jest to okraszone cena w postaci wad jakie architektura oparta o mikroserwisy ze soba niesie. Nie wiem czemu wibowit sie tak uwzial tych mikroserwisow. No chyba ze to jeden z tych ktorzy na kazdy problem maja jedno sluszne rozwiazanie :)

2

Wydaje mi się, że wątek pierwotnie miał dotyczyć nadużywania public w kodzie, z czym się zgadzam. Po kij klasa która jest użyta (i powinna być używa tylko tam) w jednym miejscu i robi tylko jedną rzecz ma być widoczna w całym projekcie? Prędzej czy później ktoś jej użyje w jakimś dziwnym kontekście i potem będzie bida (plus zaśmieca podpowiadanie składni). Wydaję mi się, że lepszym rozwiązaniem jest przeniesienie idei obiektów (hermetyzacji itp) na poziom abstrakcji wyżej.
Przykład:
mamy generowanie raportów: załóżmy, że proces składa się z kilku kroków, obsługiwanych przez kolejne obiekty. Po co teraz cały projekt ma wiedzieć, że istnieją kolejne kroki, skoro z perspektywy systemu ważne jest tylko to, że dla jakichś tam parametrów otrzyma się taki raport. Czyli zrobić jedną klasę publiczną z metodą generate (czy tam cokolwiek) i cała logika siedziałaby klasach z dostępem pakietowym wsadzonych w jednym pakiecie.
Można też iść o krok dalej i testować tylko metody publiczne w pakietach, bo one definiują API (zachowanie) całego pakietu.

1
Aventus napisał(a):

Czy w Javie i w ogole ekosystemie JVM jest z tym az tak zle ze trzeba uswiadamiac i zachecac ludzi? Jesli tak to jak to wyglada na codzien- ladujecie wszystko do jednego worka i wystawiacie jako publiczne?

A w C# jest lepiej? Większość bezmyślnie oznacza wszystko jako public.

krsp napisał(a):

No ja wolę jak pisze się na pałę public niż pisanie na pałę private.
Programowanie obiektowe to też tworzenie złożonych zachowań poprzez kompozycję prostych klas. Pisanie wszędzie private to uniemożliwienie tworzenia nowych zachowań w oparciu o istniejące klasy.

Racja, wyciek abstrakcji znacząco ułatwia dopisywanie nowych fragmentów kodu w losowo wybranej warstwie aplikacji. To ma szczególne znaczenie, jeśli chcemy aby projekt powstawał zgodnie ze wzorcem big ball of mud.

Wibowit napisał(a):

Mikroserwis składa się z dwóch modułów: API i implementacji.

Ale to jest jakaś uchwała gminy, norma ISO czy tak było w książce od JEE?

Moduł brzmi dla mnie jak jednostka logiczno-architektoniczno-deploymentowa. Jeśli implementacja znajduje się w jednym module, to znaczy, że w 2018 roku ciągle trzaskacie jednowarstwowy kod. No można i tak, wszak to też ułatwia implementację big ball of mud.

0

TL;DR
W Javie jest super-rozwiązanie package-private ale nikt z niego nie korzysta, ponieważ masz przeinżynierowanie i zamiast tego robi się pakiety typu:

com.januszsoft.dao.*
com.januszsoft.services.*
com.januszsoft.utils.*

Była o tym prezentacja, nawet chyba wspomniana przez OP.
Nie wiem czemu tak jest - albo za mała wiedza wśród ludzi do czego służy package private, albo może ten poziom dostępu nie jest zbyt wygodny.
To drugie może wynikać np. z tego że klasy
z: com.januszsoft.dao.*
nie mają dostępu do klas package-private
z: com.januszsoft.dao.users.*

3

Jest źle. Są zespoły gdzie jest ok, ale to raczej chlubne wyjątki.

Moim zdaniem największe skurczysyństwo to odwaliły JavaBeany - czyli robimy getera i setera i mamy enkapsulacjem.

Rok temu jeden junior/stazysta nie mógł zrozumieć, że nie robię getterów i setterów, a do tego, o zgrozo, czasem robię publiczne pola (finalne...).

Po dłuższej ewangelizacji podsumował:

  • ja to podejście nawet rozumiem, ale u mnie na studiach nie zaliczyłbyś nawet pierwszego semestru...
0
Aventus napisał(a):

@hcubyc: Ja bym sie wycofal z dyskusji bo moim zdaniem jest bez sensu- mikroserwisy to nie jest rozwiazanie problemow ktorych dotyczy temat. Owsze, jako skutek zmian moga po czesci przysluzyc sie rozwiazaniu problemu w temacie, ale jest to okraszone cena w postaci wad jakie architektura oparta o mikroserwisy ze soba niesie. Nie wiem czemu wibowit sie tak uwzial tych mikroserwisow. No chyba ze to jeden z tych ktorzy na kazdy problem maja jedno sluszne rozwiazanie :)

Jak ci bardzo przeszkadza nazwa "mikroserwis" to możesz ją zamienić np na "krzesełko" i wyjdzie na to samo. Moim zdaniem po prostu jeśli rozdzielę moduły na część publiczną modułX-api i prywatną modułX-impl gdzie konkretny modułX-impl może zależeć tylko od modułY-api (dla dowolnego Y) to otrzymam lepszą hierarchię zależności modułów niż w typowym monolicie. Powody już opisałem kilka razy, więc może nie będę się powtarzał.

Aktualizacja:
Niektórzy może nie zaczaili analogii api do internal, więc może lekka zmiana nazw pomoże. moduł-api to to samo co moduł-public, a moduł-impl to to samo co moduł-internal. Teraz jest jasne?

0
vpiotr napisał(a):

Nie wiem czemu tak jest - albo za mała wiedza wśród ludzi do czego służy package private, albo może ten poziom dostępu nie jest zbyt wygodny.
To drugie może wynikać np. z tego że klasy
z: com.januszsoft.dao.*
nie mają dostępu do klas package-private
z: com.januszsoft.dao.users.*

To nie jest 'niezbyt wygodne', to jest po prostu mocno słabe. To powoduje, że w prędzej czy później trzeba podjąć decyzję - czy wrzucamy milion klas w jeden płaski pakiet czy łamiemy package-private i jednak tworzymy pod-pakiety żeby to miało sens. A szkoda, bo przy małych pakietach świetnie się sprawdza.

0

Ale to nie jest teraz tak, że wszystko ma być private. Nadal część klas jest public ale tylko te, o których powinien wiedzieć świat

0

@danek: Najpierw dowiedz się czym jest package-private. To jest coś innego niż public, private, protected czy internal.

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