Share via


Algemene webtoepassingsarchitecturen

Tip

Deze inhoud is een fragment uit het eBook, Architect Modern Web Applications met ASP.NET Core en Azure, beschikbaar op .NET Docs of als een gratis downloadbare PDF die offline kan worden gelezen.

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

"Als u denkt dat goede architectuur duur is, probeer dan een slechte architectuur." - Brian Foote en Joseph Yoder

De meeste traditionele .NET-toepassingen worden geïmplementeerd als één eenheid die overeenkomt met een uitvoerbaar bestand of één webtoepassing die wordt uitgevoerd binnen één IIS-appdomein. Deze benadering is het eenvoudigste implementatiemodel en dient zeer goed voor veel interne en kleinere openbare toepassingen. Zelfs gezien deze enkele implementatie-eenheid profiteren de meeste niet-triviale bedrijfstoepassingen echter van een logische scheiding in verschillende lagen.

Wat is een monolithische toepassing?

Een monolithische toepassing is een toepassing die volledig zelfstandig is, wat betreft het gedrag ervan. Het kan communiceren met andere services of gegevensarchieven tijdens het uitvoeren van de bewerkingen, maar de kern van het gedrag wordt uitgevoerd binnen een eigen proces en de hele toepassing wordt doorgaans geïmplementeerd als één eenheid. Als een dergelijke toepassing horizontaal moet worden geschaald, wordt de hele toepassing meestal gedupliceerd op meerdere servers of virtuele machines.

Alles-in-één-toepassingen

Het kleinste mogelijke aantal projecten voor een toepassingsarchitectuur is er een. In deze architectuur bevindt de volledige logica van de toepassing zich in één project, gecompileerd naar één assembly en geïmplementeerd als één eenheid.

Een nieuw ASP.NET Core-project, of dit nu is gemaakt in Visual Studio of vanaf de opdrachtregel, begint als een eenvoudige 'all-in-one'-monolith. Het bevat alle werking van de toepassing, waaronder presentatie-, zakelijke en gegevenstoegangslogica. Afbeelding 5-1 toont de bestandsstructuur van een app met één project.

A single project ASP.NET Core app

Afbeelding 5-1. Eén project ASP.NET Core-app.

In één projectscenario wordt de scheiding van problemen bereikt door het gebruik van mappen. De standaardsjabloon bevat afzonderlijke mappen voor MVC-patroonverantwoordelijkheden van modellen, weergaven en controllers, evenals aanvullende mappen voor gegevens en services. In deze rangschikking moeten presentatiedetails zoveel mogelijk worden beperkt tot de map Weergaven en de details van de implementatie van gegevenstoegang moeten worden beperkt tot klassen die in de map Gegevens worden bewaard. Bedrijfslogica moet zich bevinden in services en klassen in de map Modellen.

Hoewel het eenvoudig is, heeft de monolithische oplossing met één project enkele nadelen. Naarmate de omvang en complexiteit van het project toeneemt, blijft het aantal bestanden en mappen ook groeien. Problemen met de gebruikersinterface (UI) (modellen, weergaven, controllers) bevinden zich in meerdere mappen, die niet alfabetisch zijn gegroepeerd. Dit probleem wordt alleen maar erger wanneer extra constructies op gebruikersinterfaceniveau, zoals Filters of ModelBinders, worden toegevoegd in hun eigen mappen. Bedrijfslogica wordt verspreid over de mappen Modellen en Services en er is geen duidelijke indicatie van welke klassen in welke mappen afhankelijk moeten zijn van welke andere klassen. Dit gebrek aan organisatie op projectniveau leidt vaak tot spaghetticode.

Om deze problemen op te lossen, ontwikkelen toepassingen zich vaak tot oplossingen voor meerdere projecten, waarbij elk project wordt beschouwd als een bepaalde laag van de toepassing.

Wat zijn lagen?

Naarmate toepassingen complexer worden, is het een manier om die complexiteit te beheren door de toepassing op te splitsen op basis van de verantwoordelijkheden of zorgen. Deze benadering volgt het principe van de scheiding van zorgen en kan helpen bij het organiseren van een groeiende codebasis, zodat ontwikkelaars gemakkelijk kunnen vinden waar bepaalde functionaliteit wordt geïmplementeerd. Gelaagde architectuur biedt echter een aantal voordelen dan alleen code-organisatie.

Door code in lagen te ordenen, kan algemene functionaliteit op laag niveau opnieuw worden gebruikt in de toepassing. Dit hergebruik is nuttig omdat dit betekent dat er minder code moet worden geschreven en omdat de toepassing kan standaardiseren op één implementatie, volgens het principe niet herhalen (DRY).

Met een gelaagde architectuur kunnen toepassingen beperkingen afdwingen voor welke lagen kunnen communiceren met andere lagen. Deze architectuur helpt bij het realiseren van inkapseling. Wanneer een laag wordt gewijzigd of vervangen, moeten alleen de lagen die ermee werken, worden beïnvloed. Door te beperken welke lagen afhankelijk zijn van welke andere lagen, kan de impact van wijzigingen worden beperkt, zodat één wijziging niet van invloed is op de hele toepassing.

Lagen (en inkapseling) maken het veel eenvoudiger om functionaliteit binnen de toepassing te vervangen. Een toepassing kan bijvoorbeeld in eerste instantie een eigen SQL Server-database gebruiken voor persistentie, maar kan er later voor kiezen om een strategie voor persistentie in de cloud te gebruiken, of een achter een web-API. Als de toepassing de persistentie-implementatie in een logische laag correct heeft ingekapseld, kan die SQL Server-specifieke laag worden vervangen door een nieuwe die dezelfde openbare interface implementeert.

Naast het potentieel van het wisselen van implementaties in reactie op toekomstige wijzigingen in vereisten, kunnen toepassingslagen het ook eenvoudiger maken om implementaties om te wisselen voor testdoeleinden. In plaats van tests te schrijven die werken op basis van de echte gegevenslaag of gebruikersinterfacelaag van de toepassing, kunnen deze lagen tijdens een test worden vervangen door valse implementaties die bekende reacties op aanvragen bieden. Deze aanpak maakt tests doorgaans veel eenvoudiger te schrijven en veel sneller uit te voeren in vergelijking met het uitvoeren van tests op de echte infrastructuur van de toepassing.

Logische lagen zijn een veelgebruikte techniek voor het verbeteren van de organisatie van code in bedrijfssoftwaretoepassingen en er zijn verschillende manieren waarop code in lagen kan worden ingedeeld.

Notitie

Lagen vertegenwoordigen logische scheiding binnen de toepassing. In het geval dat toepassingslogica fysiek wordt gedistribueerd naar afzonderlijke servers of processen, worden deze afzonderlijke fysieke implementatiedoelen aangeduid als lagen. Het is mogelijk en vrij gebruikelijk om een N-Layer-toepassing te hebben die is geïmplementeerd in één laag.

Traditionele 'N-Layer'-architectuurtoepassingen

De meest voorkomende organisatie van toepassingslogica in lagen wordt weergegeven in afbeelding 5-2.

Typical application layers

Afbeelding 5-2. Typische toepassingslagen.

Deze lagen worden vaak afgekort als UI, BLL (Business Logic Layer) en DAL (Data Access Layer). Met deze architectuur maken gebruikers aanvragen via de gebruikersinterfacelaag, die alleen met de BLL communiceert. De BLL kan op zijn beurt de DAL aanroepen voor aanvragen voor gegevenstoegang. De gebruikersinterfacelaag mag geen aanvragen rechtstreeks aan de DAL indienen en mag ook niet rechtstreeks met persistentie communiceren via andere manieren. Op dezelfde manier mag de BLL alleen met persistentie communiceren door de DAL te doorlopen. Op deze manier heeft elke laag een eigen bekende verantwoordelijkheid.

Een nadeel van deze traditionele gelaagde benadering is dat compileertijdafhankelijkheden van boven naar beneden worden uitgevoerd. Dat wil gezegd, de gebruikersinterfacelaag is afhankelijk van de BLL, die afhankelijk is van de DAL. Dit betekent dat de BLL, die meestal de belangrijkste logica in de toepassing bevat, afhankelijk is van details van de implementatie van gegevenstoegang (en vaak op het bestaan van een database). Het testen van bedrijfslogica in een dergelijke architectuur is vaak moeilijk, waarvoor een testdatabase is vereist. Het inversion-principe van afhankelijkheid kan worden gebruikt om dit probleem op te lossen, zoals u in de volgende sectie ziet.

Afbeelding 5-3 toont een voorbeeldoplossing, waarbij de toepassing wordt opgesplitst in drie projecten op basis van verantwoordelijkheid (of laag).

A simple monolithic application with three projects

Afbeelding 5-3. Een eenvoudige monolithische toepassing met drie projecten.

Hoewel deze toepassing verschillende projecten gebruikt voor organisatiedoeleinden, wordt deze nog steeds geïmplementeerd als één eenheid en de clients ermee werken als één web-app. Dit maakt een zeer eenvoudig implementatieproces mogelijk. Afbeelding 5-4 laat zien hoe een dergelijke app kan worden gehost met behulp van Azure.

Simple deployment of Azure Web App

Afbeelding 5-4. Eenvoudige implementatie van Azure Web App

Naarmate de toepassingsbehoeften toenemen, zijn er mogelijk complexere en robuustere implementatieoplossingen vereist. Afbeelding 5-5 toont een voorbeeld van een complexer implementatieplan dat ondersteuning biedt voor extra mogelijkheden.

Deploying a web app to an Azure App Service

Afbeelding 5-5. Een web-app implementeren in een Azure-app Service

Intern verbetert de organisatie van dit project in meerdere projecten op basis van verantwoordelijkheid de onderhoudbaarheid van de toepassing.

Deze eenheid kan omhoog of uit worden geschaald om te profiteren van schaalbaarheid op basis van de cloud op aanvraag. Omhoog schalen betekent dat u extra CPU, geheugen, schijfruimte of andere resources toevoegt aan de server(s) die als host fungeren voor uw app. Uitschalen betekent het toevoegen van extra exemplaren van dergelijke servers, ongeacht of dit fysieke servers, virtuele machines of containers zijn. Wanneer uw app wordt gehost op meerdere exemplaren, wordt een load balancer gebruikt om aanvragen toe te wijzen aan afzonderlijke app-exemplaren.

De eenvoudigste methode voor het schalen van een webtoepassing in Azure is het handmatig schalen in het App Service-plan van de toepassing te configureren. Afbeelding 5-6 toont het juiste Azure-dashboardscherm om te configureren hoeveel exemplaren een app bedienen.

App Service Plan scaling in Azure

Afbeelding 5-6. Schalen van App Service-plan in Azure.

Schone architectuur

Toepassingen die voldoen aan het afhankelijkheidsinversion-principe en de DDD-principes (Domain-Driven Design) komen meestal tot een vergelijkbare architectuur. Deze architectuur is in de loop der jaren door vele namen heen gegaan. Een van de voornamen was Zeshoekige architectuur, gevolgd door poorten en adapters. Onlangs is het geciteerd als de Onion Architecture of Clean Architecture. De laatste naam, Clean Architecture, wordt gebruikt als de naam voor deze architectuur in dit e-book.

De eShopOnWeb-referentietoepassing maakt gebruik van de benadering Schone architectuur bij het ordenen van de code in projecten. U vindt een oplossingssjabloon die u kunt gebruiken als uitgangspunt voor uw eigen ASP.NET Core-oplossingen in de GitHub-opslagplaats ardalis/cleanarchitecture of door de sjabloon te installeren vanuit NuGet.

Een schone architectuur plaatst de bedrijfslogica en het toepassingsmodel in het midden van de toepassing. In plaats van bedrijfslogica te hebben, hangt af van gegevenstoegang of andere problemen met de infrastructuur, wordt deze afhankelijkheid omgekeerd: infrastructuur- en implementatiedetails zijn afhankelijk van de Application Core. Deze functionaliteit wordt bereikt door abstracties of interfaces te definiëren in de Application Core, die vervolgens worden geïmplementeerd door typen die zijn gedefinieerd in de infrastructuurlaag. Een veelgebruikte manier om deze architectuur te visualiseren, is het gebruik van een reeks concentrische cirkels, vergelijkbaar met een ui. Afbeelding 5-7 toont een voorbeeld van deze stijl van architectuurweergave.

Clean Architecture; onion view

Afbeelding 5-7. Schone architectuur; ui-weergave

In dit diagram stromen afhankelijkheden naar de binnenste cirkel. De Application Core krijgt de naam van de naam van de kern van dit diagram. En u kunt in het diagram zien dat de Application Core geen afhankelijkheden heeft van andere toepassingslagen. De entiteiten en interfaces van de toepassing bevinden zich in het midden. Net buiten, maar nog steeds in Application Core, zijn domeinservices, die doorgaans interfaces implementeren die zijn gedefinieerd in de binnenste cirkel. Buiten de Application Core zijn zowel de gebruikersinterface als de infrastructuurlagen afhankelijk van de Application Core, maar niet van elkaar (noodzakelijkerwijs).

Afbeelding 5-8 toont een meer traditioneel horizontaal laagdiagram dat de afhankelijkheid tussen de gebruikersinterface en andere lagen beter weerspiegelt.

Clean Architecture; horizontal layer view

Afbeelding 5-8. Schone architectuur; horizontale laagweergave

Houd er rekening mee dat de ononderbroken pijlen gecompileerde tijdafhankelijkheden vertegenwoordigen, terwijl de stippelpijl een alleen-runtime-afhankelijkheid vertegenwoordigt. Met de schone architectuur werkt de UI-laag met interfaces die tijdens het compileren zijn gedefinieerd in Application Core en moet in het ideale geval niet weten welke implementatietypen zijn gedefinieerd in de infrastructuurlaag. Tijdens runtime zijn deze implementatietypen echter vereist om de app uit te voeren, zodat ze aanwezig moeten zijn en moeten worden aangesloten op de Application Core-interfaces via afhankelijkheidsinjectie.

In afbeelding 5-9 ziet u een gedetailleerdere weergave van de architectuur van een ASP.NET Core-toepassing bij het bouwen van deze aanbevelingen.

ASP.NET Core architecture diagram following Clean Architecture

Afbeelding 5-9. ASP.NET basisarchitectuurdiagram na Schone architectuur.

Omdat de Application Core niet afhankelijk is van Infrastructuur, is het heel eenvoudig om geautomatiseerde eenheidstests voor deze laag te schrijven. In de cijfers 5-10 en 5-11 ziet u hoe tests in deze architectuur passen.

UnitTestCore

Afbeelding 5-10. Eenheid testen van Application Core in isolatie.

IntegrationTests

Afbeelding 5-11. Integratie testen van infrastructuur-implementaties met externe afhankelijkheden.

Omdat de gebruikersinterfacelaag geen directe afhankelijkheid heeft van typen die zijn gedefinieerd in het infrastructuurproject, is het ook heel eenvoudig om implementaties te wisselen, ofwel om het testen te vergemakkelijken of in reactie op veranderende toepassingsvereisten. ASP.NET Core ingebouwd gebruik van en ondersteuning voor afhankelijkheidsinjectie maakt deze architectuur de meest geschikte manier om niet-triviale monolithische toepassingen te structureren.

Voor monolithische toepassingen worden de Application Core-, Infrastructuur- en UI-projecten allemaal uitgevoerd als één toepassing. De runtimetoepassingsarchitectuur kan er ongeveer uitzien als afbeelding 5-12.

ASP.NET Core Architecture 2

Afbeelding 5-12. Een voorbeeld van ASP.NET Runtime-architectuur van de Core-app.

Code ordenen in schone architectuur

In een oplossing voor schone architectuur heeft elk project duidelijke verantwoordelijkheden. Als zodanig behoren bepaalde typen in elk project en vindt u vaak mappen die overeenkomen met deze typen in het juiste project.

Application Core

Application Core bevat het bedrijfsmodel, waaronder entiteiten, services en interfaces. Deze interfaces omvatten abstracties voor bewerkingen die worden uitgevoerd met infrastructuur, zoals gegevenstoegang, bestandssysteemtoegang, netwerkoproepen, enzovoort. Soms moeten services of interfaces die op deze laag zijn gedefinieerd, werken met niet-entiteitstypen die geen afhankelijkheden hebben van de gebruikersinterface of infrastructuur. Deze kunnen worden gedefinieerd als eenvoudige DTU's (Data Transfer Objects).

Application Core-typen
  • Entiteiten (bedrijfsmodelklassen die persistent zijn)
  • Aggregaties (groepen entiteiten)
  • Interfaces
  • Domain Services
  • Specificaties
  • Aangepaste uitzonderingen en guard-componenten
  • Domein gebeurtenissen en handlers

Infrastructuur

Het infrastructuurproject bevat doorgaans implementaties voor gegevenstoegang. In een typische ASP.NET Core-webtoepassing omvatten deze implementaties de DbContext (Entity Framework) DbContext, eventuele EF Core-objecten Migration die zijn gedefinieerd en implementatieklassen voor gegevenstoegang. De meest voorkomende manier om code voor de implementatie van gegevenstoegang te abstraheren, is door gebruik te maken van het ontwerppatroon opslagplaats.

Naast implementaties van gegevenstoegang moet het infrastructuurproject implementaties bevatten van services die moeten communiceren met problemen met de infrastructuur. Deze services moeten interfaces implementeren die zijn gedefinieerd in de Application Core, en infrastructuur moet dus een verwijzing hebben naar het Application Core-project.

Infrastructuurtypen
  • EF Core-typen (DbContext, Migration)
  • Implementatietypen voor gegevenstoegang (opslagplaatsen)
  • Infrastructuurspecifieke services (bijvoorbeeld FileLogger of SmtpNotifier)

UI-laag

De gebruikersinterfacelaag in een ASP.NET Core MVC-toepassing is het toegangspunt voor de toepassing. Dit project moet verwijzen naar het Application Core-project en de typen moeten strikt communiceren met infrastructuur via interfaces die zijn gedefinieerd in Application Core. Er mag geen directe instantiëring van of statische aanroepen naar de typen infrastructuurlagen worden toegestaan in de gebruikersinterfacelaag.

Typen gebruikersinterfacelagen
  • Controllers
  • Aangepaste filters
  • Aangepaste middleware
  • Weergaven
  • ViewModels
  • Opstarten

Het Startup klasse- of Program.cs-bestand is verantwoordelijk voor het configureren van de toepassing en voor het bedraden van implementatietypen naar interfaces. De plaats waar deze logica wordt uitgevoerd, staat bekend als de hoofdmap van de samenstelling van de app en is wat afhankelijkheidsinjectie in staat stelt om goed te werken tijdens runtime.

Notitie

Als u afhankelijkheidsinjectie wilt koppelen tijdens het opstarten van de app, moet het ui-laagproject mogelijk verwijzen naar het infrastructuurproject. Deze afhankelijkheid kan eenvoudig worden geëlimineerd met behulp van een aangepaste DI-container met ingebouwde ondersteuning voor het laden van typen van assembly's. Voor het doel van dit voorbeeld is de eenvoudigste methode om het UI-project te laten verwijzen naar het infrastructuurproject (maar ontwikkelaars moeten werkelijke verwijzingen naar typen in het infrastructuurproject beperken tot de hoofdmap van de samenstelling van de app).

Monolithische toepassingen en containers

U kunt één webtoepassing of service op basis van monolithische implementatie bouwen en implementeren als een container. Binnen de toepassing is deze mogelijk niet monolithisch, maar ingedeeld in verschillende bibliotheken, onderdelen of lagen. Extern is het één container met één proces, één webtoepassing of één service.

Als u dit model wilt beheren, implementeert u één container die de toepassing vertegenwoordigt. Als u de schaal wilt aanpassen, voegt u extra kopieën toe met een load balancer vooraan. De eenvoud is het beheren van één implementatie in één container of VM.

Figure 5-13

U kunt meerdere onderdelen/bibliotheken of interne lagen in elke container opnemen, zoals geïllustreerd in afbeelding 5-13. Maar na het containerprincipe van 'een container doet één ding en doet het in één proces', kan het monolithische patroon een conflict zijn.

Het nadeel van deze aanpak is als/wanneer de toepassing groeit, waardoor deze moet worden geschaald. Als de hele toepassing wordt geschaald, is het geen probleem. In de meeste gevallen zijn echter enkele onderdelen van de toepassing de chokepunten waarvoor schaalaanpassing is vereist, terwijl andere onderdelen minder worden gebruikt.

Met behulp van het typische e-commercevoorbeeld, wat u waarschijnlijk moet schalen, is het productinformatieonderdeel. Veel meer klanten bladeren door producten dan ze te kopen. Meer klanten gebruiken hun winkelwagen dan de betalingspijplijn. Minder klanten voegen opmerkingen toe of bekijken hun aankoopgeschiedenis. En u hebt waarschijnlijk slechts een handvol werknemers, in één regio, die de inhoud en marketingcampagnes moeten beheren. Door het monolithische ontwerp te schalen, wordt alle code meerdere keren geïmplementeerd.

Naast het probleem 'alles schalen', moeten wijzigingen in één onderdeel volledig opnieuw worden getest van de hele toepassing en moet alle exemplaren volledig opnieuw worden geïmplementeerd.

De monolithische benadering is gebruikelijk en veel organisaties ontwikkelen zich met deze architectuurbenadering. Velen hebben goede resultaten, terwijl anderen limieten bereiken. Velen hebben hun toepassingen in dit model ontworpen, omdat de hulpprogramma's en infrastructuur te moeilijk waren om servicegeoriënteerde architecturen (SOA) te bouwen en ze de noodzaak niet zagen totdat de app groeide. Als u merkt dat u de limieten van de monolithische benadering bereikt, kunt u de app opsplitsen zodat deze beter gebruik kan maken van containers en microservices, kan dit de volgende logische stap zijn.

Figure 5-14

Het implementeren van monolithische toepassingen in Microsoft Azure kan worden bereikt met behulp van toegewezen VM's voor elk exemplaar. Met behulp van Virtuele-machineschaalsets van Azure kunt u de VM's eenvoudig schalen. Azure-app Services kunnen monolithische toepassingen uitvoeren en eenvoudig exemplaren schalen zonder de VM's te hoeven beheren. Azure-app Services kunnen ook enkele exemplaren van Docker-containers uitvoeren, waardoor de implementatie wordt vereenvoudigd. Met Docker kunt u één VIRTUELE machine implementeren als een Docker-host en meerdere exemplaren uitvoeren. Met behulp van de Azure Balancer, zoals weergegeven in afbeelding 5-14, kunt u schalen beheren.

De implementatie naar de verschillende hosts kan worden beheerd met traditionele implementatietechnieken. De Docker-hosts kunnen worden beheerd met opdrachten zoals docker die handmatig worden uitgevoerd , of via automatisering, zoals CD-pijplijnen (Continuous Delivery).

Monolithische toepassing geïmplementeerd als een container

Er zijn voordelen van het gebruik van containers voor het beheren van monolithische toepassingsimplementaties. Het schalen van de exemplaren van containers is veel sneller en eenvoudiger dan het implementeren van extra VM's. Zelfs wanneer u virtuele-machineschaalsets gebruikt om VM's te schalen, duurt het even om ze te maken. Wanneer deze wordt geïmplementeerd als app-exemplaren, wordt de configuratie van de app beheerd als onderdeel van de VIRTUELE machine.

Het implementeren van updates als Docker-installatiekopieën is veel sneller en netwerkefficiënt. Docker-installatiekopieën beginnen doorgaans in seconden, waardoor de implementaties sneller verlopen. Het verwijderen van een Docker-exemplaar is net zo eenvoudig als het uitgeven van een docker stop opdracht, meestal in minder dan een seconde.

Omdat containers inherent onveranderbaar zijn, hoeft u zich geen zorgen te maken over beschadigde VM's, terwijl updatescripts mogelijk vergeten rekening te houden met een bepaalde configuratie of bestand dat op de schijf blijft staan.

U kunt Docker-containers gebruiken voor een monolithische implementatie van eenvoudigere webtoepassingen. Deze aanpak verbetert continue integratie en pijplijnen voor continue implementatie en helpt implementatie-naar-productie te bereiken. Niet meer 'Het werkt op mijn computer, waarom werkt het niet in productie?'

Een architectuur op basis van microservices heeft veel voordelen, maar deze voordelen hebben een hogere complexiteit. In sommige gevallen wegen de kosten op tegen de voordelen, dus een monolithische implementatietoepassing die wordt uitgevoerd in één container of in slechts een paar containers is een betere optie.

Een monolithische toepassing is mogelijk niet eenvoudig te decomposeerbaar in goed gescheiden microservices. Microservices moeten onafhankelijk van elkaar werken om een tolerantere toepassing te bieden. Als u geen onafhankelijke functiesegmenten van de toepassing kunt leveren, voegt het scheiden ervan alleen complexiteit toe.

Het is mogelijk dat een toepassing functies nog niet onafhankelijk hoeft te schalen. Veel toepassingen, wanneer ze meer dan één exemplaar moeten schalen, kunnen dit doen door het relatief eenvoudige proces van het klonen van dat hele exemplaar. Het extra werk om de toepassing te scheiden in discrete services biedt een minimaal voordeel wanneer het schalen van volledige exemplaren van de toepassing eenvoudig en rendabel is.

Vroeg in de ontwikkeling van een toepassing hebt u mogelijk geen duidelijk idee waar de natuurlijke functionele grenzen zich bevinden. Naarmate u een minimaal levensvatbaar product ontwikkelt, is de natuurlijke scheiding mogelijk nog niet ontstaan. Sommige van deze voorwaarden zijn mogelijk tijdelijk. U kunt beginnen met het maken van een monolithische toepassing en later enkele functies scheiden die moeten worden ontwikkeld en geïmplementeerd als microservices. Andere voorwaarden kunnen essentieel zijn voor de probleemruimte van de toepassing, wat betekent dat de toepassing nooit kan worden opgesplitst in meerdere microservices.

Het scheiden van een toepassing in veel afzonderlijke processen introduceert ook overhead. Er is meer complexiteit bij het scheiden van functies in verschillende processen. De communicatieprotocollen worden complexer. In plaats van methodeaanroepen moet u asynchrone communicatie tussen services gebruiken. Wanneer u overstapt op een microservicesarchitectuur, moet u veel van de bouwstenen toevoegen die zijn geïmplementeerd in de microservicesversie van de eShopOnContainers-toepassing: verwerking van event bus, berichttolerantie en nieuwe pogingen, uiteindelijke consistentie en meer.

De veel eenvoudigere eShopOnWeb-referentietoepassing biedt ondersteuning voor monolithisch containergebruik met één container. De toepassing bevat één webtoepassing met traditionele MVC-weergaven, web-API's en Razor Pages. U kunt eventueel ook het blazor-beheeronderdeel van de toepassing uitvoeren. Hiervoor moet ook een afzonderlijk API-project worden uitgevoerd.

De toepassing kan worden gestart vanuit de hoofdmap van de oplossing met behulp van de docker-compose build en docker-compose up opdrachten. Met deze opdracht configureert u een container voor het webexemplaren, met behulp van de Dockerfile gevonden in de hoofdmap van het webproject en voert u de container uit op een opgegeven poort. U kunt de bron voor deze toepassing downloaden vanuit GitHub en deze lokaal uitvoeren. Zelfs deze monolithische toepassing profiteert van implementatie in een containeromgeving.

Voor één betekent de containerimplementatie dat elk exemplaar van de toepassing in dezelfde omgeving wordt uitgevoerd. Deze benadering omvat de ontwikkelomgeving waar vroege tests en ontwikkeling plaatsvinden. Het ontwikkelteam kan de toepassing uitvoeren in een containeromgeving die overeenkomt met de productieomgeving.

Bovendien worden toepassingen in containers uitgeschaald tegen lagere kosten. Het gebruik van een containeromgeving maakt het delen van resources mogelijk dan traditionele VM-omgevingen.

Ten slotte dwingt het containeriseren van de toepassing een scheiding af tussen de bedrijfslogica en de opslagserver. Naarmate de toepassing wordt uitgeschaald, zijn de meerdere containers allemaal afhankelijk van één fysiek opslagmedium. Dit opslagmedium is doorgaans een server met hoge beschikbaarheid waarop een SQL Server-database wordt uitgevoerd.

Docker-ondersteuning

Het eShopOnWeb project wordt uitgevoerd op .NET. Daarom kan het worden uitgevoerd in linux- of Windows-containers. Houd er rekening mee dat u voor docker-implementatie hetzelfde hosttype wilt gebruiken voor SQL Server. Linux-containers maken een kleinere footprint mogelijk en hebben de voorkeur.

U kunt Visual Studio 2017 of hoger gebruiken om Docker-ondersteuning toe te voegen aan een bestaande toepassing door met de rechtermuisknop op een project in Solution Explorer te klikken en Docker-ondersteuning toevoegen>te kiezen. Met deze stap worden de vereiste bestanden toegevoegd en wordt het project gewijzigd om ze te gebruiken. In het huidige eShopOnWeb voorbeeld zijn deze bestanden al aanwezig.

Het bestand op oplossingsniveau docker-compose.yml bevat informatie over welke installatiekopieën moeten worden gebouwd en welke containers moeten worden gestart. Met het bestand kunt u de docker-compose opdracht gebruiken om meerdere toepassingen tegelijk te starten. In dit geval wordt alleen het webproject gestart. U kunt deze ook gebruiken om afhankelijkheden te configureren, zoals een afzonderlijke databasecontainer.

version: '3'

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

networks:
  default:
    external:
      name: nat

Het docker-compose.yml bestand verwijst naar het DockerfileWeb project. De Dockerfile toepassing wordt gebruikt om op te geven welke basiscontainer wordt gebruikt en hoe de toepassing erop wordt geconfigureerd. De 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"]

Problemen met Docker oplossen

Zodra u de containertoepassing uitvoert, blijft deze actief totdat u deze stopt. U kunt bekijken welke containers worden uitgevoerd met de docker ps opdracht. U kunt een actieve container stoppen met behulp van de docker stop opdracht en de container-id opgeven.

Houd er rekening mee dat het uitvoeren van Docker-containers afhankelijk is van poorten die u anders probeert te gebruiken in uw ontwikkelomgeving. Als u probeert een toepassing uit te voeren of fouten op te sporen met dezelfde poort als een actieve Docker-container, krijgt u een foutmelding dat de server geen verbinding met die poort kan maken. Opnieuw moet het probleem worden opgelost door de container te stoppen.

Als u Docker-ondersteuning wilt toevoegen aan uw toepassing met Visual Studio, moet u ervoor zorgen dat Docker Desktop wordt uitgevoerd wanneer u dit doet. De wizard wordt niet correct uitgevoerd als Docker Desktop niet wordt uitgevoerd wanneer u de wizard start. Daarnaast onderzoekt de wizard uw huidige containerkeuze om de juiste Docker-ondersteuning toe te voegen. Als u ondersteuning voor Windows-containers wilt toevoegen, moet u de wizard uitvoeren terwijl Docker Desktop wordt uitgevoerd met Windows-containers geconfigureerd. Als u ondersteuning voor Linux-containers wilt toevoegen, voert u de wizard uit terwijl Docker wordt uitgevoerd met Linux-containers geconfigureerd.

Andere architectuurstijlen voor webtoepassingen

  • Web-Queue-Worker: de kernonderdelen van deze architectuur zijn een webfront-end die clientaanvragen bedient en een werkrol die resource-intensieve taken, langlopende werkstromen of batchtaken uitvoert. De web-front-end communiceert via een berichtenwachtrij met de werkrol.
  • N-laag: Een N-laag-architectuur verdeelt een toepassing in logische lagen en fysieke lagen.
  • Microservice: Een microservicesarchitectuur bestaat uit een verzameling kleine, autonome services. Elke service is zelfstandig en moet één bedrijfsmogelijkheid binnen een gebonden context implementeren.

Verwijzingen – Algemene webarchitecturen