Typowe architektury aplikacji internetowych

"Jeśli uważasz, że dobra architektura jest kosztowna, wypróbuj złą architekturę". — Brian Foote i Joseph Yoder

Większość tradycyjnych aplikacji .NET jest wdrażanych jako pojedyncze jednostki odpowiadające plikowi wykonywalneowi lub pojedynczej aplikacji internetowej działającej w ramach jednej domeny aplikacji usług IIS. Jest to najprostszy model wdrażania, który bardzo dobrze obsługuje wiele wewnętrznych i mniejszych aplikacji publicznych. Jednak nawet biorąc pod uwagę tę pojedynczą jednostkę wdrożenia, większość nieszybkich aplikacji biznesowych korzysta z pewnego logicznego rozdzielenia na kilka warstw.

Co to jest aplikacja monolityczna?

Aplikacja monolityczna to aplikacja całkowicie samodzielna pod względem jej zachowania. Może ona wchodzić w interakcje z innymi usługami lub magazynami danych w trakcie wykonywania operacji, ale rdzeń jego zachowania działa w ramach własnego procesu, a cała aplikacja jest zwykle wdrażana jako pojedyncza jednostka. Jeśli taka aplikacja musi być skalowana w poziomie, zwykle cała aplikacja jest duplikowana na wielu serwerach lub maszynach wirtualnych.

Aplikacje typu "wszystko w jednym"

Najmniejsza możliwa liczba projektów dla architektury aplikacji to jeden. W tej architekturze cała logika aplikacji jest zawarta w jednym projekcie, skompilowana do jednego zestawu i wdrożona jako pojedyncza jednostka.

Nowy projekt ASP.NET Core, utworzony w programie Visual Studio lub z wiersza polecenia, rozpoczyna się od prostego monolitu "wszystko w jednym". Zawiera ona całe zachowanie aplikacji, w tym prezentację, logikę biznesową i logikę dostępu do danych. Rysunek 5–1 przedstawia strukturę plików aplikacji pojedynczego projektu.

Pojedyncza aplikacja ASP.NET Core projektu

Rysunek 5–1. Pojedynczy projekt ASP.NET Core aplikacji.

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 takim przypadku szczegóły prezentacji powinny być jak najbardziej ograniczone 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 Models.

Chociaż rozwiązanie monolityczne jednego projektu jest proste, ma pewne wady. Wraz ze wzrostem rozmiaru i złożoności projektu liczba plików i folderów również będzie się zwiększać. Problemy z interfejsem 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 spaghghg code.

Aby rozwiązać te problemy, aplikacje często ewoluują w rozwiązania wielo projektowe, w których każdy projekt jest traktowany jako znajdować się w określonej warstwie aplikacji.

Co to są warstwy logiczne?

Gdy złożoność aplikacji rośnie, jednym ze sposobów zarządzania tym złożonością jest podział aplikacji zgodnie z jej obowiązkami lub problemami. Takie podejście jest zgodna z zasadą separacji problemów i może pomóc w uporządkowaniu rosnącej bazy kodu, dzięki czemu deweloperzy mogą łatwo znaleźć miejsca, w których zaimplementowano pewne funkcje. Jednak architektura warstwowa ma wiele zalet poza organizacją kodu.

Dzięki zorganizowaniu kodu w warstwy można ponownie używać typowych funkcji niskiego poziomu w całej aplikacji. To ponowne użycie jest korzystne, ponieważ oznacza, że należy napisanych mniej kodu, a ponieważ może to umożliwić aplikacji standaryzację w ramach jednej implementacji, zgodnie z zasadą nie powtarzaj się samodzielnie (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ę. Gdy warstwa zostanie zmieniona lub zastąpiona, będzie to miało wpływ tylko na te warstwy, które z nim pracują. Ograniczając, które warstwy zależą od innych warstw, można ograniczyć wpływ zmian, tak aby pojedyncza 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 utrwalania, 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 hermetyzuje swoją implementację trwałości w warstwie logicznej, SQL Server warstwie specyficznej dla tej warstwy można zastąpić nową, implementując 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 testu fałszywymi implementacjami, które zapewniają znane odpowiedzi na żądania. Takie podejście zwykle znacznie ułatwia pisanie testów i znacznie przyspiesza ich uruchamianie w porównaniu z uruchamianiem testów w rzeczywistej infrastrukturze aplikacji.

Warstwy logiczne to powszechna technika ulepszania organizacji kodu w aplikacjach dla przedsiębiorstw. Istnieje kilka sposobów, w jakie kod może być zorganizowany w warstwy.

Uwaga

Warstwy reprezentują separację logiczną w aplikacji. Jeśli logika aplikacji jest fizycznie dystrybuowana do oddzielnych serwerów lub procesów, te oddzielne fizyczne cele wdrożenia są określane jako warstwy. Często istnieje możliwość wdrożenia aplikacji N-warstwowej w jednej warstwie.

Tradycyjne aplikacje architektury "N-warstwowej"

Najbardziej powszechną organizację logiki aplikacji w warstwy pokazano na rysunku 5–2.

Typowe warstwy aplikacji

Rysunek 5–2. Typowe warstwy aplikacji.

Te warstwy są często skracane jako INTERFEJS UŻYTKOWNIKA, BLL (Business Logic Layer) i DAL (Warstwa dostępu do danych). Korzystając z tej architektury, użytkownicy mogą żądać za pośrednictwem warstwy interfejsu użytkownika, która współdziała tylko z warstwą BLL. Z kolei program BLL może wywołać usługę DAL dla żądań dostępu do danych. Warstwa interfejsu użytkownika nie powinna bezpośrednio zwracać żądań do warstwy DAL ani bezpośrednio wchodzić w interakcje z trwałością za pośrednictwem innych środków. Podobnie logiki logiki należy korzystać 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 do warstw jest to, że zależności w czasie kompilacji są uruchamiane od góry do dołu. Oznacza to, że warstwa interfejsu użytkownika zależy od warstwy logiki aplikacji, która zależy od warstwy DAL. Oznacza to, że logiki BLL, która zwykle przechowuje najważniejsze logiki w aplikacji, zależy 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. Zasada odwrócenia 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 rozbija 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.

Chociaż ta aplikacja używa kilku projektów do celów organizacyjnych, jest nadal wdrażana jako pojedyncza jednostka, a jej klienci będą wchodzić z nim w interakcje 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 przypadku wzrostu potrzeb aplikacji mogą być wymagane bardziej złożone i niezawodne rozwiązania do wdrażania. Rysunek 5–5 przedstawia przykład bardziej złożonego planu wdrożenia, który obsługuje dodatkowe możliwości.

Wdrażanie aplikacji internetowej w Azure App Service

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

Wewnętrznie organizacja tego projektu w wielu projektach opartych na odpowiedzialności zwiększa czytelność aplikacji.

Tę jednostkę można skalować w górę lub w górę, 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 hostjących aplikację. Skalowanie w zewnątrz oznacza dodawanie 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, do przypisywania żądań do poszczególnych wystąpień aplikacji jest używany równoważenie obciążenia.

Najprostszym podejściem do skalowania aplikacji internetowej na platformie Azure jest ręczne skonfigurowanie skalowania w ramach planu App Service aplikacji. Rysunek 5–6 przedstawia odpowiedni ekran pulpitu nawigacyjnego platformy Azure służący do konfigurowania liczby wystąpień obsługujących aplikację.

App Service planowanie skalowania na platformie Azure

Rysunek 5–6. App Service planowanie skalowania na platformie Azure.

Czysta architektura

Aplikacje, które są zgodne z zasadą odwrócenia zależności, a także zasadami projektowania Domain-Driven (DDD), zwykle mają podobną architekturę. Ta architektura przez lata była pod wieloma nazwami. Jedną z pierwszych nazw była Architektura sześciokątna, a po niej porty i karty. Niedawno był on cytowany jako Architektura onionu lub Czysta architektura. Druga nazwa, Clean Architecture(Czysta architektura), jest używana jako nazwa tej architektury w tej książce elektronicznej.

Aplikacja referencyjna eShopOnWeb używa metody Czystej architektury do organizowania swojego kodu w projekty. Szablon rozwiązania, który może być punktem wyjścia dla własnego ASP.NET Core, można znaleźć w repozytorium ardalis/cleanarchitecture GitHub repozytorium.

Czysta architektura umieszcza logikę biznesową i model aplikacji w centrum aplikacji. Logika biznesowa nie zależy od dostępu do danych lub innych kwestii związanych z infrastrukturą, ale jest odwrócona: szczegóły infrastruktury i implementacji zależą od usługi Application Core. Ta funkcja jest osiągana przez zdefiniowanie abstrakcji (interfejsów) w rdzeniu aplikacji, które są następnie implementowane przez typy zdefiniowane w warstwie infrastruktury. Częstym sposobem wizualizowania tej architektury jest użycie serii koncentrycznych okręgów, podobnych do onionu. Rysunek 5–7 przedstawia przykład tego stylu reprezentacji architektury.

Czysta architektura; widok onion

Rysunek 5–7. Czysta architektura; widok onion

Na tym diagramie zależności przepływają do najbardziej wewnętrznego okręgu. Nazwa aplikacji Core pochodzi od pozycji w rdzeniu tego diagramu. Na diagramie widać, że program Application Core nie ma zależności od innych warstw aplikacji. Jednostki i interfejsy aplikacji znajdują się na samym środku. Tuż poza, ale nadal w programie Application Core, są usługi domenowe, które zwykle implementują interfejsy zdefiniowane w wewnętrznym kole. Poza bazą danych Application Core zarówno warstwa interfejsu użytkownika, jak i warstwy infrastruktury zależą od rdzenia aplikacji, ale nie od siebie nawzajem (niekoniecznie).

Rysunek 5–8 przedstawia bardziej tradycyjny poziomy diagram warstwowy, 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

Zwróć uwagę, że pełne strzałki reprezentują zależności w czasie kompilacji, natomiast strzałka przerywana reprezentuje zależność tylko w czasie wykonywania. W przypadku czystej architektury warstwa interfejsu użytkownika współpracuje z interfejsami zdefiniowanymi w rdzeniu aplikacji w czasie kompilacji i najlepiej nie powinna wiedzieć o typach implementacji zdefiniowanych w warstwie Infrastruktura. Jednak w czasie wykonywania te typy implementacji są wymagane do wykonania aplikacji, więc muszą być obecne i podłączone do interfejsów Usługi Application Core za pośrednictwem wstrzykiwania zależności.

Rysunek 5–9 przedstawia bardziej szczegółowy widok architektury ASP.NET Core, gdy jest budowaną zgodnie z tymi zaleceniami.

ASP.NET Core diagram architektury po czystej architekturze

Rysunek 5–9. ASP.NET Core diagram architektury następujący po opisie clean architecture (Czysta architektura).

Ponieważ podstawowe funkcje aplikacji nie zależą od infrastruktury, pisanie zautomatyzowanych testów jednostkowych dla tej warstwy jest bardzo proste. Wartości 5–10 i 5–11 pokazują, jak testy mieszczą się w tej architekturze.

UnitTestCore

Rysunek 5–10. Testowanie jednostek w odizolowaniu aplikacji Application Core.

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 Infrastruktury, podobnie bardzo łatwo jest zamienić implementacje, aby ułatwić testowanie lub w odpowiedzi na zmieniające się wymagania aplikacji. ASP.NET Core wbudowanym użyciu wstrzykiwania zależności i obsłudze tej architektury sprawia, że jest to najbardziej odpowiedni sposób struktury nie trywialnych 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 Architektura 2

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

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 w odpowiednim projekcie często znajdują się foldery odpowiadające tym typom.

Application Core

Aplikacja Application Core przechowuje model biznesowy, który obejmuje jednostki, usługi i interfejsy. Te interfejsy obejmują abstrakcje dla 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 innymi 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 podstawowe aplikacji
  • Jednostki (klasy modelu biznesowego, które są utrwalane)
  • Interfejsy
  • Usługi
  • Jednostki DTO

Infrastruktura

Projekt Infrastruktura zwykle obejmuje implementacje dostępu do danych. W typowej ASP.NET Core internetowej te implementacje obejmują obiekt DbContext programu Entity Framework (EF), wszystkie zdefiniowane obiekty EF Core oraz klasy implementacji dostępu do Migration danych. Najczęstszym sposobem abstrakcji 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 problemami dotyczącymi infrastruktury. Te usługi powinny implementować interfejsy zdefiniowane w programie Application Core, a więc infrastruktura powinna mieć odwołanie do projektu Application Core.

Typy infrastruktury
  • EF Core typów ( 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 wchodzić w interakcje z infrastrukturą wyłącznie za pośrednictwem interfejsów zdefiniowanych w programie Application Core. W warstwie interfejsu użytkownika nie powinno być dozwolone bezpośrednie ani statyczne wywołania typów warstw infrastruktury.

Typy warstw interfejsu użytkownika
  • Kontrolery
  • Filtry
  • Widoki
  • ViewModels (Modele widoków)
  • Uruchamianie

Klasa Startup jest odpowiedzialna za konfigurowanie aplikacji i podłączanie typów implementacji do interfejsów, dzięki czemu wstrzykiwanie zależności działa prawidłowo w czasie wykonywania.

Uwaga

Aby zapewnić przewodowe wstrzykiwanie zależności w pliku ConfigureServices w pliku Startup.cs projektu interfejsu użytkownika, projekt może wymagać odwołania się do projektu Infrastruktury. Tę zależność można wyeliminować, najlepiej przy użyciu niestandardowego kontenera wstrzykiwania zależności. Na potrzeby tego przykładu najprostszym podejściem jest umożliwienie projektowi interfejsu użytkownika odwołania się do projektu infrastruktury.

Aplikacje monolityczne i kontenery

Możesz utworzyć pojedynczą aplikację internetową lub usługę opartą na monolicie i wdrożyć ją jako kontener. W aplikacji może nie być monolityczna, ale zorganizowana w kilka bibliotek, składników lub warstw. Na zewnątrz jest to pojedynczy kontener, taki jak pojedynczy proces, pojedyncza aplikacja internetowa lub pojedyncza usługa.

Aby zarządzać tym modelem, należy wdrożyć jeden kontener reprezentujący aplikację. Aby skalować, wystarczy dodać dodatkowe kopie z usługą równoważenia obciążenia z przodu. Prostota to zarządzanie 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 robi jedno i robi to w jednym procesie", wzorzec monolityczny może być konfliktem.

Minusem tego podejścia jest to, jeśli/kiedy aplikacja rośnie, co wymaga jej skalowania. Skalowanie całej aplikacji nie jest problemem. Jednak w większości przypadków kilka części aplikacji to punkty chłoniaka wymagające skalowania, podczas gdy inne składniki są używane mniej.

Korzystając z typowego przykładu handlu elektronicznego, prawdopodobnie musisz skalować składnik informacji o produkcie. Wielu klientów przegląda produkty, a nie je kupuje. Coraz więcej klientów korzysta ze swojego koszyka, niż korzysta z potoku płatności. Mniejsza liczba klientów dodaje komentarze lub wyświetla historię zakupów. Prawdopodobnie tylko kilku pracowników w jednym regionie musi zarządzać zawartością i kampaniami marketingowymi. Skalując projekt monolityczny, cały kod jest wdrażany wiele razy.

Oprócz problemu "skalowania wszystkiego" zmiany w jednym składniku wymagają pełnego ponownego testowania całej aplikacji i pełnego ponownego wykonania wszystkich wystąpień.

Monolityczne podejście jest powszechne, a wiele organizacji opracowuje takie podejście architektoniczne. Wiele z nich ma wystarczająco dobre wyniki, podczas gdy inne mają limity. Wiele osób zaprojektowało swoje aplikacje w tym modelu, ponieważ narzędzia i infrastruktura były zbyt trudne do tworzenia architektur zorientowanych na usługi (SOA, service-oriented architecture), i nie widziały potrzeby, dopóki aplikacja się nie rozrośnie. Jeśli okazuje się, że zbliżasz się do limitów podejścia monolitycznego, następnym logicznym krokiem może być rozbicie aplikacji w celu umożliwienia jej lepszego wykorzystania kontenerów i mikrousług.

Rysunek 5–14

Wdrażanie aplikacji monolitycznych w programie Microsoft Azure można osiągnąć przy użyciu dedykowanych maszyn wirtualnych dla każdego wystąpienia. Za pomocą usługi Azure Virtual Machine Scale Setsmożna łatwo skalować maszyny wirtualne. Usługa Azure App Services może uruchamiać aplikacje monolityczne i łatwo skalować wystąpienia bez konieczności zarządzania maszynami wirtualnych. Usługa Azure App Services może 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ń. Za pomocą usługi Azure Balancer, jak pokazano na rysunku 5–14, można zarządzać skalowaniem.

Wdrożeniem 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 uruchamianie platformy Docker, wykonywane ręcznie lub za pośrednictwem automatyzacji, takiej jak potoki ciągłego dostarczania (CD).

Aplikacja monolityczna wdrożona jako kontener

Korzystanie z kontenerów do zarządzania wdrożeniami aplikacji monolitycznych ma wiele zalet. 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 ich wystąpienie może zająć trochę czasu. Po wdrożeniu jako wystąpienia aplikacji konfiguracja aplikacji jest zarządzana jako część maszyny wirtualnej.

Wdrażanie aktualizacji jako obrazów platformy Docker jest znacznie szybsze i wydajne w sieci. Obrazy platformy Docker zwykle zaczynają się w ciągu kilku sekund, przyspieszając ich uruchamianie. Wyeliminowanie wystąpienia platformy Docker jest tak proste, jak wykonanie polecenia, zwykle trwa docker stop to krócej niż sekundę.

Kontenery są z założenia niezmienne, dlatego nigdy nie trzeba martwić się o uszkodzone maszyny wirtualne, podczas gdy skrypty aktualizacji mogą zapomnieć o uwzględnieniu konkretnej konfiguracji lub pliku pozostawionych na dysku.

Kontenerów platformy Docker można używać do monolitycznego wdrażania prostszych aplikacji internetowych. Takie podejście usprawnia potoki ciągłej integracji i ciągłego wdrażania oraz pomaga osiągnąć sukces wdrożenia w środowisku produkcyjnym. No more "It works on my machine, why does does not work in production?" (Dlaczego nie działa w środowisku produkcyjnym?

Architektura oparta na mikrousługach ma wiele zalet, ale te korzyści są związane ze zwiększoną złożonością. W niektórych przypadkach koszty przeważają nad korzyściami, więc monolityczna aplikacja wdrażania uruchomiona w jednym kontenerze lub w kilku kontenerach jest lepszym rozwiązaniem.

Aplikacji monolitycznej nie można łatwo rozdzielić na dobrze oddzielone mikrousługi. Mikrousługi powinny działać niezależnie od siebie, aby zapewnić bardziej odporną aplikację. Jeśli nie można dostarczać niezależnych wycinków funkcji aplikacji, oddzielenie go tylko zwiększa złożoność.

Aplikacja może jeszcze nie wymagać niezależnego skalowania funkcji. Wiele aplikacji, które wymagają skalowania poza pojedyncze wystąpienie, może to zrobić za pośrednictwem stosunkowo prostego procesu klonowania całego wystąpienia. Dodatkowa praca w celu oddzielenia aplikacji na odrębne usługi zapewnia minimalną korzyść, gdy skalowanie pełnych wystąpień aplikacji jest proste i ekonomiczne.

Na wczesnym etapie opracowywania aplikacji może nie być jasne, gdzie znajdują się naturalne granice funkcjonalne. Podczas opracowywania minimalnego koniecznego produktu naturalna separacja mogła jeszcze nie powstać. Niektóre z tych warunków mogą być tymczasowe. Możesz zacząć od utworzenia aplikacji monolitycznej, a następnie oddzielić niektóre funkcje, które mają zostać opracowane i wdrożone jako mikrousługi. Inne warunki mogą być istotne dla obszaru problemów aplikacji, co oznacza, że aplikacja nigdy nie może być łamana na wiele mikrousług.

Rozdzielenie aplikacji na wiele odrębnych procesów również wprowadza narzut. Rozdzielenie funkcji na różne procesy jest bardziej skomplikowane. Protokoły komunikacyjne stają się bardziej złożone. Zamiast wywołań metod należy użyć komunikacji asynchronicznej między usługami. W przypadku przechodzenia do architektury mikrousług należy dodać wiele bloków konstrukcyjnych zaimplementowanych w wersji mikrousług aplikacji eShopOnContainers: obsługę magistrali zdarzeń, odporność i ponowne próby komunikatów, spójność ostateczną i nie tylko.

Znacznie prostsza aplikacja referencyjna eShopOnWeb obsługuje monolityczne użycie kontenera pojedynczego kontenera. Aplikacja zawiera jedną aplikację internetową, która zawiera tradycyjne widoki MVC, internetowe interfejsy API i Razor Pages. Tę aplikację można uruchomić z katalogu głównego rozwiązania przy użyciu docker-compose build poleceń docker-compose up i . To polecenie konfiguruje kontener dla wystąpienia sieci Web przy użyciu pliku znalezionego w katalogu głównym projektu internetowego i uruchamia kontener Dockerfile na określonym porcie. Źródło tej aplikacji można pobrać ze strony GitHub i uruchomić ją lokalnie. Nawet ta monolityczna aplikacja korzysta z wdrażania w środowisku kontenera.

Po pierwsze wdrożenie konteneryzowane oznacza, że każde wystąpienie aplikacji działa w tym samym środowisku. Takie podejście obejmuje środowisko dewelopera, w którym odbywa się wczesne testowanie i opracowywanie. Zespół programistów może uruchomić aplikację w środowisku konteneryzowym, które odpowiada środowisku produkcyjnemu.

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

Konteneryzowanie aplikacji wymusza oddzielenie logiki biznesowej od serwera magazynu. W przypadku skalowania aplikacji w zewnątrz wszystkie kontenery będą polegać na jednym fizycznym nośniku magazynu. Ten nośnik magazynowania będzie zazwyczaj serwerem o wysokiej dostępności, na którym działa SQL Server bazy danych.

Obsługa platformy Docker

Projekt eShopOnWeb działa na .NET. W związku z tym może działać w kontenerach opartych na systemie Linux Windows opartych na systemie Linux. 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 programie Eksplorator rozwiązań i wybierając pozycję Dodaj obsługę platformy > Docker. Ten krok powoduje dodanie wymaganych plików i zmodyfikuje projekt w celu ich użycia. W bieżącym eShopOnWeb przykładzie te pliki są już zainstalowane.

Plik na poziomie rozwiązania zawiera informacje o obrazach do docker-compose.yml skompilowania i kontenerach do uruchomienia. Plik umożliwia uruchamianie wielu aplikacji w tym samym czasie za pomocą docker-compose polecenia . W tym przypadku uruchamia tylko projekt internetowy. Można go również używać 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 pliku w Dockerfile Web projekcie . Wartość służy do określania, który kontener podstawowy będzie używany i jak aplikacja zostanie w nim Dockerfile skonfigurowana. The Web ' Dockerfile :

FROM mcr.microsoft.com/dotnet/sdk:5.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:5.0 AS runtime
WORKDIR /app
COPY --from=build /app/src/Web/out ./

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

Rozwiązywanie problemów z platformy Docker

Po uruchomieniu aplikacji konteneryzowanej będzie ona działać do momentu jej zatrzymania. Aby sprawdzić, które kontenery są uruchomione, możesz użyć docker ps polecenia . Uruchomiony kontener można zatrzymać za pomocą docker stop polecenia i określając 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 środowisku dewelopera. Jeśli spróbujesz uruchomić lub debugować aplikację przy użyciu tego samego portu co uruchomiony kontener platformy Docker, zostanie wyświetlany komunikat o błędzie informujący, że serwer nie może powiązać z tym portem. Ponownie 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. Kreator nie będzie działać prawidłowo, jeśli program Docker Desktop nie jest uruchomiony po uruchomieniu kreatora. Ponadto kreator sprawdza bieżący wybór kontenera w celu dodania poprawnej obsługi platformy Docker. Jeśli chcesz dodać obsługę kontenerów usługi Windows, musisz uruchomić kreatora, gdy masz program Docker Desktop uruchomiony ze skonfigurowanymi kontenerami Windows kontenerów. Jeśli chcesz dodać obsługę kontenerów systemu Linux, uruchom kreatora, gdy masz skonfigurowaną platformę Docker ze skonfigurowanymi kontenerami systemu Linux.

Odwołania — typowe architektury internetowe