ASP.NET Core Basics: Organizing Projects with Architectural Patterns

Noções básicas do ASP.NET Core: Organizando projetos com padrões arquitetônicos

Organizar projetos no ASP.NET Core é essencial para manter um código limpo e gerenciável. Padrões de arquitetura podem ajudar. Confira este post do blog sobre a implementação de um dos padrões de arquitetura mais conhecidos, o padrão onion.

Padrões de arquitetura são abordagens de design que ajudam a organizar e estruturar aplicativos web para ótima manutenibilidade, escalabilidade e flexibilidade.

Este post explorará um dos padrões mais amplamente adotados atualmente: a arquitetura onion. Além disso, discutiremos a aplicação prática desse padrão em uma aplicação ASP.NET Core.

O que são padrões arquitetônicos?

Padrões de arquitetura são soluções ou modelos de design de alto nível que fornecem uma abordagem estruturada para organizar e projetar a arquitetura de sistemas de software.

Esses padrões oferecem um conjunto de melhores práticas e diretrizes para resolver problemas comuns de design que os desenvolvedores encontram ao desenvolver aplicativos complexos. Os padrões de arquitetura ajudam os sistemas de software a serem escaláveis, fáceis de manter e adaptáveis ​​a requisitos em constante mudança.

As principais características dos padrões arquitetônicos incluem:

  1. Reutilização: Padrões arquitetônicos são soluções reutilizáveis ​​que podem ser aplicadas a diferentes projetos e domínios. Eles encapsulam a expertise em design, facilitando a aplicação de conceitos de design comprovados pelos desenvolvedores.

  2. Abstração: Os padrões fornecem um nível de abstração que se concentra na estrutura e organização de alto nível de um sistema, em vez de detalhes específicos de implementação. Essa abstração permite que os desenvolvedores pensem na arquitetura do sistema de uma forma mais conceitual e geral.

  3. Escalabilidade: Os padrões de arquitetura são projetados para acomodar o crescimento futuro e as mudanças nos requisitos. Eles ajudam a garantir que um sistema possa ser dimensionado em termos de funcionalidade e desempenho.

  4. Manutenção: Ao promover uma separação clara de preocupações e a modularização de componentes, os padrões arquitetônicos facilitam a manutenção e a extensão de um sistema de software ao longo do tempo.

  5. Consistência: Os padrões estabelecem uma estrutura e uma abordagem de design consistentes, o que pode ser benéfico em ambientes de equipe e grandes projetos de software.

  6. Documentação: Os padrões vêm com uma grande quantidade de documentação e recursos, o que ajuda os desenvolvedores a entendê-los e aplicá-los de forma eficaz.

No contexto do ASP.NET Core, existem alguns padrões amplamente utilizados, incluindo:

  • Arquitetura de cebola
  • Arquitetura hexagonal (portas e adaptadores)
  • Arquitetura limpa (evolução da arquitetura onion)
  • Arquitetura sem servidor

Nesta postagem, aprenderemos sobre um desses padrões: o padrão de arquitetura onion.

O que é o padrão de arquitetura Onion?

O padrão de arquitetura onion é um padrão de arquitetura de software amplamente utilizado no ASP.NET Core e em outros frameworks modernos de desenvolvimento de aplicações. É uma variação da arquitetura tradicional em camadas que promove uma forma mais flexível e sustentável de projetar e estruturar aplicações. Jeffrey Palermo popularizou o padrão de arquitetura onion, que é particularmente adequado e recomendado para a construção de aplicações robustas, sustentáveis ​​e testáveis.

A ideia principal da arquitetura onion é organizar o aplicativo em círculos ou camadas concêntricas, com cada camada dependendo apenas das camadas internas.

The Onion pattern

A seguir, vamos aprender sobre e implementar cada uma das quatro camadas principais de um aplicativo típico de arquitetura onion no ASP.NET Core.

Criando o Projeto

Para criar o projeto de amostra, você precisa ter o seguinte:

  • .NET 7 ou superior instalado
  • IDE de sua escolha (este post usará o Visual Studio Code)

Você pode acessar o código-fonte completo aqui: Código fonte .

Ao final do post, o projeto completo terá a seguinte estrutura:

Project structure

Primeiro, vamos criar um projeto de solução ASP.NET Core, onde armazenaremos as camadas da aplicação. Então, no terminal, execute o seguinte comando:

 dotnet new sln -n BookingFast

Implementando as Camadas

1. Camada de Domínio

Esta é a camada mais interna e contém a parte mais crítica da lógica de negócios, representando o núcleo da aplicação, e deve ser completamente independente de quaisquer estruturas externas. Na camada central, você define seus modelos de domínio, regras de negócios e lógica específica da aplicação. Esta camada não deve ter dependências das outras camadas e é frequentemente chamada de camada "Domínio" ou "Entidades".

Para criar a camada de domínio no projeto e adicioná-la à classe de solução, use os seguintes comandos:

 dotnet new classlib -n BookingFast.Domain
 dotnet sln BookingFast.sln add BookingFast.Domain/BookingFast.Domain.csproj

Agora dentro da pasta “BookingFast.Domain”, crie uma nova pasta chamada “Entities” e dentro dela crie a seguinte classe:

  • Reserva
namespace BookingFast . Domain . Entities ;

 public class Reservation
 {
 public Reservation ( Guid id , string guestName , DateTime checkInDate , DateTime checkOutDate , string status )
 {
 Id = id ; 
NomeDoConvidado = NomeDoConvidado ;
 DataDeVerificação = DataDeVerificação ;
 DataDeSaída = dataDeSaída ;
 Status = estado ;
 }

 Id do GUID público { obter ; definir ; } 
string pública ? NomeDoConvidado { obter ; definir ; }
 Data e hora públicas CheckInDate { obter ; definir ; }
 Data e hora públicas CheckOutDate { obter ; definir ; } 
string pública ? Status { obter ; definir ; }
 }

A classe “Reserva” é a entidade principal da nossa aplicação, portanto ela pertence à camada de domínio, que é a camada mais interna em uma estrutura de cebola.

Em seguida, vamos criar a camada de infraestrutura.

2. Camada de Infraestrutura

A camada de infraestrutura é responsável por interagir com sistemas, estruturas e serviços externos. No contexto do ASP.NET Core, essa camada inclui código relacionado ao acesso a dados, comunicação com serviços externos e outras questões de infraestrutura. Essa camada pode ter dependências de bibliotecas externas, frameworks e do próprio ASP.NET Core.

Para criar a camada de infraestrutura e adicioná-la à solução, na raiz do projeto execute os seguintes comandos:

 dotnet new classlib -n BookingFast.Infrastructure
 dotnet sln BookingFast.sln add BookingFast.Infrastructure/BookingFast.Infrastructure.csproj

Primeiro, precisamos baixar as dependências para a camada de infraestrutura, então abra um terminal no projeto de infraestrutura e execute os seguintes comandos:

 dotnet add package Microsoft.Extensions.Options.ConfigurationExtensions --version 8.0.0
 dotnet add package MongoDB.Driver --version 2.22.0

Agora, vamos criar a classe que conterá as variáveis ​​responsáveis ​​por armazenar os valores da string de conexão do banco de dados.

Em seguida, dentro da pasta “BookingFast.Infrastructure”, crie uma nova pasta chamada “Repositories”. Dentro dela, crie uma nova classe chamada “StudentDatabaseSettings” e insira o seguinte código nela:

namespace BookingFast.Infrastructure.Repositories;
public class ReservationsDatabaseSettings
{
    public string ConnectionString { get; set; } 
string pública DatabaseName { obter ; definir ; }
 string pública CollectionName { obter ; definir ; }
 }

Neste exemplo, criaremos um banco de dados no MongoDB Atlas, que é um banco de dados muito simples. Para isso, primeiro você precisa criar um servidor no MongoDB Atlas e criar o banco de dados. Se você é novo no MongoDB Atlas, recomendo este guia para criar e configurar seu primeiro cluster: Introdução ao MongoDB Atlas (guia da interface do Atlas).

Com o cluster configurado, podemos criar um banco de dados “reservation_db” e uma coleção “students” como na imagem abaixo:

Create database

Para conectar nossa aplicação ao cluster e acessar o banco de dados criado, precisamos obter a string de conexão, que usaremos posteriormente. Para obtê-la, basta seguir os passos mostrados nas imagens abaixo:

No seu banco de dados, clique em “Conectar” > “Drivers” e na janela copie a string de conexão, conforme mostrado na imagem abaixo.

Get connection string

Agora que temos a conexão com o cluster, vamos implementar a configuração no projeto. Substitua o código no arquivo "appsettings.json" da camada "BookingFast.UI" pelo código abaixo:

 {
 "ReservationsDatabaseSettings" : {
 "ConnectionString" : "<your cluster connection>" ,
 "DatabaseName" : "reservations_db" ,
 "CollectionName" : "reservations" , 
"IsSSL" : verdadeiro
 } ,
 "Registro" : {
 "Nível de Log" : {
 "Padrão" : "Informações" ,
 "Microsoft.AspNetCore" : "Aviso"
 }
 } , 
"AllowedHosts" : "*"
 }

No código acima, substitua "<your cluster connection>" com sua conexão de cluster obtida anteriormente. Além disso, lembre-se de substituir "<password>" e
"<username>" com a senha e o nome de usuário do cluster.

Agora vamos criar a interface do repositório com os métodos responsáveis ​​pelas operações do banco de dados.

Em uma arquitetura onion, uma interface de repositório geralmente é encontrada na camada de domínio, pois os repositórios são parte da lógica de acesso a dados e são uma parte fundamental do domínio do aplicativo.

Então, dentro da pasta “BookingFast.Domain”, crie uma nova pasta chamada “Infra”. Dentro dela, crie outra pasta com o nome “Interfaces” e adicione a seguinte interface dentro dela:

  • Repositório de Reservas I
 using BookingFast . Domain . Entities ;

 namespace BookingFast . Domain . Infra . Interfaces ;

 public interface IReservationsRepository
 { 
Tarefa < IEnumerable < Reserva > > FindAllReservations ( ) ;
 Tarefa InsertReservation ( Reserva reserva ) ;
 Tarefa UpdateReservationStatus ( string status ) ;
 }

Para criar a classe Repository, utilizaremos a camada de infraestrutura, então dentro da pasta “BookingFast.Infrastructure”, dentro da pasta “Repositories” crie a classe abaixo:

Repositório de Reservas

 using BookingFast . Domain . Entities ;
 using BookingFast . Domain . Infra . Interfaces ;
 using Microsoft . Extensions . Options ;
 using MongoDB . Driver ;
 
namespace BookingFast . Infraestrutura . Repositórios ;

 classe pública ReservationsRepository : IReservationsRepository
 {
 privado somente leitura IMongoCollection < Reserva > _reservas ;

 public ReservationsRepository ( IOptions < ReservationsDatabaseSettings > opções ) 
{
 var mongoClient = new MongoClient ( opções . Valor . ConnectionString ) ;

 _reservas = mongoClient
 . ObterBancoDeDados ( opções . Valor . NomeDoBancoDeDados ) 
. GetCollection < Reserva > ( opções . Valor . CollectionName ) ;
 }

 tarefa assíncrona pública < IEnumerable < Reserva > > FindAllReservations ( )
 { 
se ( _reservas == nulo )
 retornar Enumerável . Vazio < Reserva > ( ) ;
 
retornar aguardar _reservas . Encontrar ( _ = > verdadeiro ) . ToListAsync ( ) ;
 }

 tarefa pública assíncrona InsertReservation ( reserva reserva )
 { 
aguardar _reservas . InsertOneAsync ( reserva ) ;
 }

 tarefa pública assíncrona UpdateReservationStatus ( string status , Guid id )
 { 
var reserva = await _reservas . Find ( a = > a . Id == id ) . SingleOrDefaultAsync ( ) ;
 reserva . Status = status ; 
aguardar _reservas . ReplaceOneAsync ( a = > a . Id == id , reserva ) ;
 }
 }

Observe que usamos a interface "IReservationsRepository", que é da camada de domínio. Para usá-la, precisamos adicionar a referência à camada de domínio na camada de infraestrutura. Então, clique duas vezes no arquivo "BookingFast.Infrastructure.csproj" e adicione o código abaixo:

 < ItemGroup > 
< ProjectReference Include = "..\BookingFast.Domain\BookingFast.Domain.csproj" / >
 < / Grupo de Itens >

A camada de Infraestrutura está pronta, o próximo passo é implementar a camada de Aplicação onde criaremos a classe de serviço.

3. Camada de Aplicação

O próximo círculo concêntrico é a camada de aplicação, que depende da camada de domínio, mas também não deve ter dependências de frameworks externos. Esta camada contém serviços específicos da aplicação, casos de uso e lógica da aplicação. Ela atua como intermediária entre a camada principal e camadas externas, como as camadas de interface do usuário e infraestrutura.

Para criar a camada de aplicação e adicioná-la à solução, execute os seguintes comandos no terminal, na raiz do projeto:

dotnet new classlib -n BookingFast.Application
 dotnet sln BookingFast.sln add BookingFast.Application/BookingFast.Application.csproj

Primeiro, vamos criar as classes de Objetos de Transferência de Dados (DTOs), que serão as classes expostas à camada de interface do usuário e que representam o modelo de entidade. Neste caso, será a classe "ReservationDto".

Então, dentro da pasta “BookingFast.Application”, crie uma nova pasta chamada “Dtos” e dentro dela crie uma nova classe chamada “ReservationDto”. Coloque o código abaixo nela:

 using BookingFast . Domain . Entities ;

 namespace BookingFast . Application . Dtos ;
 
classe pública ReservationDto
 {
 Reserva públicaDto ( ) { }

 public ReservationDto ( Reserva reserva )
 {
 Id = reserva . Id ;
 GuestName = reserva . GuestName ; 
DataDeVerificação = reserva . DataDeVerificação ;
 DataDeSaída = reserva . DataDeSaída ;
 Status = reserva . Status ;
 }

 Id do GUID público { obter ; definir ; } 
string pública ? NomeDoConvidado { obter ; definir ; }
 Data e hora públicas CheckInDate { obter ; definir ; }
 Data e hora públicas CheckOutDate { obter ; definir ; } 
string pública ? Status { obter ; definir ; }
 }

Aqui também precisamos adicionar as dependências das outras camadas, que são o domínio e a infraestrutura, então clique duas vezes no arquivo “BookingFast.Application” e adicione o código abaixo:

 < ItemGroup >
 < ProjectReference Include = "..\BookingFast.Domain\BookingFast.Domain.csproj" / > 
< ProjectReference Include = "..\BookingFast.Infrastructure\BookingFast.Infrastructure.csproj" / >
 < / Grupo de Itens >

O próximo passo é criar a classe de serviço e os métodos para realizar operações bancárias por meio da camada de infraestrutura. Dentro da pasta "BookingFast.Application", crie uma nova pasta chamada "Services" e, dentro dela, crie a seguinte interface e classe:

  • Serviço de Reservas
 using BookingFast . Application . Dtos ;
 
namespace BookingFast . Aplicativo . Serviços ;

 interface pública IReservationsService
 {
 Tarefa < Lista < ReservationDto > > FindAllReservations ( ) ;
 Tarefa CreateNewReservation ( ReservationDto reserva ) ; 
Tarefa UpdateReservationStatus ( string status , Guid id ) ;
 }
  • Serviço de Reservas
 using BookingFast . Application . Dtos ;
 using BookingFast . Domain . Entities ;
 using BookingFast . Domain . Infra . Interfaces ;
 
namespace BookingFast . Aplicativo . Serviços ;

 classe pública ReservationsService : IReservationsService
 {
 privado somente leitura IReservationsRepository _reservationsRepository ;

 Serviço de Reservas públicas ( IReservationsRepository reservationsRepository )
 {
 _reservationsRepository = reservasRepositório ; 
}

 tarefa pública assíncrona < Lista < ReservationDto > > FindAllReservations ( )
 {
 var reservas = await _reservationsRepository . FindAllReservations ( ) ; 
retornar reservas . Selecione ( reserva = > novo ReservaDto ( reserva ) ) . ToList ( ) ;
 }

 tarefa pública assíncrona CreateNewReservation ( ReservationDto reserva ) 
{
 var newReservation = new Reserva ( reserva . Id , reserva . GuestName , reserva . CheckInDate , reserva . CheckOutDate , reserva . Status ) ; 
aguarde _reservationsRepository . InsertReservation ( novaReserva ) ;
 }

 tarefa pública assíncrona UpdateReservationStatus ( string status , Guid id )
 { 
aguarde _reservationsRepository . UpdateReservationStatus ( status , id ) ;
 }
 }

4. Camada de interface do usuário

O círculo mais externo é a camada de interface do usuário (UI), que inclui os componentes da interface do usuário do aplicativo. No contexto do ASP.NET Core, essa camada inclui controladores, visualizações e outros componentes responsáveis ​​por manipular solicitações HTTP, entradas do usuário e renderização da UI. A camada de UI depende das camadas de aplicativo e infraestrutura, mas não deve conter nenhuma lógica de negócios. Ela lida principalmente com as interações do usuário e invoca os serviços do aplicativo.

Para criar o projeto de interface do usuário, execute o comando abaixo:

 dotnet new web -n BookingFast.UI

Este comando criará um novo projeto usando o modelo de APIs Mínimas do ASP.NET Core. Em seguida, execute os seguintes comandos para adicionar o projeto "BookingFast.UI" à classe de solução:

 dotnet sln BookingFast.sln add BookingFast.UI/BookingFast.UI.csproj

Agora, vamos adicionar a referência à camada de aplicação. Clique duas vezes no arquivo "BookingFast.UI.csproj" e adicione o seguinte trecho de código:

 < ItemGroup >
 < ProjectReference Include = "..\BookingFast.Application\BookingFast.Application.csproj" / >
 < / ItemGroup >

Em seguida, vamos baixar os pacotes NuGet para a camada de interface do usuário. Abra um terminal no projeto de interface do usuário e execute os seguintes comandos:

 dotnet add package Microsoft.AspNetCore.OpenApi --version 8.0.0
 dotnet add package Swashbuckle.AspNetCore --version 6.5.0

O próximo passo é criar o controlador, que chamará os métodos da classe de serviço e exporá os dados por meio dos endpoints.

Na camada UI, crie uma nova pasta chamada “Controllers”. Dentro dela, crie um novo arquivo chamado “ReservationsController.cs” e insira o código abaixo nele:

 using BookingFast . Application . Dtos ;
 using BookingFast . Application . Services ; 
usando Microsoft . AspNetCore . Mvc ;

 namespace BookingFast . UI . Controladores ;

 [ ApiController ]
 [ Rota ( "[controlador]" ) ]
 classe pública ReservationsController : Controlador
 { 
privado somente leitura IReservationsService _reservationsService ;

 Controlador de Reservas público ( IReservationsService reservationsService )
 {
 _reservationsService = reservasService ;
 }

 [ HttpGet ] 
tarefa assíncrona pública < ResultadoDaAção < Lista < ReservaDto > > > FindAllReservations ( )
 {
 var reservas = aguardar _reservationsService . LocalizarTodasReservas ( ) ; 
retornar Ok ( reservas ) ;
 }

 [ HttpPost ]
 tarefa pública assíncrona < IActionResult > CreateNewReservation ( [ FromBody ] ReservationDto reserva )
 { 
aguardar _reservationsService . CreateNewReservation ( reserva ) ;
 retornar Ok ( ) ;
 }

 [ HttpPut ( "{id}" ) ] 
tarefa pública assíncrona < IActionResult > UpdateReservationStatus ( string status , Guid id )
 {
 aguarde _reservationsService . UpdateReservationStatus ( status , id ) ;
 retornar Ok ( ) ; 
}
 }

O último passo é configurar a injeção de dependências das classes. No arquivo "Program.cs", substitua o código existente pelo código abaixo:

 using BookingFast . Application . Services ;
 using BookingFast . Domain . Infra . Interfaces ;
 using BookingFast . Infrastructure . Repositories ;
 
var builder = WebApplication . CreateBuilder ( args ) ;
 construtor . Serviços . Configurar < ReservationsDatabaseSettings > ( construtor . Configuração . GetSection ( "ReservationsDatabaseSettings" ) ) ; 
construtor . Serviços . AddSingleton < IReservationsRepository , ReservationsRepository > ( ) ;
 construtor . Serviços . AddScoped < IReservationsService , ReservationsService > ( ) ;
 
construtor . Serviços . AddControllers ( ) ;
 construtor . Serviços . AddEndpointsApiExplorer ( ) ;
 construtor . Serviços . AddSwaggerGen ( ) ;
 
var app = construtor . Construir ( ) ;

 se ( app . Ambiente . ÉDesenvolvimento ( ) )
 {
 aplicativo . UseSwagger ( ) ; 
aplicativo . UseSwaggerUI ( ) ;
 }

 aplicativo . UseHttpsRedirection ( ) ;

 aplicativo . UseAuthorization ( ) ;

 aplicativo . MapControllers ( ) ;
 
aplicativo . Executar ( ) ;

Testando o aplicativo

Para testar o aplicativo, abra um terminal no projeto UI e execute o seguinte comando:

 dotnet run

No navegador, acesse o endereço http://localhost:5202/swagger/index.html e você pode executar as operações na interface do Swagger, como mostrado no GIF abaixo:

Testando o aplicativo

Conclusão

Em resumo, o padrão arquitetônico onion se destaca como uma abordagem notável para estruturar e sustentar projetos ASP.NET Core com eficiência. Ao longo deste artigo, exploramos os fundamentos desse padrão e examinamos sua aplicação prática.

Ao criar um novo projeto, considere usar o padrão onion. Dessa forma, você não apenas aproveitará as vantagens estruturais oferecidas pelo padrão, como também investirá em um código mais legível, sustentável e de fácil manutenção.

Voltar para o blogue
  • ChatGPT Uncovered Podcast

    Podcast descoberto do ChatGPT

    Pedro Martins

    Podcast descoberto do ChatGPT Podcast descoberto do ChatGPT Explorando as fronteiras dos modelos de conversação de IA Episódio 1: Compreendendo o ChatGPT Publicado em: 15 de maio de 2023 Seu...

    Podcast descoberto do ChatGPT

    Pedro Martins

    Podcast descoberto do ChatGPT Podcast descoberto do ChatGPT Explorando as fronteiras dos modelos de conversação de IA Episódio 1: Compreendendo o ChatGPT Publicado em: 15 de maio de 2023 Seu...

  • Power Apps In-Depth Podcast

    Podcast detalhado do Power Apps

    Pedro Martins

    Podcast detalhado do Power Apps Podcast detalhado do Power Apps Explorando os recursos do Microsoft Power Apps Episódio 1: Introdução ao Power Apps Publicado em: 20 de abril de 2023...

    Podcast detalhado do Power Apps

    Pedro Martins

    Podcast detalhado do Power Apps Podcast detalhado do Power Apps Explorando os recursos do Microsoft Power Apps Episódio 1: Introdução ao Power Apps Publicado em: 20 de abril de 2023...

  • Exploring Power Pages Podcast

    Explorando o podcast Power Pages

    Pedro Martins

    Explorando o podcast Power Pages Explorando o podcast Power Pages Mergulhando no mundo das Power Pages da Microsoft Episódio 1: Primeiros passos com Power Pages Publicado em: 10 de março...

    Explorando o podcast Power Pages

    Pedro Martins

    Explorando o podcast Power Pages Explorando o podcast Power Pages Mergulhando no mundo das Power Pages da Microsoft Episódio 1: Primeiros passos com Power Pages Publicado em: 10 de março...

1 de 3