Blazor - Usando os conceitos da Clean Architecture - I

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 ?

A essência da Clean Architecture é que você precisa criar uma arquitetura de plugins. As classes que podem mudar ao mesmo tempo e pelo mesmo motivo devem ser agrupadas em componentes.

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

  1. Página do catálogo e Procura por produto;
  2. Página de detalhes do Produto

b- Casos de uso

  1. Procurar Produto
  2. Exibir Produto
  3. Retornar

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:

  1. eCatalogo,Blazor - Camada de interface onde vamos criar os componentes Blazor é representada pelo projeto Blazor Server;
  2. eCatalogo.Core  - Camada de domínio onde teremos as entidades e as regras de negócio do projeto. É um projeto Class Library;
  3. eCatalogo.Data  - Camada de infraestrutura, onde vamos definir o acesso aos dados representando pelo repositório. É um projeto Class Library;
  4. eCatalogo.UseCases - Camada da aplicação representando os casos de uso para mostrar o catálogo, procurar um produto e exibir os detalhes do produto. É um projeto Class Library.

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 class ProdutoRepository : IProdutoRepository
    {
        private List<Produto> produtos;

        public ProdutoRepository()
        {
            produtos = new List<Produto>
            {
                new Produto { ProdutoId = 495, Categoria = "Cosméticos", Nome = "MacBeauty Condicionador de Cabelo KPRO 165 g", Preco = 14.99m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/991799d3e70b8856686979f8ff6dcfe0_ra,w158,h184_pa,w158,h184.png", Descricao = "Cuidados intensivos para cabelos coloridos, ressecados e/ou danificados. A alta concentração de Extrato de Caviar, o mais poderoso antioxidante, associado à Queratina Hidrolisada e Ácidos Graxos essenciais, para prevenir contra o desbotamento da cor e restaurar a estrutura dos cabelos ressecados ou sensibilizados.  Proteção intensa da cor e hidratação profunda. Restauração dos fios. Cores mais vibrantes e com maior durabilidade. Controle do volume e do frizz. Cabelos desembaraçados, soltos, macios e brilhantes. Proteção solar."},

                new Produto { ProdutoId = 488, Categoria = "Cosméticos", Nome = "MacBeauty Fit Me Bronzer", Preco = 10.29m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/d4f7d82b4858c622bb3c1cef07b9d850_ra,w158,h184_pa,w158,h184.png", Descricao = "Por que você vai adorar Pigmentos leves se misturam facilmente e se desgastam uniformemente. Oferece uma cor bronzeada natural, à prova de desbotamento, que deixa a pele do jeito que deveria ser ... fresca, respirável e natural Para melhores resultados: para uma aparência suave e natural, escovar ao longo da bochecha, varrendo para cima."},

                new Produto { ProdutoId = 477, Categoria = "Cosméticos", Nome = "MacBeauty Extreme Strength Builder plus máscara - 500ml", Preco = 15.99m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/4f731de249cbd4cb819ea7f5f4cfb5c3_ra,w158,h184_pa,w158,h184.png", Descricao = "Máscara de tratamento intensivo para cabelos extremamente danificados. Redken Extreme Strength Builder Plus proporciona força, condicionamento e reparação intensa aos fios. Redken Extreme Strength Builder Plus aumenta a resistência do cabelo que sofreu danos severos, para que fiquem até 96% mais fortes já após três aplicações. Agente emoliente que desembaraça os fios enquanto proporciona maleabilidade, hidratação e brilho. Além disso, combate o estresse capilar decorrente de processos químicos. Interlock Protein Network: Fortalece a estrutura capilar interna e reduz a quebra frequente dos fios."},

                new Produto { ProdutoId = 468, Categoria = "Cosméticos", Nome = "MacBeauty Face Studio Master Hi-Light Light Booster Blush", Preco = 14.99m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/4621032a92cb428ad640c105b944b39c_ra,w158,h184_pa,w158,h184.png", Descricao = "MacBeauty  oferece nutrição intensiva enquanto você dorme para que quando acordar, seus fios estejam macios, suaves e fáceis de pentear. Ideal para qualquer tipo de cabelo seco, não pesa nas estruturas mais finas e ajuda a definir os cachos. O produto Nutritive 8H Magic Night penetra na fibra capilar durante 8 horas para uma alta absorção dos nutrientes perdidos durante a noite. Ele diminui a fricção com o travesseiro, reduz o frizz e deixa um toque sedoso. Tudo isso aliado a uma fragrância relaxante, que te fazer dormir bem. Ação:Extrato de Raiz de Íris: repõe os nutrientes perdidos durante o dia e reveste a fibra capilar. Mistura de 5 Vitaminas: ajuda na absorção dos nutrientes, sela as cutículas e confere um acabamento acetinado."},

                new Produto { ProdutoId = 439, Categoria = "Cosméticos", Nome = "MacBeauty Fit Me Blush", Preco = 10.29m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/53d5f825461117c0d96946e1029510b0_ra,w158,h184_pa,w158,h184.png", Descricao = "MacBeauty Leave-in para cabelos com frizz. Com ação hidratante, Redken Frizz Dismiss Rebel Tame Leave-in desembaraça, combate o frizz e protege do calor dos acessórios térmicos. O Leave-in Frizz Dismiss Rebel Tame Redken é um creme sem enxágue que oferece vários benefícios para todos os tipos de cabelos indisciplinados. Ele proporciona controle intensivo ao frizz enquanto deixa os fios muito mais sedosos e nutridos, além de mais fáceis de pentear.."},

                new Produto { ProdutoId = 414, Categoria = "Perfumaria", Nome = "MacBeauty Dream Shampoo Night ", Preco = 11.99m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/51eacb9efebbaee39399e65ffe3d9416_ra,w158,h184_pa,w158,h184.png", Descricao = "Shampoo de nutrição para cabelos normais e ligeiramente secos. Kérastase Nutritive Bain Satin 1 consegued nutrir os fios de maneira eficiente. Além disso, também os mantêm protegidos das agressões físicas, térmicas e mecânicas cotidianas, como por exemplo: escovação, prancha, secador, penteados presos. A nutrição de Kérastase Nutritive Bain Satin 1 recupera a maciez do cabelo desde o banhar, para que ele tenha um toque sedoso, agradável e um movimento bonito, de balanço natural. Seus fios ficam extraordinários: nutridos, macios, protegidos de danos e do ressecamento. Ação: Tecnologia Irisome: combina Gluco-Active e Rizoma de Íris para nutrir de forma profunda e duradoura o cabelo enquanto protege a fibra capilar do ressecamento."},

                new Produto { ProdutoId = 380, Categoria = "Perfumaria", Nome = "MacBeauty Hidratante de Cutículas Stick", Preco = 10.99m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/d04e7c2ed65dabe1dca4eed9aa268e95_ra,w158,h184_pa,w158,h184.png", Descricao = "Hidratante de cutícula em caneta. Granado SOS Cutículas Perfeitas é prático e fácil de usar, promove a renovação e hidratação imediata das cutículas, além de dar brilho e maciez desde a primeira aplicação. Enriquecido com Óleo de Girassol e Vitamina E, Granado SOS Cutículas Perfeitas tem alto poder nutritivo e hidratante, oferecendo tratamento imediato às unhas e cutículas. Possui textura leve e não altera o brilho ou coloração das unhas esmaltadas. Como usar: Aplique sobre as unhas e cutículas. Massageie até a completa absorção"},

                new Produto { ProdutoId = 379, Categoria = "Perfumaria", Nome = "MacBeauty Dream Matte Mousse Colônia", Preco = 14.79m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/029889b345c76a70e8bb978b73ed1a87_ra,w158,h184_pa,w158,h184.png", Descricao = "MacBeauty La Nuit Trésor Nude é um perfume Lancôme feminino floral. Uma fragrância intensa, sofisticada e envolvente, como a sensação de uma paixão avassaladora. Sublime e luminosa, apaixona a todos que sentem sua essência. O Perfume Feminino La Nuit Trésor Nude, de Lancôme, é inspirado nas mulheres que não se intimidam em revelar seus desejos. Um aroma sedutor que combina notas de bergamota, coco, baunilha e rosa. Entregue-se para esta fusão deslumbrante! "},

                new Produto { ProdutoId = 366, Categoria = "Perfumaria", Nome = "MacBeauty Mineral Power Natural ", Preco = 14.99m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/c77ad2da76259cfd67a9a9432f635bfb_ra,w158,h184_pa,w158,h184.png", Descricao = "Floral é um perfume feminino Jimmy Choo floral. Fragrância suave que representa a beleza, confiança e energia de cada mulher. Perfume Jimmy Choo Floral é ultra feminino e delicado. Um aroma refinado, fresco e radiante que traz notas de frutas frescas, folhas, flores como magnólia e notas leves de madeira almiscarada. O frasco verde claro é inspirado no vidro veneziano de Murano"},

                new Produto { ProdutoId = 354, Categoria = "Perfumaria", Nome = "MacBeauty Dream Velvet Foundation", Preco = 18.49m, ImagemUrl = "https://d3t32hsnjxo7q6.cloudfront.net/i/24517c6c81c92eda31cd32b6327c1298_ra,w158,h184_pa,w158,h184.png", Descricao = "Este MacBeauty Polo Red Rush é um perfume Ralph Lauren masculino aromático frutal. A tradução olfativa da adrenalina que corre nas veias do homem moderno. Uma fragrância fresca e enérgica que irradia disposição ao dia a dia. O perfume Ralph Lauren Polo Red Rush traz acordes de tangerina vermelha, toranja e menta para deixar uma atmosfera cítrica e elétrica no ar. Logo se envolve madeira de cedro para concretizar sua personalidade masculina agradável."},
            };
        }

        public Produto GetProduto(int id)
        {
            return produtos.FirstOrDefault(x => x.ProdutoId == id);
        }

        public IEnumerable<Produto> GetProdutos(string filter = null)
        {
            if (string.IsNullOrWhiteSpace(filter)) return produtos;

            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


Referências:


José Carlos Macoratti