ASP .NET Core - Implementando a Onion Architecture - III
Neste artigo vamos criar uma aplicação ASP .NET Core WEBAPI fazendo uma implementação básica da arquitetura em Cebola ou Onion Architecture. |
Continuando a segunda parte do artigo vamos iniciar a implementação da camada Core no projeto eStore.Application.
Implementação da camada Core : projeto eStore.Application
O projeto Application representa a camada Application da Onion Architecture e esta camada é a ponte entre a infraestrutura externa e as camadas de domínio. As camadas de domínio geralmente precisam de informações ou funcionalidade para completar a funcionalidade de negócios, no entanto, não devem depender diretamente delas. Em vez disso, a camada de aplicativo precisa depender dos contratos definidos na camada de Serviços de Domínio.
Assim nesta camada vamos definir as interfaces dos serviços e suas implementações, os DTOs e os mapeamentos, e outros recursos que permitiram acessar a camada de domínio.
Assim esta camada vai precisar possuir uma dependência com a camada de domínio e para isso vamos clicar com o botão direito sobre o projeto Application e selecionar Add-> Project Reference;
A seguir na janela a seguir marcar o projeto eStore.Domain e clicar em OK;
Vamos iniciar definindo os Data Transfer Objects - DTOs.
Um DTO (objetos de transferência de dados) é um contêiner de dados para mover dados entre camadas. Eles também são denominados como objetos de transferência. Um DTO é usado apenas para passar dados e não contém nenhuma lógica de negócios. Eles têm apenas setters e getters simples e podem conter também Data Annotations para efeito de validação na camada de interface do cliente.
Podemos usar DTOs para realizar as seguintes tarefas:
A seguir vamos criar a pasta DTOs no projeto Application e criar as classes CategoryDTO e ProductDTO definindo as propriedades e as anotações de dados pertinentes :
1- CategoryDTO
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace eStore.Application.DTOs
{
public class CategoryDTO
{
public int Id { get; set; }
[Required(ErrorMessage = "The Name is Required")]
[MinLength(3)]
[MaxLength(100)]
[DisplayName("Name")]
public string Name { get; set; }
}
}
|
2- ProductDTO
using eStore.Domain.Entities;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace eStore.Application.DTOs
{
public class ProductDTO
{
public int Id { get; set; }
[Required(ErrorMessage = "The Name is Required")]
[MinLength(3)]
[MaxLength(100)]
public string Name { get; set; }
[Required(ErrorMessage = "The Description is Required")]
[MinLength(5)]
[MaxLength(200)]
public string Description { get; set; }
[Required(ErrorMessage = "The Price is Required")]
[Column(TypeName = "decimal(18,2)")]
[DisplayFormat(DataFormatString = "{0:C2}")]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[Required(ErrorMessage = "The Stock is Required")]
[Range(1, 9999)]
public int Stock { get; set; }
[MaxLength(250)]
public string Image { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
}
}
|
Observe que as classes DTOs definidas não possuem lógica alguma e serão usadas para transferência de dados e para não expor o nosso modelo de domínio abstraindo os objetos do domínio da camada de apresentação.
E para nos ajudar a realizar o mapeamento entre as classes do domínio e as classe DTOs vamos incluir uma referência neste projeto à biblioteca do AutoMapper:
A seguir vamos criar a pasta Mappings e nesta pasta criar a classe DomainToDTOMappingProfile e definir o mapeamento das classes Product e Category para as classes ProductDTO e CategoryDTO :
using AutoMapper;
using eStore.Application.DTOs;
using eStore.Domain.Entities;
namespace eStore.Application.Mappings
{
public class DomainToDTOMappingProfile : Profile
{
public DomainToDTOMappingProfile()
{
CreateMap<Product, ProductDTO>().ReverseMap();
CreateMap<Category, CategoryDTO>().ReverseMap();
}
}
}
|
Aqui neste código estamos mapeando as mesmas propriedades mas como nunca devemos acessar as classes do domínio diretamente vamos implementar o mapeamento pois com certeza em uma aplicação de produção você vai precisar definir este mapeamento de forma mais sensível e pontual.
A seguir vamos criar a pasta Interfaces neste projeto e definir as interfaces ICategoryService e IProductService:
1- ICategoryService
using eStore.Application.DTOs;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace eStore.Application.Interfaces
{
public interface ICategoryService
{
Task<IEnumerable<CategoryDTO>> GetCategories();
Task<CategoryDTO> GetById(int? id);
Task Add(CategoryDTO category);
Task Update(CategoryDTO category);
Task Remove(CategoryDTO category);
}
}
|
2- IProductService
using eStore.Application.DTOs;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace eStore.Application.Interfaces
{
public interface IProductService
{
Task<IEnumerable<ProductDTO>> GetProducts();
Task<ProductDTO> GetById(int? id);
Task Add(ProductDTO product);
Task Update(ProductDTO product);
Task Remove(ProductDTO product);
}
}
|
A seguir vamos criar uma pasta Services e criar as classes CategoryService e ProductService que implementam as interfaces dos serviços onde vamos acessar a implementação do repositório e usar o AutoMapper:
1- CategoryService
using AutoMapper; using eStore.Application.DTOs; using eStore.Application.Interfaces; using eStore.Domain.Entities; using eStore.Domain.Interfaces; using System.Collections.Generic; using System.Threading.Tasks; namespace eStore.Application.Services public async Task<CategoryDTO> GetById(int? id) public async Task<IEnumerable<CategoryDTO>> GetCategories() public async Task Remove(CategoryDTO category) public async Task Update(CategoryDTO category) |
2- ProductService
using AutoMapper; using eStore.Application.DTOs; using eStore.Application.Interfaces; using eStore.Domain.Entities; using eStore.Domain.Interfaces; using System.Collections.Generic; using System.Threading.Tasks; namespace eStore.Application.Services public async Task<ProductDTO> GetById(int? id) public async Task<IEnumerable<ProductDTO>> GetProducts() public async Task Remove(ProductDTO product) public async Task Update(ProductDTO product) |
Observe que na implementação feita estamos injetando as instâncias de interface do repositório e do AutoMapper e teremos que configurar e registrar estes serviços nas camadas mais externas.
A implementação refere-se ao CRUD básico bem como a consulta de produtos e categorias.
Temos assim a implementação no projeto Application da camada Core concluída.
Agora podemos nos dedicar à camada de Infrastructure onde vamos definir o contexto da aplicação usando o EF Core e implementar os repositórios e registrar os serviços criados até o momento.
Assim teremos a seguinte visão do projeto eStore.Application em nossa solução na janela Solution Explorer, onde à esquerda vemos o arquivo de projeto mostrando a dependência com o projeto Domain a a referência ao pacote do AutoMapper:
Assim o projeto Application depende do projeto Domain.
Na próxima parte do artigo iremos continuar a implementação da camada Infrastructure focando no projeto Persistence.
"Não me envergonho do evangelho, porque é o poder de Deus
para a salvação de todo aquele que crê: primeiro do judeu, depois do grego."
Romanos 1:16
Referências:
C# - Gerando QRCode - Macoratti
ASP .NET - Gerando QRCode com a API do Google
ASP .NET Core 2.1 - Como customizar o Identity
Usando o ASP .NET Core Identity - Macoratti
ASP .NET Core - Apresentando o IdentityServer4
ASP .NET Core 3.1 - Usando Identity de cabo a rabo