Arquiteturas comuns de aplicativos Web

Gorjeta

Este conteúdo é um excerto do eBook, Architect Modern Web Applications with ASP.NET Core e Azure, disponível no .NET Docs ou como um PDF transferível gratuito que pode ser lido offline.

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

"Se você acha que uma boa arquitetura é cara, experimente a arquitetura ruim." - Brian Foote e Joseph Yoder

A maioria dos aplicativos .NET tradicionais são implantados como unidades únicas correspondentes a um executável ou a um único aplicativo Web em execução em um único domínio de aplicativo do IIS. Essa abordagem é o modelo de implantação mais simples e atende muito bem a muitos aplicativos públicos internos e menores. No entanto, mesmo dada essa única unidade de implantação, a maioria dos aplicativos de negócios não triviais se beneficia de alguma separação lógica em várias camadas.

O que é uma aplicação monolítica?

Uma aplicação monolítica é aquela que é inteiramente independente, em termos de seu comportamento. Ele pode interagir com outros serviços ou armazenamentos de dados durante a execução de suas operações, mas o núcleo de seu comportamento é executado dentro de seu próprio processo e todo o aplicativo é normalmente implantado como uma única unidade. Se esse aplicativo precisar ser dimensionado horizontalmente, normalmente todo o aplicativo será duplicado em vários servidores ou máquinas virtuais.

Aplicações tudo-em-um

O menor número possível de projetos para uma arquitetura de aplicativo é um. Nessa arquitetura, toda a lógica do aplicativo está contida em um único projeto, compilada em um único assembly e implantada como uma única unidade.

Um novo projeto ASP.NET Core, seja criado no Visual Studio ou na linha de comando, começa como um simples monólito "tudo-em-um". Ele contém todo o comportamento do aplicativo, incluindo apresentação, negócios e lógica de acesso a dados. A Figura 5-1 mostra a estrutura de arquivos de um aplicativo de projeto único.

A single project ASP.NET Core app

Figura 5-1. Um único projeto ASP.NET aplicativo principal.

Em um único cenário de projeto, a separação de preocupações é alcançada através do uso de pastas. O modelo padrão inclui pastas separadas para responsabilidades de padrão MVC de Modelos, Exibições e Controladores, bem como pastas adicionais para Dados e Serviços. Nessa disposição, os detalhes da apresentação devem ser limitados tanto quanto possível à pasta Exibições, e os detalhes da implementação do acesso a dados devem ser limitados às classes mantidas na pasta Dados. A lógica de negócios deve residir em serviços e classes dentro da pasta Modelos.

Apesar de simples, a solução monolítica de projeto único tem algumas desvantagens. À medida que o tamanho e a complexidade do projeto aumentam, o número de arquivos e pastas também continuará a crescer. As preocupações com a interface do usuário (UI) (modelos, exibições, controladores) residem em várias pastas, que não são agrupadas em ordem alfabética. Esse problema só piora quando construções adicionais no nível da interface do usuário, como Filters ou ModelBinders, são adicionadas em suas próprias pastas. A lógica de negócios está espalhada entre as pastas Modelos e Serviços, e não há uma indicação clara de quais classes em quais pastas devem depender de quais outras. Esta falta de organização ao nível do projeto conduz frequentemente ao código do esparguete.

Para resolver esses problemas, os aplicativos geralmente evoluem para soluções multiprojeto, onde cada projeto é considerado como residindo em uma camada específica do aplicativo.

O que são camadas lógicas?

À medida que os aplicativos crescem em complexidade, uma maneira de gerenciar essa complexidade é dividir o aplicativo de acordo com suas responsabilidades ou preocupações. Essa abordagem segue o princípio da separação de preocupações e pode ajudar a manter uma base de código crescente organizada para que os desenvolvedores possam encontrar facilmente onde determinada funcionalidade é implementada. No entanto, a arquitetura em camadas oferece uma série de vantagens além da organização de código.

Ao organizar o código em camadas, a funcionalidade comum de baixo nível pode ser reutilizada em todo o aplicativo. Essa reutilização é benéfica porque significa que menos código precisa ser escrito e porque pode permitir que o aplicativo seja padronizado em uma única implementação, seguindo o princípio de não se repetir (DRY ).

Com uma arquitetura em camadas, os aplicativos podem impor restrições sobre quais camadas podem se comunicar com outras camadas. Essa arquitetura ajuda a alcançar o encapsulamento. Quando uma camada é alterada ou substituída, apenas as camadas que trabalham com ela devem ser afetadas. Ao limitar quais camadas dependem de quais outras camadas, o impacto das alterações pode ser atenuado para que uma única alteração não afete todo o aplicativo.

As camadas (e o encapsulamento) facilitam muito a substituição da funcionalidade dentro do aplicativo. Por exemplo, um aplicativo pode inicialmente usar seu próprio banco de dados SQL Server para persistência, mas depois pode optar por usar uma estratégia de persistência baseada em nuvem ou uma por trás de uma API da Web. Se o aplicativo tiver encapsulado corretamente sua implementação de persistência em uma camada lógica, essa camada específica do SQL Server poderá ser substituída por uma nova que implemente a mesma interface pública.

Além do potencial de trocar implementações em resposta a futuras alterações nos requisitos, as camadas de aplicativos também podem facilitar a troca de implementações para fins de teste. Em vez de ter que escrever testes que operam em relação à camada de dados real ou camada de interface do usuário do aplicativo, essas camadas podem ser substituídas no momento do teste por implementações falsas que fornecem respostas conhecidas às solicitações. Essa abordagem normalmente torna os testes muito mais fáceis de escrever e muito mais rápidos de executar quando comparados à execução de testes na infraestrutura real do aplicativo.

A camada lógica é uma técnica comum para melhorar a organização do código em aplicativos de software empresarial, e há várias maneiras pelas quais o código pode ser organizado em camadas.

Nota

As camadas representam a separação lógica dentro do aplicativo. Caso a lógica do aplicativo seja fisicamente distribuída para servidores ou processos separados, esses destinos de implantação física separados são chamados de camadas. É possível, e bastante comum, ter um aplicativo N-Layer implantado em uma única camada.

Aplicações tradicionais de arquitetura "N-Layer"

A organização mais comum da lógica do aplicativo em camadas é mostrada na Figura 5-2.

Typical application layers

Figura 5-2. Camadas de aplicação típicas.

Essas camadas são frequentemente abreviadas como UI, BLL (Business Logic Layer) e DAL (Data Access Layer). Usando essa arquitetura, os usuários fazem solicitações por meio da camada da interface do usuário, que interage apenas com a BLL. A BLL, por sua vez, pode chamar o DAL para solicitações de acesso a dados. A camada de interface do usuário não deve fazer nenhuma solicitação à DAL diretamente, nem deve interagir com a persistência diretamente por outros meios. Da mesma forma, a BLL só deve interagir com a persistência passando pelo DAL. Desta forma, cada camada tem a sua própria responsabilidade bem conhecida.

Uma desvantagem dessa abordagem tradicional de camadas é que as dependências em tempo de compilação são executadas de cima para baixo. Ou seja, a camada da interface do usuário depende da BLL, que depende da DAL. Isso significa que a BLL, que geralmente detém a lógica mais importante no aplicativo, depende dos detalhes da implementação do acesso aos dados (e, muitas vezes, da existência de um banco de dados). Testar a lógica de negócios em tal arquitetura geralmente é difícil, exigindo um banco de dados de teste. O princípio de inversão de dependência pode ser usado para resolver esse problema, como você verá na próxima seção.

A Figura 5-3 mostra um exemplo de solução, dividindo o aplicativo em três projetos por responsabilidade (ou camada).

A simple monolithic application with three projects

Figura 5-3. Uma aplicação monolítica simples com três projetos.

Embora este aplicativo use vários projetos para fins organizacionais, ele ainda é implantado como uma única unidade e seus clientes irão interagir com ele como um único aplicativo Web. Isso permite um processo de implantação muito simples. A Figura 5-4 mostra como esse aplicativo pode ser hospedado usando o Azure.

Simple deployment of Azure Web App

Figura 5-4. Implementação simples da Aplicação Web do Azure

À medida que as necessidades dos aplicativos crescem, soluções de implantação mais complexas e robustas podem ser necessárias. A Figura 5-5 mostra um exemplo de um plano de implantação mais complexo que oferece suporte a recursos adicionais.

Deploying a web app to an Azure App Service

Figura 5-5. Implantando um aplicativo Web em um Serviço de Aplicativo do Azure

Internamente, a organização deste projeto em vários projetos com base na responsabilidade melhora a capacidade de manutenção da aplicação.

Essa unidade pode ser ampliada ou ampliada para aproveitar a escalabilidade sob demanda baseada em nuvem. Aumentar a escala significa adicionar CPU, memória, espaço em disco ou outros recursos adicionais ao(s) servidor(es) que hospeda seu aplicativo. Dimensionar significa adicionar instâncias adicionais desses servidores, sejam eles servidores físicos, máquinas virtuais ou contêineres. Quando seu aplicativo é hospedado em várias instâncias, um balanceador de carga é usado para atribuir solicitações a instâncias individuais do aplicativo.

A abordagem mais simples para dimensionar um aplicativo Web no Azure é configurar o dimensionamento manualmente no Plano do Serviço de Aplicativo do aplicativo. A Figura 5-6 mostra a tela apropriada do painel do Azure para configurar quantas instâncias estão servindo um aplicativo.

App Service Plan scaling in Azure

Figura 5-6. Dimensionamento do Plano do Serviço de Aplicativo no Azure.

Arquitetura limpa

Os aplicativos que seguem o Princípio de Inversão de Dependência, bem como os princípios de DDD (Domain-Driven Design), tendem a chegar a uma arquitetura semelhante. Esta arquitetura passou por muitos nomes ao longo dos anos. Um dos primeiros nomes foi Hexagonal Architecture, seguido por Ports-and-Adapters. Mais recentemente, tem sido citado como Onion Architecture ou Clean Architecture. O último nome, Clean Architecture, é usado como o nome para esta arquitetura neste e-book.

O aplicativo de referência eShopOnWeb usa a abordagem Clean Architecture na organização de seu código em projetos. Você pode encontrar um modelo de solução que você pode usar como ponto de partida para suas próprias soluções ASP.NET Core no repositório GitHub ardalis/cleanarchitecture ou instalando o modelo do NuGet.

A arquitetura limpa coloca a lógica de negócios e o modelo de aplicativo no centro do aplicativo. Em vez de a lógica de negócios depender do acesso aos dados ou de outras preocupações com a infraestrutura, essa dependência é invertida: os detalhes da infraestrutura e da implementação dependem do núcleo do aplicativo. Essa funcionalidade é alcançada através da definição de abstrações, ou interfaces, no Application Core, que são então implementadas por tipos definidos na camada de infraestrutura. Uma maneira comum de visualizar essa arquitetura é usar uma série de círculos concêntricos, semelhantes a uma cebola. A Figura 5-7 mostra um exemplo desse estilo de representação arquitetônica.

Clean Architecture; onion view

Figura 5-7. Arquitetura Limpa; vista cebola

Neste diagrama, as dependências fluem em direção ao círculo mais interno. O Application Core leva seu nome de sua posição no núcleo deste diagrama. E você pode ver no diagrama que o Application Core não tem dependências em outras camadas de aplicativo. As entidades e interfaces do aplicativo estão no centro. Do lado de fora, mas ainda no Application Core, estão os serviços de domínio, que normalmente implementam interfaces definidas no círculo interno. Fora do Application Core, as camadas UI e Infrastructure dependem do Application Core, mas não uma da outra (necessariamente).

A Figura 5-8 mostra um diagrama de camada horizontal mais tradicional que reflete melhor a dependência entre a interface do usuário e outras camadas.

Clean Architecture; horizontal layer view

Figura 5-8. Arquitetura Limpa; vista de camada horizontal

Observe que as setas sólidas representam dependências em tempo de compilação, enquanto a seta tracejada representa uma dependência somente em tempo de execução. Com a arquitetura limpa, a camada de interface do usuário funciona com interfaces definidas no Application Core em tempo de compilação e, idealmente, não deve saber sobre os tipos de implementação definidos na camada de infraestrutura. Em tempo de execução, no entanto, esses tipos de implementação são necessários para que o aplicativo seja executado, portanto, eles precisam estar presentes e conectados às interfaces do Application Core por meio de injeção de dependência.

A Figura 5-9 mostra uma visão mais detalhada da arquitetura de um aplicativo ASP.NET Core quando criado seguindo essas recomendações.

ASP.NET Core architecture diagram following Clean Architecture

Figura 5-9. ASP.NET Diagrama de arquitetura principal seguindo Clean Architecture.

Como o Application Core não depende da infraestrutura, é muito fácil escrever testes de unidade automatizados para essa camada. As figuras 5-10 e 5-11 mostram como os testes se encaixam nessa arquitetura.

UnitTestCore

Figura 5-10. Teste unitário do Application Core isoladamente.

IntegrationTests

Figura 5-11. Testes de integração Implementações de infraestrutura com dependências externas.

Como a camada da interface do usuário não tem nenhuma dependência direta dos tipos definidos no projeto de infraestrutura, também é muito fácil trocar implementações, seja para facilitar os testes ou em resposta às mudanças nos requisitos do aplicativo. O uso interno do ASP.NET Core e o suporte para injeção de dependência tornam essa arquitetura a maneira mais apropriada de estruturar aplicativos monolíticos não triviais.

Para aplicativos monolíticos, os projetos Application Core, Infrastructure e UI são executados como um único aplicativo. A arquitetura do aplicativo de tempo de execução pode se parecer com a Figura 5-12.

ASP.NET Core Architecture 2

Figura 5-12. Um exemplo ASP.NET arquitetura de tempo de execução do aplicativo Core.

Organizando código em Clean Architecture

Numa solução de Clean Architecture, cada projeto tem responsabilidades claras. Como tal, certos tipos pertencem a cada projeto e você frequentemente encontrará pastas correspondentes a esses tipos no projeto apropriado.

Núcleo do aplicativo

O Application Core contém o modelo de negócios, que inclui entidades, serviços e interfaces. Essas interfaces incluem abstrações para operações que serão realizadas usando a infraestrutura, como acesso a dados, acesso ao sistema de arquivos, chamadas de rede, etc. Às vezes, os serviços ou interfaces definidos nessa camada precisarão trabalhar com tipos que não sejam de entidade e que não tenham dependências da interface do usuário ou da infraestrutura. Estes podem ser definidos como simples Data Transfer Objects (DTOs).

Tipos de núcleo de aplicativo
  • Entidades (classes de modelo de negócios que são persistentes)
  • Agregados (grupos de entidades)
  • Interfaces
  • Serviços de Domínio
  • Especificações
  • Exceções personalizadas e cláusulas de proteção
  • Eventos e manipuladores de domínio

Infraestrutura

O projeto de infraestrutura normalmente inclui implementações de acesso a dados. Em um aplicativo Web ASP.NET Core típico, essas implementações incluem o DbContext do Entity Framework (EF), quaisquer objetos do EF Core Migration que tenham sido definidos e classes de implementação de acesso a dados. A maneira mais comum de abstrair o código de implementação de acesso a dados é através do uso do padrão de design do repositório.

Além das implementações de acesso a dados, o projeto de infraestrutura deve conter implementações de serviços que devem interagir com preocupações de infraestrutura. Esses serviços devem implementar interfaces definidas no Application Core e, portanto, a infraestrutura deve ter uma referência ao projeto Application Core.

Tipos de infraestrutura
  • Tipos EF Core (DbContext, Migration)
  • Tipos de implementação de acesso a dados (Repositórios)
  • Serviços específicos da infraestrutura (por exemplo, FileLogger ou SmtpNotifier)

Camada da interface do usuário

A camada de interface do usuário em um aplicativo MVC ASP.NET Core é o ponto de entrada para o aplicativo. Este projeto deve fazer referência ao projeto Application Core, e seus tipos devem interagir com a infraestrutura estritamente por meio de interfaces definidas no Application Core. Nenhuma instanciação direta ou chamadas estáticas para os tipos de camada de infraestrutura devem ser permitidas na camada de interface do usuário.

Tipos de camada de interface do usuário
  • Controllers
  • Filtros personalizados
  • Middleware personalizado
  • Visualizações
  • ViewModels
  • Arranque

O Startup arquivo de classe ou Program.cs é responsável por configurar o aplicativo e por conectar tipos de implementação a interfaces. O local onde essa lógica é executada é conhecido como raiz de composição do aplicativo e é o que permite que a injeção de dependência funcione corretamente em tempo de execução.

Nota

Para conectar a injeção de dependência durante a inicialização do aplicativo, o projeto de camada de interface do usuário pode precisar fazer referência ao projeto de infraestrutura. Essa dependência pode ser eliminada, mais facilmente, usando um contêiner DI personalizado que tenha suporte interno para tipos de carregamento de montagens. Para os fins deste exemplo, a abordagem mais simples é permitir que o projeto de interface do usuário faça referência ao projeto de infraestrutura (mas os desenvolvedores devem limitar as referências reais aos tipos no projeto de infraestrutura à raiz de composição do aplicativo).

Aplicações e contentores monolíticos

Você pode criar um aplicativo ou serviço Web único e baseado em implantação monolítica e implantá-lo como um contêiner. Dentro do aplicativo, ele pode não ser monolítico, mas organizado em várias bibliotecas, componentes ou camadas. Externamente, é um único contêiner com um único processo, um único aplicativo Web ou um único serviço.

Para gerenciar esse modelo, implante um único contêiner para representar o aplicativo. Para dimensionar, basta adicionar cópias adicionais com um balanceador de carga na frente. A simplicidade vem do gerenciamento de uma única implantação em um único contêiner ou VM.

Figure 5-13

É possível incluir vários componentes/bibliotecas ou camadas internas em cada contêiner, conforme ilustrado na Figura 5-13. Mas, seguindo o princípio do contêiner de "um contêiner faz uma coisa e faz isso em um processo", o padrão monolítico pode ser um conflito.

A desvantagem dessa abordagem vem se/quando o aplicativo cresce, exigindo que ele seja dimensionado. Se todo o aplicativo for dimensionado, não é realmente um problema. No entanto, na maioria dos casos, algumas partes da aplicação são os pontos de estrangulamento que requerem escala, enquanto outros componentes são usados menos.

Usando o exemplo típico de comércio eletrônico, o que você provavelmente precisa dimensionar é o componente de informações do produto. Muito mais clientes procuram produtos do que os compram. Mais clientes usam sua cesta do que usam o pipeline de pagamento. Menos clientes adicionam comentários ou visualizam seu histórico de compras. E você provavelmente só tem um punhado de funcionários, em uma única região, que precisam gerenciar o conteúdo e as campanhas de marketing. Ao dimensionar o design monolítico, todo o código é implantado várias vezes.

Além do problema de "dimensionar tudo", as alterações em um único componente exigem um novo teste completo de todo o aplicativo e uma reimplantação completa de todas as instâncias.

A abordagem monolítica é comum, e muitas organizações estão se desenvolvendo com essa abordagem arquitetônica. Muitos estão a ter resultados suficientemente bons, enquanto outros estão a atingir limites. Muitos projetaram seus aplicativos nesse modelo, porque as ferramentas e a infraestrutura eram muito difíceis de criar arquiteturas orientadas a serviços (SOA), e eles não viram a necessidade até que o aplicativo crescesse. Se você achar que está atingindo os limites da abordagem monolítica, dividir o aplicativo para permitir que ele aproveite melhor os contêineres e microsserviços pode ser a próxima etapa lógica.

Figure 5-14

A implantação de aplicativos monolíticos no Microsoft Azure pode ser obtida usando VMs dedicadas para cada instância. Usando os Conjuntos de Escala de Máquina Virtual do Azure, você pode dimensionar facilmente as VMs. Os Serviços de Aplicativo do Azure podem executar aplicativos monolíticos e dimensionar instâncias facilmente sem precisar gerenciar as VMs. Os Serviços de Aplicativo do Azure também podem executar instâncias únicas de contêineres do Docker, simplificando a implantação. Usando o Docker, você pode implantar uma única VM como host do Docker e executar várias instâncias. Usando o balanceador do Azure, conforme mostrado na Figura 5-14, você pode gerenciar o dimensionamento.

A implantação para os vários hosts pode ser gerenciada com técnicas de implantação tradicionais. Os hosts do Docker podem ser gerenciados com comandos como a execução do docker executada manualmente ou por meio de automação, como pipelines de Entrega Contínua (CD).

Aplicativo monolítico implantado como um contêiner

Há benefícios de usar contêineres para gerenciar implantações de aplicativos monolíticos. Dimensionar as instâncias de contêineres é muito mais rápido e fácil do que implantar VMs adicionais. Mesmo ao usar conjuntos de dimensionamento de máquina virtual para dimensionar VMs, eles levam tempo para serem criados. Quando implantado como instâncias de aplicativo, a configuração do aplicativo é gerenciada como parte da VM.

A implantação de atualizações como imagens do Docker é muito mais rápida e eficiente na rede. As imagens do Docker normalmente começam em segundos, acelerando as distribuições. Derrubar uma instância do Docker é tão fácil quanto emitir um docker stop comando, normalmente concluído em menos de um segundo.

Como os contêineres são inerentemente imutáveis por design, você nunca precisa se preocupar com VMs corrompidas, enquanto os scripts de atualização podem esquecer de levar em conta alguma configuração ou arquivo específico deixado no disco.

Você pode usar contêineres do Docker para uma implantação monolítica de aplicativos Web mais simples. Essa abordagem melhora a integração contínua e os pipelines de implantação contínua e ajuda a alcançar o sucesso da implantação até a produção. Não mais "Funciona na minha máquina, por que não funciona na produção?"

Uma arquitetura baseada em microsserviços tem muitos benefícios, mas esses benefícios têm um custo de maior complexidade. Em alguns casos, os custos superam os benefícios, portanto, um aplicativo de implantação monolítico executado em um único contêiner ou em apenas alguns contêineres é uma opção melhor.

Um aplicativo monolítico pode não ser facilmente decomponível em microsserviços bem separados. Os microsserviços devem funcionar independentemente uns dos outros para fornecer uma aplicação mais resiliente. Se você não puder fornecer fatias de recursos independentes do aplicativo, separá-lo só aumenta a complexidade.

Um aplicativo pode ainda não precisar dimensionar recursos de forma independente. Muitos aplicativos, quando precisam ser dimensionados além de uma única instância, podem fazê-lo por meio do processo relativamente simples de clonagem de toda essa instância. O trabalho adicional para separar o aplicativo em serviços discretos fornece um benefício mínimo quando o dimensionamento de instâncias completas do aplicativo é simples e econômico.

No início do desenvolvimento de um aplicativo, você pode não ter uma ideia clara de onde estão os limites funcionais naturais. À medida que você desenvolve um produto mínimo viável, a separação natural pode ainda não ter surgido. Algumas destas condições podem ser temporárias. Você pode começar criando um aplicativo monolítico e, posteriormente, separar alguns recursos a serem desenvolvidos e implantados como microsserviços. Outras condições podem ser essenciais para o espaço de problemas do aplicativo, o que significa que o aplicativo pode nunca ser dividido em vários microsserviços.

Separar um aplicativo em muitos processos discretos também introduz sobrecarga. Há mais complexidade em separar recursos em diferentes processos. Os protocolos de comunicação tornam-se mais complexos. Em vez de chamadas de método, você deve usar comunicações assíncronas entre serviços. Ao mudar para uma arquitetura de microsserviços, você precisa adicionar muitos dos blocos de construção implementados na versão de microsserviços do aplicativo eShopOnContainers: manipulação de barramento de eventos, resiliência e tentativas de mensagens, consistência eventual e muito mais.

O aplicativo de referência eShopOnWeb, muito mais simples, suporta o uso de contêiner monolítico de contêiner único. O aplicativo inclui um aplicativo Web que inclui visualizações MVC tradicionais, APIs da Web e Razor Pages. Opcionalmente, você pode executar o componente de administração baseado em Blazor do aplicativo, que requer um projeto de API separado para ser executado também.

O aplicativo pode ser iniciado a partir da raiz da solução usando os docker-compose build comandos and docker-compose up . Este comando configura um contêiner para a instância da Web, usando o Dockerfile encontrado na raiz do projeto da Web, e executa o contêiner em uma porta especificada. Você pode baixar a fonte para este aplicativo do GitHub e executá-lo localmente. Até mesmo esse aplicativo monolítico se beneficia de ser implantado em um ambiente de contêiner.

Por um lado, a implantação em contêiner significa que cada instância do aplicativo é executada no mesmo ambiente. Essa abordagem inclui o ambiente do desenvolvedor onde os primeiros testes e desenvolvimento ocorrem. A equipe de desenvolvimento pode executar o aplicativo em um ambiente conteinerizado que corresponda ao ambiente de produção.

Além disso, os aplicativos em contêineres são dimensionados a um custo menor. O uso de um ambiente de contêiner permite um compartilhamento de recursos maior do que os ambientes de VM tradicionais.

Finalmente, a conteinerização do aplicativo força uma separação entre a lógica de negócios e o servidor de armazenamento. À medida que o aplicativo se expande, os vários contêineres dependerão de um único meio de armazenamento físico. Esse meio de armazenamento normalmente seria um servidor de alta disponibilidade executando um banco de dados do SQL Server.

Suporte ao Docker

O eShopOnWeb projeto é executado em .NET. Portanto, ele pode ser executado em contêineres baseados em Linux ou Windows. Observe que, para a implantação do Docker, você deseja usar o mesmo tipo de host para o SQL Server. Os contêineres baseados em Linux permitem uma pegada menor e são preferidos.

Você pode usar o Visual Studio 2017 ou posterior para adicionar suporte ao Docker a um aplicativo existente clicando com o botão direito do mouse em um projeto no Gerenciador de Soluções e escolhendo Adicionar>Suporte ao Docker. Esta etapa adiciona os arquivos necessários e modifica o projeto para usá-los. O exemplo atual eShopOnWeb já tem esses arquivos no lugar.

O arquivo de nível docker-compose.yml de solução contém informações sobre quais imagens criar e quais contêineres iniciar. O arquivo permite que você use o docker-compose comando para iniciar vários aplicativos ao mesmo tempo. Neste caso, ele está apenas iniciando o projeto Web. Você também pode usá-lo para configurar dependências, como um contêiner de banco de dados separado.

version: '3'

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

networks:
  default:
    external:
      name: nat

O docker-compose.yml arquivo faz referência ao DockerfileWeb no projeto. O Dockerfile é usado para especificar qual contêiner base será usado e como o aplicativo será configurado nele. O 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"]

Solução de problemas do Docker

Depois de executar o aplicativo em contêiner, ele continua a ser executado até que você o interrompa. Você pode visualizar quais contêineres estão sendo executados com o docker ps comando. Você pode parar um contêiner em execução usando o docker stop comando e especificando o ID do contêiner.

Observe que a execução de contêineres do Docker pode estar vinculada a portas que você poderia tentar usar em seu ambiente de desenvolvimento. Se você tentar executar ou depurar um aplicativo usando a mesma porta de um contêiner do Docker em execução, receberá um erro informando que o servidor não pode se vincular a essa porta. Mais uma vez, parar o contêiner deve resolver o problema.

Se você quiser adicionar suporte ao Docker ao seu aplicativo usando o Visual Studio, verifique se o Docker Desktop está em execução quando você fizer isso. O assistente não será executado corretamente se o Docker Desktop não estiver em execução quando você iniciar o assistente. Além disso, o assistente examina sua opção de contêiner atual para adicionar o suporte correto ao Docker. Se quiser adicionar suporte para Contêineres do Windows, você precisará executar o assistente enquanto tiver o Docker Desktop em execução com os Contêineres do Windows configurados. Se você quiser adicionar, suporte para contêineres Linux, execute o assistente enquanto você tem o Docker em execução com contêineres Linux configurados.

Outros estilos de arquitetura de aplicativos Web

  • Web-Queue-Worker: Os principais componentes dessa arquitetura são um front-end da Web que atende às solicitações do cliente e um trabalhador que executa tarefas que consomem muitos recursos, fluxos de trabalho de longa execução ou trabalhos em lote. O front-end da Web comunica com a função de trabalho através de uma fila de mensagens.
  • N camadas: uma arquitetura de N camadas divide um aplicativo em camadas lógicas e camadas físicas.
  • Microsserviço: Uma arquitetura de microsserviços consiste em uma coleção de serviços pequenos e autônomos. Cada serviço é autônomo e deve implementar um único recurso de negócios dentro de um contexto limitado.

Referências – Arquiteturas web comuns