Typowe architektury aplikacji internetowych

Porada

Ta zawartość jest fragmentem książki eBook, architekta nowoczesnych aplikacji internetowych z ASP.NET Core i platformą Azure, dostępnym na platformie .NET Docs lub jako bezpłatny plik PDF do pobrania, który można odczytać w trybie offline.

Architect Modern Web Applications with ASP.NET Core and Azure eBook cover thumbnail.

"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 .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 obsługuje wiele wewnętrznych i mniejszych aplikacji publicznych bardzo dobrze. Jednak nawet biorąc pod uwagę tę pojedynczą jednostkę wdrożenia, większość aplikacji biznesowych niebłodnych korzysta z pewnego logicznego rozdzielenia na kilka warstw.

Co to jest aplikacja monolityczna?

Aplikacja monolityczna jest taka, która jest całkowicie samodzielna, pod względem jego zachowania. Może on współdziałać 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.

Wszystkie 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 w Visual Studio lub z wiersza polecenia, zaczyna się jako prosty monolit "all-in-one". Zawiera wszystkie zachowania aplikacji, w tym prezentację, firmę i logikę dostępu do danych. Rysunek 5–1 przedstawia strukturę plików aplikacji jednoprojektowej.

A single project ASP.NET Core app

Rysunek 5–1. Aplikacja ASP.NET Core pojedynczego projektu.

W jednym scenariuszu projektu separacja obaw jest osiągana przy użyciu 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 Views, 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 rosnąć. Problemy dotyczące interfejsu użytkownika (modele, widoki, kontrolery) znajdują się w wielu folderach, które nie są grupowane alfabetycznie. Ten problem staje się gorszy tylko wtedy, gdy dodatkowe konstrukcje na poziomie interfejsu użytkownika, takie jak Filtry lub ModelBinders, są dodawane we własnych folderach. Logika biznesowa jest rozrzucona 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 logiczne?

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ą rozdzielenia zagadnień i może pomóc w organizowaniu rosnącej bazy kodu, dzięki czemu deweloperzy mogą ł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 można ponownie wykorzystać 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, na których warstwy mogą komunikować się z innymi warstwami. Ta architektura pomaga osiągnąć hermetyzację. Po zmianie lub zastąpieniu warstwy tylko te warstwy, które z nią współpracują, powinny mieć wpływ. 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) ułatwiają zastępowanie funkcji w aplikacji. Na przykład aplikacja może początkowo używać własnej bazy danych SQL Server do trwałości, ale później może użyć strategii trwałości opartej na chmurze lub jednej za internetowym interfejsem API. Jeśli aplikacja prawidłowo hermetyzowała implementację trwałości w warstwie logicznej, ta SQL Server warstwa specyficzna dla danej warstwy może zostać zastąpiona przez nową implementację tego samego interfejsu publicznego.

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 warstwach.

Uwaga

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 dość powszechne, aby mieć aplikację n-warstwową, która jest wdrażana w jednej warstwie.

Tradycyjne aplikacje architektury "N-Layer"

Najbardziej typową organizacją logiki aplikacji do warstw jest pokazana na rysunku 5–2.

Typical application layers

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 dal ani nie powinna bezpośrednio korzystać z trwałości za pośrednictwem innych środków. Podobnie BLL powinien współdziałać tylko z 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 w przypadku istnienia bazy danych). Testowanie logiki biznesowej w takiej architekturze jest często trudne, co wymaga testowej bazy danych. Reguła inwersji zależności może służyć do rozwiązania tego problemu, jak zobaczysz 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).

A simple monolithic application with three projects

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

Mimo że ta aplikacja używa kilku projektów do celów organizacyjnych, nadal jest 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.

Simple deployment of Azure Web App

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

W miarę zwiększania się potrzeb aplikacji mogą 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.

Deploying a web app to an Azure App Service

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

Wewnętrznie organizacja tego projektu w wielu projektach na podstawie odpowiedzialności zwiększa możliwości utrzymania aplikacji.

Tę jednostkę można skalować w górę lub w poziomie, aby korzystać ze skalowalności opartej na chmurze na żądanie. 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 w poziomie 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 App Service aplikacji. Rysunek 5–6 przedstawia odpowiedni ekran pulpitu nawigacyjnego platformy Azure w celu skonfigurowania liczby wystąpień obsługujących aplikację.

App Service Plan scaling in Azure

Rysunek 5–6. App Service skalowanie planu na platformie Azure.

Czysta architektura

Aplikacje zgodne z zasadą inwersji zależności, a także zasady projektowania Domain-Driven (DDD) mają tendencję do podobnego architektury. Ta architektura minęła przez wiele nazw od lat. Jedną z pierwszych nazw była sześciokątna architektura, a następnie Porty i karty. Ostatnio został on cytowany jako architektura cebuli lub czysta architektura. Ta ostatnia nazwa, Clean Architecture, jest używana jako nazwa tej architektury w tej książce elektronicznej.

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 NuGet.

Czysta architektura umieszcza logikę biznesową i model aplikacji w centrum aplikacji. Zamiast logiki biznesowej zależy od dostępu do danych lub innych problemów z infrastrukturą, ta zależność jest odwrócona: szczegóły infrastruktury i implementacji zależą od rdzenia aplikacji. Ta funkcja jest osiągana przez zdefiniowanie abstrakcji lub interfejsów w środowisku Application Core, które następnie są 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.

Clean Architecture; onion view

Rysunek 5–7. Czysta architektura; widok cebuli

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

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

Clean Architecture; horizontal layer view

Rysunek 5–8. Czysta architektura; widok warstwy poziomej

Zwróć uwagę, że stałe strzałki reprezentują zależności czasu kompilacji, a strzałka przerywana reprezentuje zależność tylko do środowiska uruchomieniowego. W przypadku czystej architektury warstwa interfejsu użytkownika współdziała z interfejsami zdefiniowanymi w środowisku Application Core 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 aplikacji, dlatego muszą być obecne i podłączone do interfejsów Application Core za pośrednictwem wstrzykiwania zależności.

Rysunek 5–9 przedstawia bardziej szczegółowy widok architektury aplikacji ASP.NET Core podczas tworzenia zgodnie z tymi zaleceniami.

ASP.NET Core architecture diagram following Clean Architecture

Rysunek 5–9. ASP.NET Core diagram architektury po czystej architekturze.

Ponieważ platforma Application Core 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. Testowanie jednostkowe aplikacji Core w izolacji.

IntegrationTests

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

Ponieważ warstwa interfejsu użytkownika nie ma bezpośredniej 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 użycie i obsługa wstrzykiwania zależności sprawia, że ta architektura jest najbardziej odpowiednia do struktury niebłychłych 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.

ASP.NET Core Architecture 2

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

Organizowanie kodu w czystej architekturze

W rozwiązaniu Clean Architecture 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.

Aplikacja Core

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, takiej 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 ani 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
  • Domain Services
  • Specyfikacje
  • Niestandardowe wyjątki i klauzule guard
  • Zdarzenia domeny i programy obsługi

Infrastruktura

Projekt Infrastruktura zwykle obejmuje implementacje dostępu do danych. W typowej aplikacji internetowej ASP.NET Core implementacje te obejmują dbContext (Entity Framework, EF) DbContext, wszystkie zdefiniowane obiekty EF Core Migration i klasy implementacji dostępu do danych. Najczęstszym sposobem abstrakcjonowania 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 interakcje z kwestiami infrastruktury. Te usługi powinny implementować interfejsy zdefiniowane w aplikacji Core, dlatego infrastruktura powinna mieć odwołanie do projektu Application Core.

Typy infrastruktury
  • Typy platformy 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 współdziałać z infrastrukturą za pośrednictwem interfejsów zdefiniowanych w aplikacji Core. W warstwie interfejsu użytkownika nie powinno być dozwolone bezpośrednie wystąpienie ani wywołania statyczne typów warstw infrastruktury.

Typy warstw interfejsu użytkownika
  • Kontrolery
  • Filtry niestandardowe
  • Niestandardowe oprogramowanie pośredniczące
  • Widoki
  • ViewModels
  • Uruchamianie

Plik Startup class lub Program.cs jest odpowiedzialny za konfigurowanie aplikacji i podłączanie typów implementacji do interfejsów. Miejsce, w którym jest wykonywana ta logika, jest nazywane katalogiem głównym kompozycji aplikacji i umożliwia prawidłowe działanie wstrzykiwania zależności w czasie wykonywania.

Uwaga

Aby połączyć iniekcję zależności podczas uruchamiania aplikacji, projekt warstwy interfejsu użytkownika może wymagać odwołania się do projektu infrastruktury. Tę zależność można wyeliminować z łatwością, korzystając z 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 interfejsu użytkownika odwołania do projektu infrastruktury (ale deweloperzy powinni ograniczyć rzeczywiste odwołania do typów w projekcie Infrastruktura do katalogu głównego kompozycji aplikacji).

Monolityczne aplikacje 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 jednym 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 wynika z zarządzania pojedynczym wdrożeniem w jednym kontenerze lub maszynie wirtualnej.

Figure 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 po zasadzie kontenera "kontener robi jedną rzecz i robi to w jednym procesie", wzorzec monolityczny może być konfliktem.

Wadą tego podejścia jest to, czy/kiedy aplikacja rośnie, wymagając jej skalowania. Jeśli cała aplikacja jest skalowana, to naprawdę nie jest to problem. Jednak w większości przypadków kilka części aplikacji to punkty dławiające wymagające 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 kupować. 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órzy 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 rozwija się przy użyciu tego podejścia architektonicznego. Wielu ma wystarczająco dobre wyniki, podczas gdy inni osiągają limity. Wiele z nich zaprojektowało swoje aplikacje w tym modelu, ponieważ narzędzia i infrastruktura były zbyt trudne do tworzenia architektur zorientowanych na usługi (SOA) i nie widziały potrzeby aż do wzrostu aplikacji. 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.

Figure 5-14

Wdrażanie aplikacji monolitycznych w Microsoft Azure można osiągnąć przy użyciu dedykowanych maszyn wirtualnych dla każdego wystąpienia. Korzystając z usługi Azure Virtual Machine Scale Sets, można łatwo skalować maszyny wirtualne. aplikacja systemu Azure Services mogą uruchamiać aplikacje monolityczne i łatwo skalować wystąpienia bez konieczności zarządzania maszynami wirtualnymi. aplikacja systemu Azure Services mogą również uruchamiać pojedyncze wystąpienia kontenerów platformy Docker, upraszczając wdrażanie. Za pomocą platformy Docker można wdrożyć jedną 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żna zarządzać skalowaniem.

Wdrożenie na różnych hostach można zarządzać przy użyciu tradycyjnych technik wdrażania. Hosty platformy Docker można zarządzać za pomocą poleceń, takich jak uruchamianie platformy Docker wykonywane ręcznie, lub za pomocą automatyzacji, takich jak potoki ciągłego dostarczania (CD).

Aplikacja monolityczna wdrożona jako kontener

Korzystanie z kontenerów umożliwia zarządzanie 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 wystąpienia aplikacji konfiguracja aplikacji jest zarządzana jako część maszyny wirtualnej.

Wdrażanie aktualizacji, ponieważ obrazy platformy Docker są znacznie szybsze i wydajne w sieci. Obrazy platformy Docker zwykle rozpoczynają się w sekundach, przyspieszając wprowadzanie. Usunięcie wystąpienia platformy Docker jest tak proste, jak wykonanie docker stop polecenia, zwykle trwa mniej niż sekundę.

Ponieważ kontenery są z natury niezmienne zgodnie z projektem, nigdy nie trzeba martwić się o uszkodzone maszyny wirtualne, podczas gdy skrypty aktualizacji mogą zapominać o uwzględnieniu 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 wdrażania w środowisku produkcyjnym. Nie ma więcej "To 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 są kosztem zwiększonej złożoności. 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 dostarczyć niezależnych wycinków funkcji aplikacji, oddzielenie jej tylko zwiększa złożoność.

Aplikacja może nie być jeszcze potrzebna do niezależnego skalowania funkcji. Wiele aplikacji, gdy zachodzi potrzeba skalowania poza pojedyncze wystąpienie, może to zrobić za pomocą stosunkowo prostego procesu klonowania 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żesz nie mieć jasnego pojęcia, gdzie znajdują się naturalne granice funkcjonalne. W miarę rozwoju minimalnego opłacalnego produktu naturalne rozdzielenie może 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 złożoność oddzielania funkcji do różnych procesów. Protokoły komunikacyjne stają się bardziej złożone. Zamiast wywołań metod należy użyć asynchronicznej komunikacji między usługami. W miarę przechodzenia do architektury mikrousług należy dodać wiele bloków konstrukcyjnych implementowanych 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 monolitycznych z jednym kontenerem. Aplikacja zawiera jedną aplikację internetową, która obejmuje tradycyjne widoki MVC, internetowe interfejsy API i strony Razor. Opcjonalnie możesz uruchomić składnik administracyjny oparty na platformie Blazor aplikacji, który wymaga również uruchomienia oddzielnego projektu interfejsu API.

Aplikację można uruchomić z poziomu katalogu głównego rozwiązania przy użyciu docker-compose build poleceń i docker-compose up . To polecenie konfiguruje kontener dla wystąpienia internetowego przy użyciu Dockerfile elementu znajdującego się w katalogu głównym projektu internetowego i uruchamia kontener na określonym porcie. Źródło tej aplikacji można pobrać z GitHub i uruchomić ją lokalnie. Nawet ta monolityczna aplikacja korzysta z wdrażania w środowisku kontenera.

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, które jest zgodne ze środowiskiem produkcyjnym.

Ponadto konteneryzowane aplikacje są skalowane w poziomie przy niższych kosztach. Korzystanie ze środowiska kontenera umożliwia większe udostępnianie zasobów niż tradycyjne środowiska maszyn wirtualnych.

Na koniec konteneryzowanie aplikacji wymusza rozdzielenie logiki biznesowej i serwera magazynu. W miarę skalowania aplikacji wiele kontenerów będzie polegać na jednym fizycznym nośniku magazynu. Ten nośnik magazynu zazwyczaj jest serwerem o wysokiej dostępności z uruchomioną bazą danych SQL Server.

Obsługa platformy Docker

Projekt eShopOnWeb działa 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 SQL Server. Kontenery oparte na systemie Linux umożliwiają mniejsze zużycie i są 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 Eksplorator rozwiązań i wybierając pozycję AddDocker Support (Obsługa platformy AddDocker>). Ten krok powoduje dodanie wymaganych plików i zmodyfikowanie projektu w celu ich użycia. Bieżący eShopOnWeb przykład zawiera już 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 pliku w projekcie Web . Element służy do określania Dockerfile , który kontener podstawowy będzie używany i jak aplikacja zostanie skonfigurowana. Element Web" Dockerfile:

FROM mcr.microsoft.com/dotnet/sdk:6.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:6.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ć do momentu jej zatrzymania. Możesz wyświetlić, które kontenery są uruchomione za docker ps pomocą polecenia . Uruchomiony kontener można zatrzymać przy użyciu docker stop polecenia i określić identyfikator kontenera.

Pamiętaj, że uruchamianie kontenerów platformy Docker może być powiązane z portami, których w przeciwnym razie spróbujesz użyć w środowisku deweloperów. Jeśli spróbujesz uruchomić lub debugować aplikację przy użyciu tego samego portu co uruchomiony kontener platformy Docker, zostanie wyświetlony komunikat o błędzie informujący, że serwer nie może powiązać z tym portem. Po raz kolejny zatrzymanie kontenera powinno rozwiązać problem.

Jeśli chcesz dodać obsługę platformy Docker do aplikacji przy użyciu 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 po uruchomieniu kreatora. Ponadto kreator sprawdza bieżący wybór kontenera, aby dodać poprawną obsługę platformy Docker. Jeśli chcesz dodać obsługę kontenerów Windows, musisz uruchomić kreatora, gdy program Docker Desktop jest uruchomiony z usługą Windows Containers. Jeśli chcesz dodać kontenery systemu Linux, uruchom kreatora, gdy masz skonfigurowaną platformę Docker z kontenerami systemu Linux.

Odwołania — typowe architektury internetowe