fbpx
Modelagem – Conectando os pontos
Publicado em: quinta-feira, 10 de jan de 2019
Categorias: Arquitetura

Já faz muito tempo que gosto de modelar os elementos de uma arquitetura como componentes reaproveitáveis, em geral abstrações que me proporcionem a qualquer consumidor simplesmente usá-la, e para isso há algum esforço na modelagem para abstrair aspectos que são peculiares às suas demandas de negócio.

Em conjunto com um bom Container de IoC/DI, é possível fazer coisas incríveis. E muito do que reclamo da abordagem com Register/Resolver se dá pela impossibilidade de modelar direito e de realmente promover reaproveitamento, quando sua única informação para obter uma dependência é seu tipo. Para a configuração do container, é preciso dar alguma pista de que você quer uma instância configurada do jeito A ou do jeito B, para que possa existir 2 jeitos diferentes de configurar uma dependência. É a inversão de controle quem determinará onde usar cada uma das versões da mesma classe. Reside aí o real poder de reaproveitamento e a simplicidade de uso quando falamos de Inversão de Controle e Injeção de Dependência.

Exemplos:

Indo direto ao ponto, e tentando ser o menos abstrato possível na explicação vejamos 2 exemplos, Email e Parser. Nestes exemplos vou dar alguns detalhes para tornar palpável a compreensão.

Email

Se eu preciso enviar emails, meu primeiro instinto é modelar um serviço com um único método e suas sobrecargas para enviar um email para um destinatário. Quem (a classe) precisa enviar um email (geralmente um requisitante de negócio) sequer precisa saber que há um servidor SMTP, que há configurações dessa natureza, é da infraestrutura de envio de email a responsabilidade por saber isso. Assim eu reduzo complexidade e facilito o reaproveitamento.

public interface IEmailService {
    void SendMessage(string subject, MailAddress[] To, MailAddress[] Cc, MailAddress[] Bcc, string message);
}

Já a implementação teria todas as informações, como SMTP Server, Configurações de Autenticação, enfim, tudo o que for necessário para conectar nos principais serviços do mercado. Sim, eu bedufo.

Parser

Não é incomum que o envio de email se desdobre na necessidade de criar templates de email, então para isso você precisa realizar o parse de uma estrutura de dados, usando uma template engine e posteriormente resultando em um HTML, o que é mais provável. Assim transpor um objeto em um template produzindo o body da mensagem a ser enviada. Bom, quem faz o parser é um outro elemento, destinado a parser, também configurável e com uma interface também irritantemente simples. Seria algo como:

public interface IParser {
    string Parse(string templateName, objet rootMessage);
}

Para que um templateName? Estou cogitando haver um repositório de templates, ok?!

Ambos são simples, um não depende do outro, mas se fizer sentido juntar ambos, aí nasce um terceiro elemento que orquestra as chamadas a ambos. Poderia ser uma composição ou mesmo uma herança sobre a infraestrutura de email. Sendo uma herança, cada classe da pilha de hereditariedade precisaria ter seu sentido bem definido. Trocando em miúdos, ambas as classes deveriam fazer sentido sozinhas, dependendo do contexto e necessidade.

Infraestrutura Global vs Infraestrutura Local

Uma das práticas que me ajudou muito a modelar melhor é separar o que é infraestrutura do que é domínio. Essa decisão te dá o poder de modelar de forma agnóstica. O envio de email só envia email, não faz update em tabela alguma, não chama serviço de notificação, no máximo possui um evento que precisa ser assinado. Quem quer que tenha de fazer tais tarefas adicionais, trata o evento e fará seu trabalho.

Ainda assim, eu gosto de tratar 2 tipos de Infraestrutura, nos meus projetos eu chamo de projetos de arquitetura (****.Architecture).

Infraestrutura global

Infraestrutura global é aquele arcabouço que você sabe que poderá usar em todos ou quase todos os projetos. No meu caso eu trago comigo toda a parte de AOP para dar suporte a transações, exception handling, contextos de acesso a dados (uma forma excelente de resolver leaks e interferências na modelagem). Tudo que sei que precisarei em 100% dos projetos (ou quase isso 😉 ). Esses são elementos menos mutáveis, já que diversos projetos dependem deles, e possuem um nível de maturidade alto.

Infraestrutura local

Também sobre o sufixo Architecture, uso em geral um projeto class library dentro da solution do sistema em questão para criar as abstrações que são específicas para esse projeto. E um ponto é extremamente interessante. Esse é o playground que me permite criar novas soluções que no futuro podem ser promovidas a implementações globais. É assim que mesmo com um modelo parcialmente rígido, o time não perde flexibilidade para inovar e criar abstrações na arquitetura. Aliás...

Lugar de Complexidade é na Arquitetura

Sua arquitetura deve abstrair a complexidade tecnológica afim de prover uma infraestrutura que suporte sua aplicação de forma simples. Se seu código de domínio está complexo, então você não fez o dever de casa na arquitetura. Levando em conta que estamos falando de uma aplicação de linha de negócio, LOB Apps, seu domínio não pode realizar tarefas tecnicamente complexas de forma direta. Seu domínio precisa ser o mais simples possível e se não está simples, você pecou em não criar boas abstrações para essa complexidade.

Domínios orquestram e delegam, e assim, a aparência de um método de domínio que siga essa linha de raciocínio, é invariavelmente simples, não obstante, com uma boa arquitetura, ele pode parecer ao olhar desatento, até omisso (por não esboçar tratamento de exceção, e outros desvios de fluxo desnecessários ali pois alguém o faz de forma global e consistente).

Modelagem

Gosto de encarar cada elemento como autônomo e agnóstico. E aqui ainda estou falando da infraestrutura/arquitetura. Gosto de olhar para uma implementação na infraestrutura e ver que ela não diz respeito ao negócio, mas a uma tarefa técnica, pertinente ao negócio, mas completamente desconectada enquanto solução. Como disse ela é autônoma.

Já no domínio acabo por não usar tantas abstrações e em geral modelo um domínio muito mais flat. Mais flat, significa mais simplista, minimalista ao máximo. São poucas heranças pouco polimorfismo, enfim, simples. Não é uma regra, não crio limites, apenas não tenho feito nada diferente (há exceções, mas não convém citar para não confundir ainda mais). Essa é uma escolha controversa e polêmica, e ainda não tive resultados negativos com os mais variados cenários de negócio de forma a esbarrar em necessidades que enderecem outro perfil de modelagem, como DDD, por exemplo.

A propósito, a infraestrutura de automação com AOP funciona perfeitamente com domínios ricos.

Acesso a Dados

Eu sou um fã de full featured ORM's, como NHibernate, por exemplo. Trago NHibernate comigo há pelo menos 12 anos, como meu ORM default. A primeira recomendação do NHibernate fiz em 2005 em um estudo direcionado à escolha do ORM que iria compor, mais tarde, o framework corporativo .NET  da Petrobras, o FCORP. (NH não foi aceito na época, resolveram implementar um, anos mais tarde jogaram fora e adotaram o NHibernate, enfim isso é assunto para mesa de bar).

De qualquer forma, quando modelo minhas abstrações, o acesso ao NHiberrnate é abstraído, mas não por completo. Isso significa que tenho facilitadores que se você quiser, consegue trabalhar sem saber que existe NHibernate na arquitetura, no entanto, se você estiver sob um repositório/dataprocess, então você consegue sem esforço algum ter acesso ao contexto, à session e objetos internos de gestão do NHibernate. A propósito, independente de CQRS, mesmo com ORM em operações procedimentais, eu separo escrita de leitura. Há somente 1 persistidor para todas as entidades que operam em um determinado banco. Eu também suporto múltiplos bancos/tecnologias side-by-side. É relativamente comum achar cenários em que você precisa de 2 tecnologias diferentes, como MySQL e SQL Server, já houve cenários com até 4 bancos. 

Em relação à separação da escrita e leitura a questão é muito simples. Eu não preciso criar formas diferentes de escrita, mas com certeza preciso lidar com regras específicas de consulta, e em geral há regras de acesso a dados que não quero proliferar, por isso preciso especializar meus repositórios de consulta, diferente do repositório de escrita, este eu simplesmente consumo.

Outro ponto interessante é que para esses repositórios, a arquitetura global provê essa funcionalidade, mas extensões são criadas na arquitetura local, via herança, para dar flexibilidade e tomar a decisão, no projeto, do que quero expor ou não de forma pública, ou com nomes em inglês, português, nomes de métodos que possam fazer mais sentido de acordo com a cultura da empresa ou do projeto em que estou refatorando. Na prática, algumas decisões são irrelevantes, ou precisam ser irrelevantes para a arquitetura global e sequer gosto de entrar nessas brigas. Sigo aplicando Open-Closed principle não só para não engessar o projeto, mas para dar flexibilidade e evitar algumas discussões que ao meu ver são pouco produtivas.

Conectando os pontos

Por fim, como unir isso tudo: IoC + DI. Uso Spring .NET já há mais de 10 anos, e recentemente criei um fork do projeto para portá-lo para .NET Standard. E sinceramente, sinto muita falta de soluções que façam o que ele faz. A flexibilidade de adicionar comportamentos, proxies AOP e outros recursos o fazem, na minha opinião, o melhor IoC/DI container do mercado. E tenho muito a falar sobre o projeto, são muitos recursos úteis que facilitam o dia-a-dia. Com algumas automações que criei, fica ainda mais fácil torná-lo compatível com ASP.NET Core. É incrível ver o que conseguimos fazer com ele.

Conclusão 

Todos temos predileções quanto a modelagem e arquitetura, essas são as dicas que levo comigo e por enquanto trago para o meu dia-a-dia de projeto. Gosto desse tipo de abordagem e gosto dessa forma de modelar. Embora possa parecer abstrata demais ao olhar de quem usa esse tipo de arquitetura, ela oferece imensa flexibilidade, extensibilidade e permite criar soluções incríveis e altamente reaproveitáveis.

O Cloud Native .NET é meu principal projeto.

Onde empenho energia para ajudar, acompanhar, direcionar Desenvolvedores, Líderes Técnicos e jovens Arquitetos na jornada Cloud Native.

Conduzo entregando a maior e mais completa stack de tecnologias do mercado.

Ao trabalhar com desenvolvedores experientes, eu consigo usar seu aprendizado com .NET, banco de dados, e arquitetura para encurtar a jornada.

Ao restringir à desenvolvedores .NET eu consigo usar do contexto de tecnologias e problemas do seu dia-a-dia, coisas que você conhece hoje, como WCF, WebForms, IIS e MVC, por exemplo, para mostrar a comparação entre o que você conhece e o que está sendo apresentado.

É assim que construímos fundamentos sólidos, digerindo a complexidade com didática, tornando o complexo, simples.

É assim que conseguimos tornar uma jornada densa, em um pacote de ~4 meses.

Eu não acredito que um desenvolvedor possa entender uma tecnologia sem compreender seus fundamentos. Ele no máximo consegue ser produtivo, mas isso não faz desse desenvolvedor um bom tomador de decisões técnicas.

É preciso entender os fundamentos para conseguir tomar boas decisões.

0 comentários

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Luiz Carlos Faria

Mensagem do Autor

Espero que goste desse post. Não deixe de comentar e falar o que achou.

Se acha que esse post pode ajudar alguém que você conheça, compartilhe!

 

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.