Hoje vamos criar uma aplicação Blazor usando os conceitos da Clean Architecture. |
Se você esta chegando agora e não sabe o que é o Blazor leia o artigo ASP .NET Core - Iniciando com o Blazor - Macoratti; se você já conhece e quer saber mais pode fazer o curso de Blazor Essencial.
E o que é
Clean Architecture ?
Arquitetura limpa refere-se à organização do projeto de forma que seja fácil de
entender e fácil de mudar conforme o projeto cresce. Isso não acontece por
acaso. É preciso um planejamento intencional para que isso ocorra.
O segredo para construir um grande projeto que seja fácil de manter é este:
Vamos ilustrar isso com algumas imagens.
Vamos iniciar com uma imagem onde vamos representar o domínio e a infraestrutura da aplicação.
No círculo interno temos a camada de domínio da aplicação. É aqui que você coloca as regras de negócios que representa a alma do seu aplicativo, a funcionalidade central do seu projeto.
O círculo externo
é onde esta a infraestrutura. Aqui você inclui a
interface com o usuário (UI), o banco de dados, as APIs da web e frameworks.
Essas coisas têm mais probabilidade de mudar do que o domínio. Por exemplo, é
mais provável que você mude a aparência de um botão na interface do que mudar
uma regra do seu negócio.
Uma fronteira entre o domínio e a infraestrutura é configurada de forma que o
domínio não saiba nada sobre a infraestrutura. Isso significa que a IU e o banco
de dados dependem das regras de negócios, mas as regras de negócios não dependem
da IU ou do banco de dados.
Não importa se a IU é uma interface web, um aplicativo desktop ou um aplicativo mobile. Não importa se os dados são armazenados usando SQL Server, MySQL ou NoSQL ou na nuvem. O domínio não se importa. Isso torna mais fácil mudar a infraestrutura.
A seguir vamos usar outra imagem para definir alguns termos com mais detalhes:
Agora a camada de domínio foi subdividida em Entidades e Casos de uso, e uma camada adaptadora (Adapters) forma a fronteira entre o domínio e a camada de infraestrutura.
Dessa forma ao iniciar um projeto, você deve primeiro trabalhar nas regras de negócios. Todas as outras coisas são detalhes. O banco de dados é um detalhe. A UI é um detalhe. O SO é um detalhe. Uma web API é um detalhe. Uma framework é um detalhe.
Adie as decisões sobre eles pelo maior tempo possível, assim, quando precisar deles, você estará em uma posição melhor para fazer uma escolha inteligente. Esses detalhes não importam para o seu desenvolvimento inicial, porque as camadas de domínio não sabem nada sobre a infraestrutura.
Quando estiver pronto para escolher um banco de dados, preencha o código do adaptador de banco de dados e conecte-o. Quando estiver pronto para criar a IU, preencha o código do adaptador de IU e conecte-o. Entendeu ?
Os componentes da regra de
negócios são mais estáveis e não devem saber nada sobre
os componentes de infraestrutura que são mais voláteis,
os que lidam com a interface do usuário, o banco de
dados, web, frameworks e outros detalhes.
O limite entre as camadas de componentes é mantido
usando adaptadores de interface que traduzem os
dados entre as camadas e mantêm as dependências
apontando na direção dos componentes internos mais
estáveis.
Para ilustrar a aplicações dos conceitos da Clean Architecture no Blazor vamos criar uma aplicação Blazor bem simples onde vamos criar um catálogo de produtos de beleza e permitir a exibição dos detalhes de um produto selecionado.
Assim nossa aplicação terá duas regras de negócio : Procurar Produto e Exibir Produto
Dessa forma o projeto da aplicação terá duas páginas e três casos de uso :
a- Páginas
b- Casos de uso
No contexto da Clean Architecture esses casos de uso orquestram o fluxo de dados de e para as entidades e direcionam essas entidades a usarem suas Regras de negócios críticas para atingir os objetivos do caso de uso.
Com isso estabelecido vamos iniciar a criação da nossa aplicação criando uma solução em branco chamada eCatalogo e a seguir criando os seguintes projetos:
Recursos usados:
Criando a solução e os projetos no VS 2019
Vamos iniciar criando uma Blank Solution chamada eCatalogo.
A seguir vamos criar os projetos individuais na solução.
No menu File selecione Add -> New Project, selecione o template Class Library(.NET Standard) e informe o nome eCatalogo.Core;
A seguir vamos repetir o procedimento acima e criar os projetos do tipo Class Library(.NET Standard) nomeados como eCatalogo.Data e eCatalogo.UseCases.
Para criar o projeto Blazor selecione o menu File-> Add-> New Project e selecione a opção Blazor app e clique em next;
Informe o nome do projeto : eCatalogo.Blazor a localização e clique em Create;
Na próxima janela selecione .NET 5.0 e marque a opção - Blazor Server App e clique no botão Create para criar o projeto.
A seguir vamos remover do projeto Blazor criado a pasta Data e as referências a WeatherforeCast do projeto. Também vamos remover da pasta Pages os componentes Counter.razor e FetchData.razor e da pasta Shared o componente SurveyPrompt.razor.
Ao final teremos a seguinte estrutura para a nossa solução:
Definindo os relacionamentos entre os projetos
Vamos agora definir as referências entre os projetos criados da seguinte forma:
Para incluir uma referência aos projetos eCatalogo.Core, eCatalogo.Data e eCatalogo.UseCases no projeto Blazor, clique com o botão direito do mouse sobre o projeto eCatalogo.Blazor e selecione Add-> Project Reference;
A seguir marque os projetos conforme indicado abaixo:
Repita o procedimento acima para os demais projetos incluindo as respectivas referências conforme indicado.
Definindo o projeto Core
Vamos iniciar definindo os recursos do projeto eCatalogo.Core onde vamos criar uma pasta Models e nesta pasta vamos criar a classe Produto que representa o nosso modelo de domínio:
public class Produto { public int ProdutoId { get; set; } public string Nome { get; set; } public string Descricao { get; set; } public decimal Preco { get; set; } public string ImagemUrl { get; set; } public string Categoria { get; set; } } |
Aqui criamos uma classe POCO anêmica bem simples sem comportamento algum para simplificar o nosso projeto.
Usando uma abordagem mais aderente deveríamos definir a entidade Produto com get e set privados e identificar as propriedades candidatas a serem definidas como Value Objects.
Definindo o projeto UseCases
O projeto UseCases representa a nossa aplicação e nele vamos criar as pastas Catalogo e Interfaces.
Na pasta Interfaces vamos criar a interface IProdutoRepository com o contrato para obter um produto pelo id e uma lista de produtos:
using eCatalogo.Core.Models;
using System.Collections.Generic;
namespace eCatalogo.UseCases.Interfaces.Repository
{
public interface IProdutoRepository
{
Produto GetProduto(int id);
IEnumerable<Produto> GetProdutos(string filter = null);
}
}
|
Note que podemos obter todos os produtos ou também fazer o filtro dos produtos retornando uma seleção de produtos.
Na pasta Catalogo vamos criar as interfaces e as implementações para exibir e procurar produtos. Começando com as interfaces:
1- IExibeProduto
using eCatalogo.Core.Models;
namespace eCatalogo.UseCases.Catalogo
{
public interface IExibeProduto
{
Produto Execute(int id);
}
}
|
2- IProcuraProduto
using eCatalogo.Core.Models;
using System.Collections.Generic;
namespace eCatalogo.UseCases.Catalogo
{
public interface IProcuraProduto
{
IEnumerable<Produto> Execute(string filter = null);
}
}
|
A seguir vamos criar as classes que implementam essas interfaces:
1- ExibeProduto
using eCatalogo.Core.Models;
using eCatalogo.UseCases.Interfaces.Repository;
namespace eCatalogo.UseCases.Catalogo
{
public class ExibeProduto : IExibeProduto
{
private readonly IProdutoRepository produtoRepository;
public ExibeProduto(IProdutoRepository produtoRepository)
{
this.produtoRepository = produtoRepository;
}
public Produto Execute(int id)
{
return produtoRepository.GetProduto(id);
}
}
}
|
2- ProcuraProduto
using eCatalogo.Core.Models;
using eCatalogo.UseCases.Interfaces.Repository;
using System.Collections.Generic;
namespace eCatalogo.UseCases.Catalogo
{
public class ProcuraProduto : IProcuraProduto
{
private readonly IProdutoRepository produtoRepository;
public ProcuraProduto(IProdutoRepository produtoRepository)
{
this.produtoRepository = produtoRepository;
}
public IEnumerable<Produto> Execute(string filter = null)
{
return produtoRepository.GetProdutos(filter);
}
}
}
|
Na implementação estamos injetando no construtor das classes concretas um instância do repositório de dados que vamos implementar no projeto eCatalogo.Data.
Definindo o projeto Data
No projeto eCatalogo.Data vamos definir a implementação do nosso repositório onde vamos usar dados fictícios a partir da memória em uma lista de produtos.
Para isso vamos criar uma pasta Repository neste projeto e nesta pasta criar a classe ProdutoRepository que implementa a interface IProdutoRepository:
using eCatalogo.Core.Models; using eCatalogo.UseCases.Interfaces.Repository; using System.Collections.Generic; using System.Linq;
namespace eCatalogo.Data.Repository
public ProdutoRepository()
public Produto GetProduto(int id)
public IEnumerable<Produto> GetProdutos(string
filter = null)
return produtos.Where(x => x.Nome.ToLower().Contains(filter.ToLower())); |
Criamos assim um repositório fake que usaremos para testar nossa aplicação. Poderemos facilmente depois mudar essa implementação para um repositório real.
A seguir na próxima parte do artigo vamos definir o projeto Blazor que é a nossa camada de apresentação.
(Disse Jesus)
"E, se o teu olho te escandalizar, lança-o fora; melhor é para ti entrares no
reino de Deus com um só olho do que, tendo dois olhos, seres lançado no fogo do
inferno, Onde o seu bicho não morre, e o fogo nunca se apaga."
Marcos 9:47,48
ASP .NET Core - Iniciando com o Blazor
ASP .NET Core - CRUD usando Blazor e Entity ...
Blazor - O novo framework SPA da Microsoft -
Visual Studio Code - Suporte ao desenvolvimento Blazor