Udostępnij za pośrednictwem


Typowe architektury aplikacji internetowych

Wskazówka

Ta treść jest fragmentem eBooka, Architektura nowoczesnych aplikacji internetowych z ASP.NET Core i Azure, dostępny na .NET Docs lub jako bezpłatny plik PDF do pobrania, który można czytać w trybie offline.

Miniatura okładki eBooka

"Jeśli uważasz, że dobra architektura jest kosztowna, spróbuj użyć złej architektury". - Brian Foote i Joseph Yoder

Większość tradycyjnych aplikacji platformy .NET jest wdrażana jako pojedyncze jednostki odpowiadające plikowi wykonywalnemu lub pojedynczej aplikacji internetowej działającej w ramach jednej domeny aplikacji usług IIS. Takie podejście jest najprostszym modelem wdrażania i bardzo dobrze obsługuje wiele wewnętrznych i mniejszych aplikacji publicznych. Jednak nawet biorąc pod uwagę tę pojedynczą jednostkę wdrożenia, większość złożonych aplikacji biznesowych korzysta z pewnego logicznego podziału na kilka warstw.

Co to jest aplikacja monolityczna?

Aplikacja monolityczna to aplikacja, która jest całkowicie samodzielna w kwestii jej działania. Może on wchodzić w interakcje z innymi usługami lub magazynami danych w trakcie wykonywania operacji, ale rdzeń jego zachowania działa we własnym procesie, a cała aplikacja jest zwykle wdrażana jako pojedyncza jednostka. Jeśli taka aplikacja musi być skalowana w poziomie, zazwyczaj cała aplikacja jest duplikowana na wielu serwerach lub maszynach wirtualnych.

Wielofunkcyjne aplikacje

Najmniejsza możliwa liczba projektów dla architektury aplikacji to jedna. W tej architekturze cała logika aplikacji jest zawarta w jednym projekcie, skompilowanym w jednym zestawie i wdrożona jako pojedyncza jednostka.

Nowy projekt ASP.NET Core, utworzony zarówno w programie Visual Studio, jak i z poziomu wiersza polecenia, rozpoczyna się jako prosty monolit all-in-one. Zawiera ona całą logikę aplikacji, w tym prezentację, logikę biznesową oraz logikę dostępu do danych. Rysunek 5–1 przedstawia strukturę plików aplikacji jednoprojektowej.

Pojedyncza aplikacja ASP.NET Core projektu

Rysunek 5–1. Pojedyncza aplikacja w ramach projektu ASP.NET Core.

W jednym scenariuszu projektu separacja problemów jest osiągana za pomocą folderów. Szablon domyślny zawiera oddzielne foldery dla obowiązków wzorca MVC modeli, widoków i kontrolerów, a także dodatkowe foldery dla danych i usług. W tym układzie szczegóły prezentacji powinny być ograniczone jak najwięcej do folderu Widoki, a szczegóły implementacji dostępu do danych powinny być ograniczone do klas przechowywanych w folderze Dane. Logika biznesowa powinna znajdować się w usługach i klasach w folderze Modele.

Chociaż proste, jednoprojektowe rozwiązanie monolityczne ma pewne wady. Wraz ze wzrostem rozmiaru i złożoności projektu liczba plików i folderów będzie nadal rosła. Problemy dotyczące interfejsu użytkownika (modele, widoki, kontrolery) znajdują się w wielu folderach, które nie są grupowane alfabetycznie. Ten problem pogarsza się tylko wtedy, gdy dodatkowe konstrukcje na poziomie interfejsu użytkownika, takie jak Filtry lub ModelBinders, są dodawane we własnych folderach. Logika biznesowa jest rozproszona między folderami Modele i usługi i nie ma wyraźnego wskazania, które klasy, w których folderach powinny zależeć od innych. Ten brak organizacji na poziomie projektu często prowadzi do kodu spaghetti.

Aby rozwiązać te problemy, aplikacje często ewoluują w rozwiązania wieloprojektowe, w których każdy projekt jest uznawany za znajdujący się w określonej warstwie aplikacji.

Co to są warstwy?

W miarę zwiększania złożoności aplikacji jednym ze sposobów zarządzania tym złożonością jest podzielenie aplikacji zgodnie z jej obowiązkami lub obawami. Takie podejście jest zgodne z zasadą separacji zagadnień i może pomóc utrzymać rosnącą bazę kodu zorganizowaną tak, aby deweloperzy mogli łatwo znaleźć miejsce implementacji niektórych funkcji. Architektura warstwowa oferuje jednak wiele zalet poza organizacją kodu.

Organizując kod w warstwach, typowe funkcje niskiego poziomu mogą być ponownie używane w całej aplikacji. Ponowne użycie jest korzystne, ponieważ oznacza to, że należy napisać mniej kodu i ponieważ może umożliwić aplikacji standaryzację w ramach jednej implementacji, zgodnie z zasadą nie powtarzaj się (DRY).

W przypadku architektury warstwowej aplikacje mogą wymuszać ograniczenia dotyczące warstw, na których warstwy mogą komunikować się z innymi warstwami. Ta architektura pomaga osiągnąć hermetyzację. W przypadku zmiany lub zastąpienia warstwy wpływ powinien mieć tylko na te warstwy, które z nią współpracują. Ograniczając warstwy zależne od innych warstw, można ograniczyć wpływ zmian, aby jedna zmiana nie wpływała na całą aplikację.

Warstwy (i hermetyzacja) znacznie ułatwiają zastępowanie funkcji w aplikacji. Na przykład aplikacja może początkowo używać własnej bazy danych SQL Server do przechowywania danych, ale później może zdecydować się na strategię utrwalania w chmurze lub za pomocą webowego interfejsu API. Jeśli aplikacja prawidłowo hermetyzuje implementację swojej trwałości w warstwie logicznej, to ta specyficzna dla SQL Server warstwa mogłaby zostać zastąpiona nową warstwą, która implementuje ten sam interfejs publiczny.

Oprócz możliwości zamiany implementacji w odpowiedzi na przyszłe zmiany wymagań warstwy aplikacji mogą również ułatwić zamianę implementacji na potrzeby testowania. Zamiast pisać testy, które działają względem rzeczywistej warstwy danych lub warstwy interfejsu użytkownika aplikacji, te warstwy można zastąpić w czasie testowania fałszywymi implementacjami, które zapewniają znane odpowiedzi na żądania. Takie podejście zwykle znacznie ułatwia pisanie testów i znacznie szybsze uruchamianie w porównaniu z uruchamianiem testów względem rzeczywistej infrastruktury aplikacji.

Warstwy logiczne to powszechna technika ulepszania organizacji kodu w aplikacjach oprogramowania dla przedsiębiorstw i istnieje kilka sposobów organizowania kodu w warstwy.

Uwaga / Notatka

Warstwy reprezentują separację logiczną w aplikacji. W przypadku, gdy logika aplikacji jest fizycznie dystrybuowana do oddzielnych serwerów lub procesów, te oddzielne obiekty docelowe wdrożenia fizycznego są określane jako warstwy. Jest to możliwe i całkiem powszechne mieć aplikację N-Warstwową, która jest wdrażana na jednej warstwie.

Tradycyjne aplikacje architektury "N-Layer"

Najbardziej typowa organizacja logiki aplikacji na warstwy jest przedstawiona na rysunku 5–2.

Typowe warstwy aplikacji

Rysunek 5–2. Typowe warstwy aplikacji.

Te warstwy są często skracane jako interfejs użytkownika, BLL (warstwa logiki biznesowej) i DAL (warstwa dostępu do danych). Korzystając z tej architektury, użytkownicy wysyłają żądania za pośrednictwem warstwy interfejsu użytkownika, która współdziała tylko z usługą BLL. Z kolei BLL może wywołać DAL dla żądań dostępu do danych. Warstwa interfejsu użytkownika nie powinna bezpośrednio wysyłać żadnych żądań do warstwy dostępu do danych ani nie powinna bezpośrednio nawiązywać interakcji z warstwą trwałości danych za pośrednictwem innych środków. Podobnie BLL powinien współdziałać tylko z warstwą trwałości, przechodząc przez DAL. W ten sposób każda warstwa ma własną dobrze znaną odpowiedzialność.

Jedną z wad tego tradycyjnego podejścia warstwowego jest to, że zależności czasu kompilacji są uruchamiane od góry do dołu. Oznacza to, że warstwa interfejsu użytkownika zależy od biblioteki BLL, która zależy od dal. Oznacza to, że usługa BLL, która zwykle przechowuje najważniejszą logikę w aplikacji, jest zależna od szczegółów implementacji dostępu do danych (i często od istnienia bazy danych). Testowanie logiki biznesowej w takiej architekturze jest często trudne i wymaga testowej bazy danych. Reguła inwersji zależności może służyć do rozwiązania tego problemu, jak pokazano w następnej sekcji.

Rysunek 5–3 przedstawia przykładowe rozwiązanie, które dzieli aplikację na trzy projekty według odpowiedzialności (lub warstwy).

Prosta aplikacja monolityczna z trzema projektami

Rysunek 5–3. Prosta aplikacja monolityczna z trzema projektami.

Mimo że ta aplikacja używa kilku projektów do celów organizacyjnych, jest ona nadal wdrażana jako pojedyncza jednostka, a jej klienci będą z nią korzystać jako pojedyncza aplikacja internetowa. Umożliwia to bardzo prosty proces wdrażania. Rysunek 5–4 pokazuje, jak taka aplikacja może być hostowana przy użyciu platformy Azure.

Proste wdrażanie aplikacji internetowej platformy Azure

Rysunek 5–4. Proste wdrażanie aplikacji internetowej platformy Azure

W miarę rozwoju potrzeb aplikacji może być wymagane bardziej złożone i niezawodne rozwiązania wdrożeniowe. Rysunek 5–5 przedstawia przykład bardziej złożonego planu wdrażania, który obsługuje dodatkowe możliwości.

Wdrażanie aplikacji internetowej w usłudze Azure App Service

Rysunek 5–5. Wdrażanie aplikacji internetowej w usłudze Azure App Service

Wewnętrznie organizacja tego projektu w wielu projektach w oparciu o odpowiedzialność zwiększa łatwość utrzymania aplikacji.

Tę jednostkę można skalować w górę lub w poziomie, aby korzystać ze skalowalności opartej na chmurze. Skalowanie w górę oznacza dodanie dodatkowego procesora CPU, pamięci, miejsca na dysku lub innych zasobów do serwerów hostujących aplikację. Skalowanie poziome oznacza dodanie dodatkowych wystąpień takich serwerów, niezależnie od tego, czy są to serwery fizyczne, maszyny wirtualne czy kontenery. Gdy aplikacja jest hostowana w wielu wystąpieniach, moduł równoważenia obciążenia służy do przypisywania żądań do poszczególnych wystąpień aplikacji.

Najprostszym podejściem do skalowania aplikacji internetowej na platformie Azure jest skonfigurowanie skalowania ręcznego w planie usługi App Service aplikacji. Rysunek 5–6 przedstawia odpowiedni ekran pulpitu nawigacyjnego platformy Azure w celu skonfigurowania liczby wystąpień obsługujących aplikację.

Skalowanie planu usługi App Service na platformie Azure

Rysunek 5–6. Skalowanie planu usługi App Service na platformie Azure.

Czysta architektura

Aplikacje zgodne z zasadą inwersji zależności, a także zasady projektowania Domain-Driven (DDD) zwykle docierają do podobnej architektury. Ta architektura była znana pod wieloma nazwami przez lata. Jedną z pierwszych nazw była architektura sześciokątna, a następnie porty i adaptery. Ostatnio cytowano ją jako architekturę cebuli lub czystą architekturę. Ostatnia nazwa, Clean Architecture, jest używana jako nazwa tej architektury w tym e-booku.

Aplikacja referencyjna eShopOnWeb używa podejścia Clean Architecture w organizowaniu kodu w projektach. Szablon rozwiązania można znaleźć jako punkt wyjścia dla własnych rozwiązań ASP.NET Core w repozytorium GitHub ardalis/cleanarchitecture lub instalując szablon z narzędzia NuGet.

Czysta architektura umieszcza logikę biznesową i model aplikacji w centrum aplikacji. Zamiast logika biznesowa zależy od dostępu do danych lub innych kwestii z infrastrukturą, zależność ta jest odwrócona: infrastruktura i szczegóły implementacji zależą od rdzenia aplikacji. Ta funkcja jest osiągana przez zdefiniowanie abstrakcji lub interfejsów w rdzeniu aplikacji, które są następnie implementowane przez typy zdefiniowane w warstwie infrastruktury. Typowym sposobem wizualizacji tej architektury jest użycie serii okręgów koncentrycznych, podobnych do cebuli. Rysunek 5–7 przedstawia przykład tego stylu reprezentacji architektury.

Czysta architektura; widok cebuli

Rysunek 5–7. Czysta architektura; widok cebuli

Na tym diagramie zależności przepływają w kierunku najbardziej wewnętrznego okręgu. Aplikacja Application Core przyjmuje swoją nazwę z pozycji na rdzeniu tego diagramu. Na diagramie widać, że platforma Application Core nie ma zależności od innych warstw aplikacji. Jednostki i interfejsy aplikacji znajdują się w samym centrum. Tuż na zewnątrz, ale nadal częścią rdzenia aplikacji, znajdują się serwisy domenowe, które zazwyczaj implementują interfejsy zdefiniowane w kole wewnętrznym. Poza rdzeniem aplikacji zarówno interfejs użytkownika, jak i warstwy infrastruktury zależą od rdzenia aplikacji, ale nie od siebie (koniecznie).

Rysunek 5–8 przedstawia bardziej tradycyjny diagram warstwy poziomej, który lepiej odzwierciedla zależność między interfejsem użytkownika a innymi warstwami.

Czysta architektura; widok warstwy poziomej

Rysunek 5–8. Czysta architektura; widok warstwy poziomej

Proszę zauważyć, że ciągłe strzałki reprezentują zależności czasu kompilacji, a strzałka przerywana reprezentuje zależność tylko w czasie uruchomienia. W przypadku czystej architektury warstwa interfejsu użytkownika współdziała z interfejsami zdefiniowanymi w rdzeniu aplikacji w czasie kompilacji i najlepiej nie powinna wiedzieć o typach implementacji zdefiniowanych w warstwie infrastruktury. Jednak w czasie wykonywania te typy implementacji są wymagane do wykonania przez aplikację, dlatego muszą być obecne i połączone z interfejsami Application Core za pośrednictwem wstrzykiwania zależności.

Rysunek 5–9 przedstawia bardziej szczegółowy widok architektury aplikacji ASP.NET Core podczas tworzenia poniższych zaleceń.

diagram architektury ASP.NET Core po czystej architekturze

Rysunek 5–9. Diagram architektury ASP.NET Core zgodnie z zasadami Czystej Architektury.

Ponieważ rdzeń aplikacji nie zależy od infrastruktury, bardzo łatwo jest pisać zautomatyzowane testy jednostkowe dla tej warstwy. Na rysunkach 5-10 i 5-11 pokazano, jak testy pasują do tej architektury.

UnitTestCore

Rysunek 5–10. Jednostkowe testowanie rdzenia aplikacji w izolacji.

IntegrationTests

Rysunek 5–11. Implementacje infrastruktury testowania integracji z zależnościami zewnętrznymi.

Ponieważ warstwa interfejsu użytkownika nie ma żadnych bezpośrednich zależności od typów zdefiniowanych w projekcie Infrastruktura, bardzo łatwo jest zamienić implementacje, aby ułatwić testowanie lub w odpowiedzi na zmieniające się wymagania aplikacji. ASP.NET Core wbudowane zastosowanie oraz wsparcie dla wstrzykiwania zależności sprawia, że ta architektura jest najbardziej odpowiednia do struktury nietrywialnych aplikacji monolitycznych.

W przypadku aplikacji monolitycznych projekty Application Core, Infrastructure i UI są uruchamiane jako pojedyncza aplikacja. Architektura aplikacji środowiska uruchomieniowego może wyglądać podobnie do rysunku 5–12.

architektura podstawowa ASP.NET 2

Rysunek 5–12. Przykładowa architektura środowiska uruchomieniowego aplikacji ASP.NET Core.

Organizowanie kodu w czystej architekturze

W rozwiązaniu Czystej architektury każdy projekt ma jasne obowiązki. W związku z tym niektóre typy należą do każdego projektu i często znajdziesz foldery odpowiadające tym typom w odpowiednim projekcie.

Rdzeń Aplikacji

Aplikacja Core przechowuje model biznesowy, który obejmuje jednostki, usługi i interfejsy. Te interfejsy obejmują abstrakcje operacji, które będą wykonywane przy użyciu infrastruktury, takich jak dostęp do danych, dostęp do systemu plików, wywołania sieciowe itp. Czasami usługi lub interfejsy zdefiniowane w tej warstwie muszą współpracować z typami innych niż jednostki, które nie mają zależności od interfejsu użytkownika lub infrastruktury. Można je zdefiniować jako proste obiekty transferu danych (DTO).

Typy rdzeni aplikacji
  • Jednostki (klasy modelu biznesowego, które są utrwalane)
  • Agregacje (grupy jednostek)
  • Interfejsy
  • Usługi domenowe
  • Specyfikacje
  • Wyjątki niestandardowe i klauzule strażnicze
  • Zdarzenia domeny i programy obsługi

Infrastruktura

Projekt Infrastruktura zwykle obejmuje implementacje dostępu do danych. W typowej aplikacji internetowej platformy ASP.NET Core implementacje te obejmują obiekty Programu Entity Framework (EF) DbContext, wszystkie zdefiniowane obiekty programu EF Core Migration i klasy implementacji dostępu do danych. Najczęstszym sposobem abstrakcyjnego kodu implementacji dostępu do danych jest użycie wzorca projektowego repozytorium.

Oprócz implementacji dostępu do danych projekt Infrastruktura powinien zawierać implementacje usług, które muszą wchodzić w interakcję z kwestiami związanymi z infrastrukturą. Te usługi powinny implementować interfejsy zdefiniowane w elemencie Application Core, dlatego infrastruktura powinna mieć odwołanie do projektu Application Core.

Typy infrastruktury
  • Typy programu EF Core (DbContext, Migration)
  • Typy implementacji dostępu do danych (repozytoria)
  • Usługi specyficzne dla infrastruktury (na przykład FileLogger lub SmtpNotifier)

Warstwa interfejsu użytkownika

Warstwa interfejsu użytkownika w aplikacji ASP.NET Core MVC jest punktem wejścia dla aplikacji. Ten projekt powinien odwoływać się do projektu Application Core, a jego typy powinny ściśle korzystać z infrastruktury za pośrednictwem interfejsów zdefiniowanych w aplikacji Core. W warstwie interfejsu użytkownika nie powinny być dozwolone żadne bezpośrednie wystąpienia ani wywołania statyczne typów warstw infrastruktury.

Typy warstw interfejsu użytkownika
  • Kontrolery
  • Filtry niestandardowe
  • Niestandardowe oprogramowanie pośredniczące
  • Widoki
  • Modele widoków
  • Firma startupowa

Plik Startup klasy lub Program.cs jest odpowiedzialny za konfigurowanie aplikacji oraz podłączanie typów implementacji do interfejsów. Miejsce, w którym jest wykonywana ta logika, jest znane jako composition root aplikacji i to ono umożliwia prawidłowe działanie wstrzykiwania zależności w czasie działania aplikacji.

Uwaga / Notatka

Aby skonfigurować wstrzykiwanie zależności podczas uruchamiania aplikacji, projekt warstwy UI może wymagać odwołania się do projektu warstwy Infrastruktury. Tę zależność można wyeliminować najłatwiej przy użyciu niestandardowego kontenera DI, który ma wbudowaną obsługę ładowania typów z zestawów. Na potrzeby tego przykładu najprostszym podejściem jest umożliwienie projektowi UI odwołania się do projektu Infrastruktury. Deweloperzy powinni jednak ograniczyć rzeczywiste odwołania do typów w projekcie Infrastruktury do głównego komponentu struktury aplikacji.

Aplikacje monolityczne i kontenery

Możesz utworzyć pojedynczą i monolityczną aplikację internetową lub usługę opartą na monolitycznym wdrożeniu i wdrożyć ją jako kontener. W aplikacji może nie być monolityczna, ale zorganizowana w kilka bibliotek, składników lub warstw. Zewnętrznie jest to pojedynczy kontener z pojedynczym procesem, pojedynczą aplikacją internetową lub pojedynczą usługą.

Aby zarządzać tym modelem, należy wdrożyć pojedynczy kontener reprezentujący aplikację. Aby przeprowadzić skalowanie, wystarczy dodać dodatkowe kopie z modułem równoważenia obciążenia z przodu. Prostota pochodzi z zarządzania pojedynczym wdrożeniem w jednym kontenerze lub maszynie wirtualnej.

Rysunek 5–13

W każdym kontenerze można uwzględnić wiele składników/bibliotek lub warstw wewnętrznych, jak pokazano na rysunku 5–13. Jednak zgodnie z zasadą kontenera "kontener wykonuje jedną rzecz i robi to w jednym procesie", wzorzec monolityczny może być konfliktem.

Wadą tego podejścia jest to, czy i kiedy aplikacja rośnie, wymagając jej skalowania. Jeśli cała aplikacja się skaluje, to naprawdę nie stanowi problemu. Jednak w większości przypadków kilka części aplikacji jest punktami dławiania wymagającymi skalowania, podczas gdy inne składniki są używane mniej.

Korzystając z typowego przykładu handlu elektronicznego, prawdopodobnie konieczne jest skalowanie składnika informacji o produkcie. Wielu innych klientów przegląda produkty niż je kupuje. Więcej klientów korzysta z koszyka niż używa potoku płatności. Mniej klientów dodaje komentarze lub wyświetla historię zakupów. Prawdopodobnie masz tylko kilku pracowników w jednym regionie, które muszą zarządzać kampaniami marketingowymi i treściami. Skalując projekt monolityczny, cały kod jest wdrażany wiele razy.

Oprócz problemu "skalowanie wszystkiego" zmiany w jednym składniku wymagają całkowitego ponownego testowania całej aplikacji i całkowitego ponownego wdrożenia wszystkich wystąpień.

Podejście monolityczne jest powszechne, a wiele organizacji opracowuje przy użyciu tego podejścia architektonicznego. Wiele z tych osób ma wystarczająco dobre wyniki, podczas gdy inni osiągają limity. Wiele osób zaprojektowało swoje aplikacje w tym modelu, ponieważ narzędzia i infrastruktura były zbyt skomplikowane do zbudowania architektur SOA i nie widziały potrzeby aż do momentu, gdy aplikacja się rozrosła. Jeśli okaże się, że osiągasz limity podejścia monolitycznego, podzielenie aplikacji w celu umożliwienia jej lepszego wykorzystania kontenerów i mikrousług może być następnym krokiem logicznym.

Rysunek 5–14

Wdrażanie aplikacji monolitycznych na platformie Microsoft Azure można wykonać przy użyciu dedykowanych maszyn wirtualnych dla każdego wystąpienia. Za pomocą usługi Azure Virtual Machine Scale Sets można łatwo skalować maszyny wirtualne. Usługi Azure App Services mogą uruchamiać aplikacje monolityczne i łatwo skalować wystąpienia bez konieczności zarządzania maszynami wirtualnymi. Usługi Azure App Services mogą również uruchamiać pojedyncze wystąpienia kontenerów platformy Docker, upraszczając wdrażanie. Za pomocą platformy Docker można wdrożyć pojedynczą maszynę wirtualną jako hosta platformy Docker i uruchomić wiele wystąpień. Korzystając z usługi Azure Balancer, jak pokazano na rysunku 5–14, możesz zarządzać skalowaniem.

Wdrożenie na różnych hostach można zarządzać przy użyciu tradycyjnych technik wdrażania. Hostami platformy Docker można zarządzać za pomocą poleceń, takich jak docker run, wykonywanych ręcznie, lub poprzez automatyzację, taką jak potoki ciągłego dostarczania (CD).

Aplikacja monolityczna wdrożona jako kontener

Istnieją korzyści wynikające z używania kontenerów do zarządzania wdrożeniami aplikacji monolitycznych. Skalowanie wystąpień kontenerów jest znacznie szybsze i łatwiejsze niż wdrażanie dodatkowych maszyn wirtualnych. Nawet w przypadku używania zestawów skalowania maszyn wirtualnych do skalowania maszyn wirtualnych tworzenie ich zajmuje trochę czasu. Po wdrożeniu jako instancje aplikacji konfiguracja aplikacji jest zarządzana jako część maszyny wirtualnej.

Wdrażanie aktualizacji jako obrazów Docker jest znacznie szybsze i bardziej efektywne pod względem sieci. Obrazy Dockera zwykle uruchamiają się w kilka sekund, co przyspiesza wdrożenia. Usunięcie instancji Dockera jest tak proste, jak wydanie polecenia docker stop, a proces zwykle kończy się w mniej niż sekundę.

Ponieważ kontenery są z założenia niezmienne zgodnie z projektem, nigdy nie musisz martwić się o uszkodzone maszyny wirtualne, podczas gdy skrypty aktualizacji mogą zapomnieć o określonej konfiguracji lub pliku pozostawionego na dysku.

Kontenery platformy Docker umożliwiają monolityczne wdrażanie prostszych aplikacji internetowych. Takie podejście usprawnia ciągłą integrację i potoki ciągłego wdrażania oraz pomaga osiągnąć sukces wdrożeń produkcyjnych. Nie ma więcej "Działa na mojej maszynie, dlaczego nie działa w środowisku produkcyjnym?"

Architektura oparta na mikrousługach ma wiele korzyści, ale te korzyści kosztują większą złożoność. W niektórych przypadkach koszty przewyższają korzyści, więc aplikacja wdrożenia monolitycznego uruchomiona w jednym kontenerze lub w kilku kontenerach jest lepszym rozwiązaniem.

Aplikacja monolityczna może nie być łatwo rozłożona na dobrze oddzielone mikrousługi. Mikrousługi powinny działać niezależnie od siebie, aby zapewnić bardziej odporną aplikację. Jeśli nie możesz dostarczać niezależnych fragmentów funkcji aplikacji, oddzielenie jej tylko zwiększa złożoność.

Aplikacja może jeszcze nie wymagać niezależnego skalowania funkcji. Wiele aplikacji, gdy konieczne jest skalowanie poza pojedyncze wystąpienie, może to zrobić za pomocą stosunkowo prostego procesu klonowania tego całego wystąpienia. Dodatkowa praca w celu oddzielenia aplikacji od dyskretnych usług zapewnia minimalną korzyść, gdy skalowanie pełnych wystąpień aplikacji jest proste i ekonomiczne.

Na wczesnym etapie tworzenia aplikacji może nie być jasne, gdzie znajdują się naturalne granice funkcjonalne. W miarę rozwoju minimalnego opłacalnego produktu naturalne rozdzielenie mogło jeszcze nie pojawić się. Niektóre z tych warunków mogą być tymczasowe. Możesz zacząć od utworzenia aplikacji monolitycznej, a później oddzielić niektóre funkcje, które mają zostać opracowane i wdrożone jako mikrousługi. Inne warunki mogą być istotne dla przestrzeni problemowej aplikacji, co oznacza, że aplikacja nigdy nie może być podzielona na wiele mikrousług.

Rozdzielenie aplikacji na wiele odrębnych procesów powoduje również narzut. Istnieje większa trudność w podziale funkcji na różne procesy. Protokoły komunikacyjne stają się bardziej złożone. Zamiast wywołań metod trzeba używać asynchronicznej komunikacji między usługami. Podczas przechodzenia do architektury mikrousług należy dodać wiele bloków konstrukcyjnych zaimplementowanych w wersji mikrousług aplikacji eShopOnContainers: obsługa magistrali zdarzeń, odporność komunikatów i ponawianie prób, spójność ostateczna i nie tylko.

Znacznie prostsza aplikacja referencyjna eShopOnWeb obsługuje użycie kontenerów jednokontenerowych monolitycznych. Aplikacja zawiera jedną aplikację internetową, która zawiera tradycyjne widoki MVC, internetowe interfejsy API i strony Razor. Opcjonalnie możesz uruchomić administracyjny składnik aplikacji oparty na Blazor, który wymaga uruchomienia oddzielnego projektu API.

Aplikację można uruchomić z poziomu katalogu głównego rozwiązania przy użyciu poleceń docker-compose build oraz docker-compose up. To polecenie konfiguruje kontener dla instancji webowej, z użyciem Dockerfile, znajdującego się w katalogu głównym projektu webowego, i uruchamia kontener na określonym porcie. Źródło tej aplikacji można pobrać z usługi GitHub i uruchomić ją lokalnie. Nawet ta monolityczna aplikacja zyskuje na wdrożeniu w środowisku kontenerowym.

W przypadku jednego wdrożenie konteneryzowane oznacza, że każde wystąpienie aplikacji działa w tym samym środowisku. Takie podejście obejmuje środowisko deweloperskie, w którym odbywa się wczesne testowanie i programowanie. Zespół deweloperów może uruchomić aplikację w środowisku konteneryzowanym zgodnym ze środowiskiem produkcyjnym.

Ponadto aplikacje kontenerowe skalują się poziomo przy niższych kosztach. Korzystanie ze środowiska kontenera umożliwia większe udostępnianie zasobów niż tradycyjne środowiska maszyn wirtualnych.

Na koniec konteneryzacja aplikacji wymusza rozdzielenie logiki biznesowej od serwera przechowywania. W miarę rozszerzania aplikacji wiele kontenerów będzie polegać na jednym fizycznym medium przechowywania. To medium magazynowe zazwyczaj jest serwerem zapewniającym wysoką dostępność, na którym działa baza danych programu SQL Server.

Obsługa platformy Docker

Projekt eShopOnWeb jest uruchamiany na platformie .NET. W związku z tym może działać w kontenerach opartych na systemie Linux lub Windows. Należy pamiętać, że w przypadku wdrożenia platformy Docker chcesz użyć tego samego typu hosta dla programu SQL Server. Kontenery oparte na systemie Linux pozwalają na mniejszy ślad i są bardziej preferowane.

Możesz użyć programu Visual Studio 2017 lub nowszego, aby dodać obsługę platformy Docker do istniejącej aplikacji, klikając prawym przyciskiem myszy projekt w Eksploratorze rozwiązań i wybierając polecenie Dodaj>obsługę platformy Docker. Ten krok powoduje dodanie wymaganych plików i zmodyfikowanie projektu w celu ich użycia. Obecna próbka eShopOnWeb już zawiera te pliki.

Plik na poziomie docker-compose.yml rozwiązania zawiera informacje o obrazach do skompilowania i kontenerach do uruchomienia. Plik umożliwia uruchamianie wielu aplikacji w tym samym czasie za pomocą docker-compose polecenia . W takim przypadku uruchamia tylko projekt internetowy. Można go również użyć do konfigurowania zależności, takich jak oddzielny kontener bazy danych.

version: '3'

services:
  eshopwebmvc:
    image: eshopwebmvc
    build:
      context: .
      dockerfile: src/Web/Dockerfile
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    ports:
      - "5106:5106"

networks:
  default:
    external:
      name: nat

Plik docker-compose.yml odwołuje się do Dockerfile w projekcie Web. Służy Dockerfile do określania, który kontener podstawowy będzie używany i jak aplikacja zostanie skonfigurowana. Element Web" : Dockerfile

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app

COPY *.sln .
COPY . .
WORKDIR /app/src/Web
RUN dotnet restore

RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/src/Web/out ./

ENTRYPOINT ["dotnet", "Web.dll"]

Rozwiązywanie problemów z platformą Docker

Po uruchomieniu konteneryzowanej aplikacji będzie ona nadal działać, dopóki nie zostanie zatrzymana. Możesz wyświetlić, które kontenery są uruchomione za pomocą polecenia docker ps. Możesz zatrzymać uruchomiony kontener przy użyciu docker stop polecenia i określić identyfikator kontenera.

Należy pamiętać, że uruchamianie kontenerów platformy Docker może być powiązane z portami, których można użyć w innym środowisku programistycznym. Jeśli spróbujesz uruchomić lub debugować aplikację przy użyciu tego samego portu co uruchomiony kontener platformy Docker, zostanie wyświetlony błąd informujący, że serwer nie może powiązać z tym portem. Po raz kolejny zatrzymanie kontenera powinno rozwiązać ten problem.

Jeśli chcesz dodać obsługę platformy Docker do aplikacji przy użyciu programu Visual Studio, upewnij się, że program Docker Desktop jest uruchomiony, gdy to zrobisz. Kreator nie zostanie uruchomiony poprawnie, jeśli program Docker Desktop nie jest uruchomiony podczas uruchamiania kreatora. Ponadto kreator sprawdza bieżący wybór kontenera, aby dodać poprawną obsługę platformy Docker. Jeśli chcesz dodać obsługę kontenerów systemu Windows, musisz uruchomić kreatora, gdy masz skonfigurowany program Docker Desktop z kontenerami systemu Windows. Jeśli chcesz dodać obsługę kontenerów Linux, uruchom kreatora, gdy masz Docker uruchomiony z skonfigurowanymi kontenerami Linux.

Inne style architektury aplikacji internetowej

  • Web-Queue-Worker: podstawowe składniki tej architektury to frontend webowy obsługujący żądania klientów oraz pracownik wykonujący zasobożerne zadania, długotrwałe przepływy pracy lub zadania wsadowe. Interfejs internetowy komunikuje się z pracownikiem za pośrednictwem kolejki komunikatów.
  • N-warstwowa: Architektura N-warstwowa dzieli aplikację na warstwy logiczne i warstwy fizyczne.
  • Mikrousługi: architektura mikrousług składa się z kolekcji małych, autonomicznych usług. Każda usługa jest samodzielna i powinna implementować pojedynczą funkcję biznesową w ograniczonym kontekście.

Odwołania — typowe architektury internetowe