Innym sposobem zadawania tego pytania jest; dlaczego programy wydają się być monolityczne?
Mam na myśli coś takiego jak pakiet animacji, taki jak Maya, który ludzie używają do różnych przepływów pracy.
Gdyby możliwości animacji i modelowania zostały podzielone na osobne aplikacje i opracowane osobno, a pliki były przesyłane między nimi, czy nie byłoby łatwiejsze do utrzymania?
If the animation and modelling capabilities were split into their own separate application and developed separately, with files being passed between them, would they not be easier to maintain?
Nie mieszaj łatwiejsze do rozszerzenia z łatwiejszym do utrzymania modułem - jeśli nie jest wolny od komplikacji lub wątpliwych projektów. Maya może być piekłem na ziemi, a jej wtyczki nie. Lub odwrotnie.Odpowiedzi:
Tak. Zasadniczo dwie mniejsze, mniej złożone aplikacje są o wiele łatwiejsze w utrzymaniu niż jedna duża.
Pojawia się jednak nowy rodzaj błędu, gdy wszystkie aplikacje współpracują ze sobą, aby osiągnąć cel. Aby zmusić ich do współpracy, muszą wymieniać wiadomości, a ta aranżacja może pójść nie tak na różne sposoby, nawet jeśli każda aplikacja może działać idealnie. Posiadanie miliona małych aplikacji ma swoje własne problemy.
Aplikacja monolityczna jest tak naprawdę domyślną opcją, z jaką się kończysz, gdy dodajesz coraz więcej funkcji do jednej aplikacji. Jest to najłatwiejsze podejście, jeśli weźmiesz pod uwagę każdą funkcję osobno. Dopiero gdy urosną, możesz spojrzeć na całość i powiedzieć: „wiesz co, lepiej by było, gdybyśmy wyodrębnili X i Y”.
źródło
Rzeczy w rzeczywistości rzadko są tak proste.
Podział zdecydowanie nie pomaga przede wszystkim w zapobieganiu tym błędom. Czasami może pomóc w szybszym znajdowaniu błędów. Aplikacja, która składa się z małych, izolowanych komponentów, może pozwolić na bardziej indywidualne testy (tych „jednostek” -) dla tych komponentów, co może czasem ułatwić wykrycie pierwotnej przyczyny niektórych błędów, a tym samym umożliwić ich szybsze usunięcie.
Jednak,
nawet aplikacja, która wydaje się monolityczna z zewnątrz, może składać się z wielu elementów możliwych do przetestowania w jednostce, więc testowanie jednostkowe niekoniecznie jest trudniejsze dla aplikacji monolitycznej
jak już wspomniano Ewan, interakcja kilku składników wprowadza dodatkowe ryzyko i błędy. Debugowanie systemu aplikacji ze złożoną komunikacją międzyprocesową może być znacznie trudniejsze niż debugowanie aplikacji jednoprocesowej
Zależy to również w dużej mierze od tego, jak dobrze większa aplikacja może podzielić się na komponenty, jak szerokie są interfejsy między komponentami i jak te interfejsy są używane.
Krótko mówiąc, często jest to kompromis i nic, w przypadku gdy odpowiedź „tak” lub „nie” jest ogólnie poprawna.
Czy oni? Rozejrzyj się wokół, są tysiące aplikacji internetowych na świecie, które nie wyglądają dla mnie zbyt monolitycznie, wręcz przeciwnie. Istnieje również wiele programów, które zapewniają model wtyczki (AFAIK nawet oprogramowanie Maya, o którym wspomniałeś, robi).
„Łatwiejsza konserwacja” często wynika z faktu, że różne części aplikacji mogą być łatwiej opracowywane przez różne zespoły, dzięki czemu lepiej rozłożone obciążenie, wyspecjalizowane zespoły z wyraźniejszym nastawieniem i dalej.
źródło
Będę musiał się nie zgodzić z większością w tej sprawie. Podział aplikacji na dwie osobne nie sprawia, że kod jest łatwiejszy do utrzymania lub uzasadnienia.
Rozdzielenie kodu na dwa pliki wykonywalne po prostu zmienia fizyczną strukturę kodu, ale to nie jest ważne. O tym, jak złożona jest aplikacja, decyduje to, jak ściśle powiązane są ze sobą różne części, które ją tworzą. To nie jest własność fizyczna, ale logiczna .
Możesz mieć monolityczną aplikację, która ma wyraźne oddzielenie różnych problemów i prostych interfejsów. Możesz mieć architekturę mikrousług, która opiera się na szczegółach implementacji innych mikrousług i jest ściśle powiązana ze wszystkimi innymi.
Prawdą jest, że proces dzielenia jednej dużej aplikacji na mniejsze jest bardzo pomocny przy próbie ustalenia jasnych interfejsów i wymagań dla każdej części. W DDD mów, że wymyślisz swoje ograniczone konteksty. Ale to, czy następnie utworzysz wiele małych aplikacji, czy jedną dużą, która ma tę samą logiczną strukturę, jest bardziej decyzją techniczną.
źródło
Łatwiejsze w utrzymaniu, gdy skończysz je dzielić, tak. Ale podział ich nie zawsze jest łatwy. Próba podzielenia fragmentu programu na bibliotekę wielokrotnego użytku ujawnia, gdzie pierwotni programiści nie zastanawiali się, gdzie powinny znajdować się szwy . Jeśli jedna część aplikacji sięga głęboko w inną część aplikacji, naprawa może być trudna. Zgrywanie szwy wymusza zdefiniowanie wewnętrznych API jaśniej, i to jest to, co ostatecznie sprawia, że baza kod łatwiejszy w utrzymaniu. Wielokrotnego użytku i konserwacji są produkty dobrze określonych szwów.
źródło
Ważne jest, aby pamiętać, że korelacja nie jest przyczyną.
Zbudowanie dużego monolitu, a następnie podzielenie go na kilka małych części może, ale nie musi, prowadzić do dobrego projektu. ( Może poprawić projekt, ale nie ma takiej gwarancji.)
Ale dobry projekt często prowadzi do zbudowania systemu jako kilku małych części zamiast dużego monolitu. (Monolit może być najlepszym projektem, jest o wiele mniej prawdopodobne).
Dlaczego małe części są lepsze? Ponieważ łatwiej je zrozumieć. A jeśli łatwo jest uzasadnić poprawność, istnieje większe prawdopodobieństwo, że uzyskasz poprawny wynik.
Cytując CAR Hoare:
Jeśli tak, to dlaczego ktoś miałby budować niepotrzebnie skomplikowane lub monolityczne rozwiązanie? Hoare udziela odpowiedzi w następnym zdaniu:
A później w tym samym źródle (wykład Turinga z 1980 r.):
źródło
To nie jest pytanie z odpowiedzią tak lub nie. Pytanie to nie tylko łatwość konserwacji, ale także pytanie o efektywne wykorzystanie umiejętności.
Ogólnie dobrze napisana aplikacja monolityczna jest wydajna. Komunikacja między procesami i między urządzeniami nie jest tania. Przerwanie jednego procesu zmniejsza wydajność. Jednak wykonanie wszystkiego na jednym procesorze może przeciążyć procesor i obniżyć wydajność. Jest to podstawowy problem ze skalowalnością. Gdy sieć wchodzi w obraz, problem staje się bardziej skomplikowany.
Dobrze napisana aplikacja monolityczna, która może działać skutecznie jako pojedynczy proces na jednym serwerze, może być łatwa w utrzymaniu i wolna od wad, ale nadal nie może być efektywnym wykorzystaniem umiejętności kodowania i architektury. Pierwszym krokiem jest rozbicie procesu na biblioteki, które nadal działają jako ten sam proces, ale są kodowane niezależnie, zgodnie z dyscypliną spójności i luźnego łączenia. Dobra praca na tym poziomie poprawia łatwość konserwacji i rzadko wpływa na wydajność.
Następnym etapem jest podzielenie monolitu na osobne procesy. Jest to trudniejsze, ponieważ wchodzisz na trudne terytorium. Łatwo jest wprowadzić błędy warunków wyścigu. Zwiększają się koszty komunikacji i należy uważać na „gadatliwe interfejsy”. Nagrody są świetne, ponieważ przełamujesz barierę skalowalności, ale zwiększa się również potencjał wad. Aplikacje wieloprocesowe są łatwiejsze w utrzymaniu na poziomie modułu, ale cały system jest bardziej skomplikowany i trudniejszy do rozwiązania. Naprawy mogą być diabelnie skomplikowane.
Gdy procesy są dystrybuowane do oddzielnych serwerów lub do implementacji w chmurze, problemy stają się coraz trudniejsze, a nagrody większe. Skalowalność szybuje. (Jeśli zastanawiasz się nad implementacją chmury, która nie daje skalowalności, zastanów się.) Jednak problemy, które pojawiają się na tym etapie, mogą być niezwykle trudne do zidentyfikowania i przemyślenia.
źródło
Nie . nie ułatwia to utrzymania. Jeśli cokolwiek, zapraszamy na więcej problemów.
Dlaczego?
Masz teraz dwa produkty, które potrzebują:
Masz teraz trzy rynki konsumenckie: modelarzy, animatorów i animatorów modellerów
Biorąc pod uwagę, że mniejsze bazy kodu są łatwiejsze do utrzymania na poziomie aplikacji, po prostu nie dostaniesz darmowego lunchu. Jest to ten sam problem w sercu architektury Micro-Service / Any-Modular-Architecture. To nie jest panaceum, trudności konserwacyjne na poziomie aplikacji są wymieniane za trudności konserwacyjne na poziomie aranżacji. Te problemy są nadal problemami, po prostu nie znajdują się już w bazie kodu, należy ich uniknąć lub rozwiązać.
Jeśli rozwiązanie problemu na poziomie aranżacji jest prostsze niż rozwiązywanie go na każdym poziomie aplikacji, wówczas sensowne jest podzielenie go na dwie bazy kodu i zajęcie się problemami z aranżacją.
W przeciwnym razie nie, po prostu nie rób tego, lepiej byś był obsługiwany przez poprawę wewnętrznej modułowości samej aplikacji. Wypchnij sekcje kodu do spójnych i łatwiejszych w utrzymaniu bibliotek, do których aplikacja działa jak wtyczka. W końcu monolit jest tylko warstwą aranżacyjną krajobrazu bibliotecznego.
źródło
Było wiele dobrych odpowiedzi, ale ponieważ jest prawie martwy podział, wrzucę też kapelusz na ring.
Z mojego doświadczenia jako inżyniera oprogramowania odkryłem, że nie jest to prosty problem. To zależy od wielkości , skali i celu zastosowania. Starsze zastosowania ze względu na bezwładność wymaganą do ich zmiany są generalnie monolityczne, ponieważ była to powszechna praktyka przez długi czas (Maya kwalifikowała się w tej kategorii). Zakładam, że ogólnie mówisz o nowszych aplikacjach.
W wystarczająco małych aplikacjach, które są mniej więcej pojedynczymi problemami, narzut wymagany do utrzymania wielu oddzielnych części ogólnie przewyższa użyteczność oddzielenia. Jeśli może go utrzymywać jedna osoba, prawdopodobnie może być monolityczny, nie powodując zbyt wielu problemów. Wyjątkiem od tej reguły jest sytuacja, gdy masz wiele różnych części (frontend, backend, być może niektóre warstwy danych pomiędzy nimi), które są dogodnie oddzielone (logicznie).
W bardzo dużych nawet pojedynczych aplikacjach dzielenie go ma sens z mojego doświadczenia. Zaletą jest zmniejszenie części możliwych błędów w zamian za inne (czasem łatwiejsze do rozwiązania) błędy. Ogólnie rzecz biorąc, możesz również mieć zespoły ludzi pracujących w izolacji, co poprawia wydajność. Wiele aplikacji jest jednak obecnie bardzo dobrze podzielonych, czasem na własną szkodę. Byłem też w zespołach, w których aplikacja została niepotrzebnie podzielona na tak wiele mikrousług, że spowodowało to duże obciążenie, gdy sprawy przestały ze sobą rozmawiać. Ponadto konieczność posiadania całej wiedzy na temat tego, jak każda część rozmawia z innymi częściami, staje się znacznie trudniejsza z każdym kolejnym podziałem. Istnieje równowaga i jak można stwierdzić po odpowiedziach tutaj, sposób na zrobienie tego nie jest bardzo jasny,
źródło
W przypadku aplikacji interfejsu użytkownika jest mało prawdopodobne, aby zmniejszyć ogólną liczbę błędów, ale zmieni bilans mieszania błędów w kierunku problemów spowodowanych komunikacją.
Mówiąc o aplikacjach / witrynach z interfejsem użytkownika - użytkownicy są wyjątkowo niecierpliwi i wymagają krótkiego czasu reakcji. Powoduje to, że wszelkie opóźnienia w komunikacji stają się błędami. W rezultacie wymienimy potencjalne zmniejszenie liczby błędów z powodu zmniejszonej złożoności pojedynczego komponentu z bardzo trudnymi błędami i wymogiem czasowym komunikacji między procesami / między maszynami.
Jeśli jednostki danych, którymi zajmuje się program, są duże (tzn. Obrazy), wszelkie opóźnienia między procesami byłyby dłuższe i trudniejsze do wyeliminowania - coś w rodzaju „zastosuj transformację do obrazu 10 MB” natychmiast zyska dodatkowo + 20 MB dysku I / dysku IO do 2 konwersji z formatu w pamięci na format serializacji iz powrotem. Naprawdę niewiele można zrobić, aby ukryć czas potrzebny użytkownikowi.
Ponadto wszelka komunikacja, a zwłaszcza dyskowe operacje wejścia / wyjścia, podlegają kontroli antywirusowej / zapory ogniowej - to nieuchronnie dodaje kolejną warstwę trudnych do odtworzenia błędów i jeszcze więcej opóźnień.
Podział monolitycznego „programu” świeci tam, gdzie opóźnienia komunikacyjne nie są krytyczne lub są już nieuniknione
Należy pamiętać, że dotyczy to zarówno aplikacji komputerowych, jak i stron internetowych - część programu skierowana do użytkownika jest zazwyczaj „monolityczna” - cały kod interakcji użytkownika związany z pojedynczą częścią danych jest zwykle uruchamiany w jednym procesie (podział nie jest niczym niezwykłym przetwarza na podstawie danych, takich jak strona HTML lub obraz, ale jest to ortogonalne dla tego pytania). Nawet w przypadku najbardziej podstawowej witryny z danymi wprowadzonymi przez użytkownika zobaczysz logikę sprawdzania poprawności uruchomioną po stronie klienta, nawet jeśli uczynienie jej po stronie serwera będzie bardziej modułowe i zmniejszy złożoność / duplikację kodu.
źródło
Zapobiec? Nie, nie bardzo.
Mianowicie wszystkie błędy, o których nawet nie wiedziałeś, że odkryłeś je, kiedy próbowałeś podzielić cały ten bałagan na mniejsze części. W pewien sposób zapobiegło to pojawieniu się tych błędów w produkcji - ale błędy już tam były.
Błędy w aplikacjach monolitycznych mogą doprowadzić do awarii całego systemu i uniemożliwić użytkownikowi interakcję z aplikacją. Jeśli podzielisz tę aplikację na komponenty, większość błędów - zgodnie z projektem - wpłynie tylko na jeden z komponentów.
Jeśli chcesz zachować niezmienione wrażenia użytkownika, musisz wprowadzić nową logikę dla wszystkich tych komponentów do komunikacji (za pośrednictwem usług REST, za pośrednictwem wywołań systemu operacyjnego, co masz), aby mogły bezproblemowo współdziałać z POV użytkownika.
Jako prosty przykład: Twoja monolityczna aplikacja pozwala użytkownikom stworzyć model i animować go bez opuszczania aplikacji. Aplikacja została podzielona na dwa komponenty: modelowanie i animację. Teraz użytkownicy muszą wyeksportować model aplikacji do modelowania do pliku, a następnie znaleźć plik, a następnie otworzyć go za pomocą aplikacji do animacji ... Spójrzmy prawdzie w oczy, niektórym użytkownikom się to nie spodoba, więc musisz uwzględnić nową logikę dla aplikacja do modelowania w celu wyeksportowania pliku iautomatycznie uruchom aplikację do animacji i otwórz plik. I ta nowa logika, choć może być prosta, może zawierać szereg błędów dotyczących serializacji danych, dostępu do plików i uprawnień, użytkowników zmieniających ścieżkę instalacyjną aplikacji itp.
Decydując się na podzielenie monolitycznej aplikacji na mniejsze komponenty, (miejmy nadzieję) robisz to z dużo większą wiedzą i doświadczeniem na temat systemu niż w momencie jego pierwszego zaprojektowania, a dzięki temu możesz zastosować wiele refaktorów do stworzenia kodu czystsze, prostsze, wydajniejsze, bardziej elastyczne, bezpieczniejsze. A to refaktoryzacja może w pewien sposób pomóc w zapobieganiu błędom. Oczywiście możesz również zastosować to samo refaktoryzowanie do monolitycznej aplikacji, aby zapobiec tym samym błędom, ale nie robisz tego, ponieważ jest tak monolityczny, że boisz się dotknąć czegoś w interfejsie użytkownika i złamać logikę biznesową ¯ \ _ (ツ) _ / ¯
Nie powiedziałbym więc, że zapobiegasz błędom, dzieląc monolityczną aplikację na mniejsze komponenty, ale w rzeczywistości ułatwiasz osiągnięcie punktu, w którym można łatwiej zapobiec błędom.
źródło