Microservices é uma das sensações do momento, muito se fala sobre este estilo arquitetural e para muitos é a solução de todos os problemas. Entenda sobre os desafios de implementar uma arquitetura de microservices e o que deve ser feito antes para evitar problemas futuros.
Microservices é (mais) um novo termo que está sendo bastante utilizado como definição de arquitetura ideal para aplicações corporativas, o termo inclusive é mais novo do que a própria implementação de fato, sendo que o principal objetivo é desenvolver serviços distribuídos e independentes que compõem uma ou mais aplicações.
“O conteúdo técnico está no vídeo ao final do post, o texto a seguir é apenas uma introdução ao assunto.”
Microservices em poucas palavras
Microservices (ou arquitetura de microsserviços) é um estilo arquitetural que propõe uma abordagem de desenvolver uma aplicação através da construção de pequenos serviços, cada um com sua própria responsabilidade (capacidade de negócios) e comunicando-se através de mecanismos “leves”. Geralmente assumem o formato de API’s conversando através de HTTP.
Por serem independentes e pequenos (micro) eles funcionam através de mecanismos de deploy independentes e totalmente automatizados onde há o mínimo de gerenciamento centralizado sobre como são escritos. Sendo assim podem ser escritos em diferentes linguagens e tecnologias e utilizar diferentes tipos de persistência de dados (BD).
Muitas empresas já utilizam com sucesso este estilo arquitetural, por que eu devo evitar começar a desenhar minha arquitetura neste estilo?
Conforme a tecnologia avança ela se torna cada vez mais complexa e requer mais conhecimento técnico de quem vai implementar. No cenário de grande escassez técnica (principalmente no Brasil) percebo que muitos desenvolvedores não estão aptos tecnicamente para entregar uma solução baseada na arquitetura de microservices.
Isso sempre foi um problema, porém a arquitetura de microservices exige muito mais conhecimento do que projetar uma aplicação monolítica tradicional. Sendo assim é altamente recomendável que o arquiteto da futura aplicação em microservices esteja ciente de todos os pré-requisitos e habilidades antes de começar o projeto.
Atuando como consultor eu já atendi dezenas de grandes empresas que falharam na implementação de uma arquitetura de microservices e pior deixaram de entregar a aplicação no prazo ou entregaram com falhas muito graves que comprometeram a disponibilidade e confiabilidade da empresa / serviço.
Como implementar uma arquitetura de microservices de forma responsável e dentro dos pré-requisitos técnicos esperados?
Não existe uma receita para isto. Tudo depende dos skills técnicos da equipe e das plataformas / tecnologias utilizadas na empresa. O que podemos fazer é observar e até mesmo gerar uma lista com pontos cruciais que realmente irão fazer toda a diferença.
Esta lista dos 10 motivos não foi obtida de algum livro / artigo / material. Eu a criei baseada em minhas “cicatrizes” de anos de consultoria, portanto trata-se de minha visão técnica e de consultor, portanto recomendo que assista meu vídeo para compreender melhor sobre estes pré-requisitos/motivos e o por que eles são tão fundamentais.
Espero que vocês aproveitem as dicas e qualquer dúvida estou a disposição.
Vamos continuar a troca de experiências, deixe seu comentário abaixo. Se gostou e concorda com o artigo, compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.
Migrando para o ASP.NET Core? Não é necessário migrar todo o projeto de uma única vez, utilize migrações modulares e compartilhe o Cookie de autorização de usuários.
O ASP.NET Core está pronto e muitas demandas de migração de projetos ASP.NET 4.X para a nova plataforma irão surgir. Como planejar e executar esta migração?
Uma migração total pode ser complexa, afinal vai exigir a alocação de diversas pessoas e enquanto isto as duas aplicações precisarão receber futuras implementações. Alguns projetos são tão grandes que simplesmente não seriam migrados se esta fosse a única alternativa, seria impossível atender as duas demandas simultaneamente.
Por que não ter duas aplicações?
Não há motivos para uma migração total se sua aplicação já funciona bem. Portar parcialmente módulos da aplicação para um projeto ASP.NET Core é uma alternativa bem mais interessante e que vai exigir muito menos esforço.
Imagine que em sua aplicação o módulo de gestão de Produtos funciona no ASP.NET MVC 5 e o módulo de gestão de Clientes no ASP.NET Core, o usuário final mal poderá perceber a diferença!
Integração de dados entre duas aplicações.
Certo! Posso migrar parcialmente, mas como faço a integração entre os dados que estão em aplicações diferentes?
Uma aplicação que utiliza uma boa arquitetura e uma boa modelagem estratégica pode facilmente integrar diversas entidades através dos ORM’s. Se a aplicação possuir uma modelagem DDD cada Bounded Context resolve seus próprios problemas, migre um BC de cada vez! (está mais fácil do que você imagina).
Compartilhando o usuário entre duas aplicações
Este é o grande foco da abordagem que apresento, como realizar uma integração para uma aplicação ASP.NET Core compartilhar o mesmo usuário de uma aplicação ASP.NET MVC 5.
Disponibilizei um novo repositório no meu GitHub com o projeto AspNetInterop este projeto implementa exatamente este cenário onde duas aplicações ASP.NET Core 1.1 e ASP.NET MVC 5 compartilham o mesmo Usuário, Cookies, Claims e Token para o AntiForgery.
Para reproduzir este projeto você precisa utilizar alguns Nuget Packages
Esta abordagem faz a utilização do ASP.NET Identity, mas é possível produzir o mesmo resultado utilizando somente as classes do Microsoft.Owin.Security.
O importante é setar o mesmo cookie entre as duas aplicações:
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Cookies = new Microsoft.AspNetCore.Identity.IdentityCookieOptions
{
ApplicationCookie = new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookie",
LoginPath = new PathString("/Account/Login/"),
AccessDeniedPath = new PathString("/Account/Forbidden/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
CookieName = ".AspNet.SharedCookie"
// If you have subdomains use this config:
CookieDomain = "localhost"
};
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
É necessário que as aplicações rodem no mesmo nome de domínio, se você tiver sub-dominios para cada aplicação basta setar a raíz do domínio em CookieDomain.
Selecione um repositório para Data Protection
O exemplo está utilizando um diretório como repositório, mas o ASP.NET Core 1.1 já suporta Redis, Azure Blob Storage e Azure Key Vault.
No ASP.NET MVC 5 além de configurar o Identity para utilizar o mesmo cookie (verifique implementação no código fonte) é necessário configurar o AntiForgery para que o usuário consiga utilizar a mesma chave entre as aplicações, para isto é necessário esta configuração no Global.asax
O AntiForgery faz a validação baseada em claims e no cookie do usuário logado.
Migre o módulo de gestão de usuários primeiro
Para utilizar este recurso é importante que esteja utilizando o ASP.NET Core com o ASP.NET Identity 3.0, portanto eu sugiro que este seja o primeiro módulo a ser migrado. A implementação deste projeto compartilha a mesma base do ASP.NET Identity entre as aplicações.
Esta implementação foi baseada no repositório idunno.CookieSharing do qual o Scott Hanselman faz parte dos integrantes. O projeto que disponibilizo no repositório AspNetInterop é uma melhoria da implementação original, no meu projeto as melhorias implementadas foram:
Utilização do ASP.NET Identity ao invés do Cookie Middleware
Implementação de controle de acesso com base em claims para o ASP.NET Core e ASP.NET MVC 5 (cada um possui uma implementação diferente devido a forma de autorizar usuários no ASP.NET Core ter mudado)
Compartilhamento do Cookie no AntiForgery
CRUD’s de entidades em cada aplicação
Navegação integrada para o usuário não notar a mudança
Este projeto não conta com divisão em camadas para implementar separação de responsabilidades de Dados, Negócios e etc, porém é muito simples de implementar, pois isto não gera impacto na implementação existente.
Caso esteja interessado em conhecer mais sobre ASP.NET, DDD, Padrões de Arquitetura como CQRS, Event Sourcing e boas práticas de desenvolvimento não deixe de conferir as ementas dos meus cursos:
Vamos continuar a troca de experiências, deixe seu comentário abaixo. Se gostou e concorda com o artigo, compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.
O Equinox Project é um projeto open-source desenvolvido em ASP.NET Core que implementa uma série de tecnologias e abordagens muito utilizadas em grandes projetos.
O Equinox Project é a mais recente contribuição que eu entrego a comunidade técnica e espero que seja de grande ajuda para servir de referência nos futuros projetos escritos em ASP.NET Core.
O Equinox Project (versão 1.0) é o resultado de quase duas semanas de estudos e desenvolvimento que dediquei ao criar uma aplicação funcional implementando diversas tecnologias e abordagens na nova plataforma ASP.NET Core.
Por ser totalmente desenvolvido com .NET Core esta aplicação pode rodar em ambientes Windows, Linux e OSX (Mac).
Tecnologias/Recursos Implementados
ASP.NET Core 1.1 (com .NET Core)
ASP.NET MVC Core
ASP.NET Identity Core
Isolado do MVC e Autenticando via
Facebook ou Cadastro
Entity Framework Core
AutoMapper
.NET Core Native DI (Isolado do MVC)
Unit of Work
Repository e Generic Repository
FluentValidator
Arquitetura
Arquitetura completa com separação de responsabilidades, SOLID e Clean Code
O Equinox Project na versão 1.0 implementa um cadastro de clientes (CRUD) com regras de negócio e validações de dados.
Toda escrita de dados ocorre através de Commands e CommandHandlers que são processados por um Bus em memória (podendo ser adaptado para um Message Queue por exemplo).
Após a execução de um Command é disparado um Evento que realiza alguma ação informativa e também é persistido na base (Event Sourcing).
A leitura de dados ocorre de forma mais simples dispensando algumas camadas de negócio.
Todas as ações são autorizadas pelo mecanismo do ASP.NET Identity que baseia-se em Claims para permitir a leitura e escrita dos dados.
As validações de consistência dos dados são realizadas nos Commands e utilizam o FluentValidator como mecanismo.
Todos os erros no processamento ou na validação dos dados são disparados através de Domain Events/Notifications e são informados ao usuário de forma personalizada.
É possível visualizar a história da entidade através da aplicação que informa desde a criação até a exclusão as mudanças dos dados que ocorreram e o usuário que as executou.
Estes são alguns dos recursos implementados, existem diversos outros em toda extensão da aplicação. Cada um destes recursos eu irei tratar em artigos individuais em uma nova série sobre ASP.NET Core que irei iniciar muito em breve.
Aviso
Este projeto não pretende ser uma solução definitiva para todos os cenários.
Algumas versões utilizadas (inclusive do ASP.NET Core 1.1) estão em Beta ou Pre-Release.
Cuidado ao utilizar este projeto na sua produção. Analise bem os riscos de ser um Early Adopter.
Talvez você não irá precisar de muitos dos recursos implementados, procure evitar o OverEngineering
Sobre o futuro
A versão 2.0 do Equinox Project será uma aplicação bem mais extensa com os recursos a seguir:
Aplicação completa de aluguel (Booking) utilizando Domain Model Pattern, CQRS e Event Sourcing.
ASP.NET Identity trabalhando através de serviços WebAPI com Bearer Token
Novo front-end
Bancos separados para leitura e gravação de dados
Testes de Unidade
Acompanhe os detalhes que serão atualizados no RoadMap do projeto.
Sugestões?
Tem uma boa ideia sobre implementação ou gostaria de ver algo implementado?
Sugestões e críticas serão muito bem vindas!
Por que Equinox?
O Equinócio (Equinox) é um evento astronômico em que o plano do equador da Terra passa pelo centro do Sol. Este evento ocorre duas vezes por ano em torno de 20 de Março e 23 de Setembro. Wikipedia
Equinox é também uma série de publicações (subtítulo: “The Review of Scientific Illuminism”) em forma de livro que serve como o órgão oficial da A∴A∴, uma ordem iniciática fundada por Aleister Crowley Wikipedia
Estamos Online
O projeto está publicado *orgulhosamente* no Microsoft Azure, experimente!
Caso esteja interessado em conhecer mais sobre ASP.NET, DDD, Padrões de Arquitetura como CQRS, Event Sourcing e boas práticas de desenvolvimento não deixe de conferir as ementas dos meus cursos:
Vamos continuar a troca de experiências, deixe seu comentário abaixo. Se gostou e concorda com o artigo, compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.
O DDD tornou-se um tema altamente debatido e naturalmente uma série de equívocos surgiram através dos diferentes entendimentos sobre o assunto.
Vou citar algumas perguntas / afirmações que venho observando faz alguns anos e são a minha principal motivação para escrever este post.
Como faço para persistir uma entidade com Entity Framework no DDD?
Como faço para popular um DropDownList seguindo o padrão DDD?
Iniciei um novo projeto em MVC + DDD na minha empresa e tenho uma dúvida.
Estou criando um back-end em WebAPI + DDD
Onde coloco uma camada de cache num projeto DDD?
Existe algum framework de DDD em .NET?
DDD não é arquitetura em camadas.
O DDD é uma abordagem de modelagem de software que segue um conjunto de práticas com objetivo de facilitar a implementação de complexas regras / processos de negócios que tratamos como domínio.
Domain Driven Design como o nome já diz é sobre design. Design guiado pelo domínio, ou seja, uma modelagem de software focada em resolver problemas na complexidade do negócio.
“Toda arquitetura é design, mas nem todo design é arquitetura” – Grady Booch
O DDD não é uma receita pronta sobre como desenvolver uma arquitetura baseada em Presentation, Services, Application, Domain e Infra.
DDD não é tecnologia.
O DDD não depende da tecnologia que você irá utilizar para fornecer sua aplicação seja ela ASP.NET MVC, WebAPI, SPA, Windows Forms ou etc.
O DDD não irá influenciar em diversas decisões:
Como preencher um controle na camada de apresentação
Como expor uma API REST
Qual tecnologia usar para persistir os dados
Como realizar o deploy da aplicação
Como modelar seu banco de dados
Como trabalhar com camadas de Infra (ex. Cache, Log, IoC)
Qualquer outra decisão de tecnologia.
Evite as gafes
Evite utilizar o termo DDD acompanhado de questões que não envolvem diretamente os conceitos do DDD, pois pode dar a entender que você não compreendeu sobre do que se trata o assunto.
Então do que se trata o DDD?
O DDD resgatou e catalogou uma série de boas práticas que foram ignoradas durante anos na maioria dos projetos e na minha opinião esta é a maior conquista do autor na concepção da sua ideia.
Eric Evans em seu livro aborda desde o primeiro capítulo a preocupação no entendimento do negócio, pois entender e destilar o negócio é o único meio de implementar o DDD em um projeto.
Não existe um modelo passo-a-passo de como implementar o DDD, mas podemos tentar criar um resumo básico:
Passo #1 – Entender o Negócio
Sem entender o negócio não tem como implementar o DDD. Em um projeto existem basicamente dois tipos de papéis, o Time de Desenvolvimento e os Domain Experts.
Os Domain Experts entendem do negócio e vão guiar o time de desenvolvimento no projeto tirando dúvidas, definindo regras e processos e nomeando os termos a serem utilizados.
Passo #2 – Extrair a Linguagem Ubíqua
A Linguagem Ubíqua é uma linguagem compartilhada e desenvolvida pela equipe de Domain Experts e de Desenvolvimento. A Linguagem Ubíqua é a linguagem do negócio dentro da empresa e todos devem fazer uso dela para expressar corretamente todos processos / intenções de negócio.
Existem diversas técnicas para extrair e catalogar a Linguagem Ubíqua, cabe ao time definir a melhor maneira de colaborar.
Passo #3 – Modelagem Estratégica
Extrair a Linguagem Ubíqua vai colaborar na visão e entendimento do negócio e como segregar seu domínio em partes menores e responsáveis.
Para documentar estas segregações responsáveis utilizamos o Mapa de Contextos (Context Map) que pode ser representado através de imagens e uma simples documentação do tipo de relacionamento entre os contextos.
Além de delimitar os contextos a modelagem estratégica engloba outros conceitos como Sub-Domain, Shared Kernel, Customer/Supplier, Conformist, Anti-Corruption Layer, Separate Ways, Open Host Service e Published Language.
Cada contexto delimitado possui sua própria Linguagem Ubíqua, para entender melhor estes conceitos eu escrevi um artigo falando sobre os Bounded Contexts.
Nota: Eu acredito que não existe uma forma de implementar o DDD sem aplicar os conceitos da modelagem estratégica. Inclusive o próprio Eric Evans comentou que se ele fosse escrever seu livro nos dias de hoje ele teria começado pelo conceito de Bounded Contexts.
Passo #4 – Definir a Arquitetura
Tendo uma clara visão do Context Map é possível trabalhar na definição da arquitetura.
Cada contexto pode possuir uma arquitetura independente dos demais, não é necessário impor o mesmo estilo arquitetural para todos os contextos.
O DDD não prega a necessidade de uma arquitetura de 4 camadas (Presentation, Application, Domain e Infra). Pelo contrário, o arquiteto tem a liberdade de definir o melhor estilo arquitetural para atender a necessidade da aplicação, podendo utilizar modelos simples de 3 camadas como o Table Module Pattern.
Um arquiteto deve conhecer os estilos e patterns arquiteturais e saber reconhecer onde e quando devem ser utilizados.
Passo #5 – Modelagem Tática
Quando o assunto é DDD a modelagem tática fica por conta do Domain Model Pattern que é uma abordagem de como escrever as classes que vão mapear os modelos do mundo real e implementar os comportamentos do negócio.
O Domain Model Pattern deve ser isolado dos detalhes da sua arquitetura como persistência e etc.
O Eric Evans não criou os patterns utilizados no Domain Model, apenas fez o uso correto deles criando então esta abordagem de modelagem tática que incluem os seguintes componentes:
Aggregate Object Uma entidade que é a raiz agregadora de um processo do domínio que envolve mais de uma entidade.
Domain Model Uma entidade do domínio, possui estados e comportamentos, lógica de negócio, getters e setters AdHoc, etc.
Value Object Um objeto que agrega valor às entidades, não possui identidade e é imutável.
Factory Classe responsável por construir adequadamente um objeto / entidade.
Domain Service Serviço do domínio que atende partes do negócio que não se encaixam em entidades específicas, trabalha com diversas entidades, realiza persistência através de repositórios e etc.
Application Service Serviço de aplicação que orquestra ações disparadas pela camada de apresentação e fornece DTOs para comunicação entre as demais camadas e para o consumo da camada de apresentação.
Repository Uma classe que realiza a persistência das entidades se comunicando diretamente com o meio de acesso aos dados, é utilizado apenas um repositório por agregação.
External Service Serviço externo que realiza a consulta/persistência de informações por meios diversos.
Se você trabalhou em todos os passos citados, parabéns! Você provavelmente está implementando o DDD. Fique a vontade para utilizar técnicas como TDD e BDD e sinta-se livre para escolher a plataforma da camada de apresentação ou como distribuir suas API’s etc. Afinal este não é o foco e nem uma exigência do DDD.
Quando devo utilizar o DDD?
Apesar de muitas pessoas afirmarem utilizar DDD apenas por possuir uma arquitetura em camadas isto não significa que realmente estejam usando o DDD, existem algumas interpretações como DDD-Lite que seria uma aplicação utilizando alguns conceitos encontrados no DDD porém ignorando muitos outros.
Uma analogia a este cenário seria um time de desenvolvimento dizer que utiliza Scrum como uma metodologia de desenvolvimento ágil quando somente faz uso de um quadro Kanban e ignora as práticas de sprints, cerimônias etc. Nós chamamos isto de ScrumBut.
Espero que tenha ficado claro até este ponto o que é realmente o DDD e os passos principais para implementá-lo. Para saber se você deve ou não implementar o DDD no seu projeto, disponibilizo abaixo um DDD Score Card extraído do livro Implementing Domain Driven Design.
A ideia é bem simples, a primeira coluna descreve seu projeto, em seguida o número de pontos que devem ser acumulados, a última coluna descreve algumas concepções.
Se no final a somatória dos pontos for igual ou maior que 7 considere seriamente em implementar o DDD em seu projeto.
Se seu Projeto…
Pontos
Pensamentos de Suporte
Se sua aplicação for completamente centrada em dados e se qualificar verdadeiramente para uma solução CRUD pura, em que cada operação é basicamente uma consulta simples de banco de dados para criar, ler, atualizar ou excluir, você não precisa do DDD. Sua equipe só precisa colocar um rosto
bonito em um editor de tabelas de banco de dados. Em outras palavras, se você puder confiar no fato de que os usuários irão inserir os dados diretamente em uma tabela, atualizá-los e, às vezes, excluí-los, você nem mesmo precisará de uma interface do usuário. Isso não é realista, mas é conceitualmente relevante. Se pudesse usar uma ferramenta simples de desenvolvimento de banco de dados para criar uma solução, você não desperdiçaria o tempo e dinheiro de sua empresa no DDD.
0
Isso parece óbvio, mas normalmente não é fácil determinar
simples versus complexo. Não é como se todas as aplicações que não são CRUD puras merecem o tempo e o esforço
do uso do DDD. Assim, talvez possamos sugerir outros indicadores para nos ajudar a traçar uma linha entre o que é complexo e o que não é …
Se seu sistema exigir apenas 30 ou menos operações de
negócio, ele provavelmente é bem simples. Isso significaria
que a aplicação não teria um total de mais de 30 histórias de usuário ou fluxos de caso de uso, com cada um desses fluxos tendo apenas uma lógica mínima de negócio. Se você puder desenvolver rápida e facilmente esse tipo de aplicação e não se importar com a falta de poder e controle em relação à complexidade e alteração, o sistema provavelmente não precisará usar o DDD.
1
Para ser claro, estou falando de 25 a 30 únicos métodos de negócio, não de 25 a 30 interfaces de serviço completas, cada uma com vários métodos. O último pode ser complexo.
Assim, digamos que, em algum lugar no intervalo entre 30 e 40 histórias de usuário ou fluxos de caso de uso, a complexidade poderia ser pior. Seu sistema pode estar entrando no território do DDD.
2
O risco é do comprador: Bem frequentemente a complexidade não é reconhecida rapidamente. Nós, desenvolvedores de software, somos realmente muito bons para subestimar a complexidade e o nível de esforços. Só porque talvez queiramos codificar uma aplicação em N camadas com diversos Patterns não significa que devemos. No longo prazo, essas aplicações poderiam prejudicar mais do que ajudar.
Mesmo que a aplicação não seja complexa agora, a complexidade dela aumentará? Você só pode saber isso ao certo depois que os usuários reais começam a trabalhar com ela, mas há um passo na coluna “Pensamentos de suporte” que pode ajudar a revelar a situação real.
Tenha cuidado aqui. Se houver absolutamente qualquer indício de que a aplicação tem complexidade mesmo moderada — este é um bom momento para ser paranoico —, isso pode ser uma indicação suficiente de que ela na verdade será mais do que moderadamente complexa. Incline-se em direção ao DDD.
3
Aqui vale a pena analisar os cenários de uso mais complexos com especialistas em domínio e ver aonde eles levam.Os especialistas em domínio: #1 Já estão solicitando recursos mais complexos? Se sim, isso provavelmente é uma indicação de que a aplicação já é ou em breve se tornará excessivamente complexa para usar uma abordagem CRUD.#2 Estão entediados com os recursos ao ponto em que dificilmente vale a pena discuti-los? Provavelmente não é complexa.
Os recursos da aplicação serão alterados com frequência ao longo de alguns anos, e você não pode antecipar que as alterações serão simples.
4
O DDD pode ajudá-lo a gerenciar a complexidade da refatoração de seu modelo ao longo do tempo.
Você não entende o Domínio porque ele é novo. Na medida em que você e sua equipe sabem, ninguém fez isso antes. Isso provavelmente significa que ele é complexo ou, pelo menos, merece a devida diligência com análise analítica para determinar o nível de complexidade.
5
Você precisará trabalhar com Domain Experts e testar os modelos para fazer a coisa certa. Você certamente também pontuou em um ou mais dos critérios anteriores, portanto, use o DDD.
Ao finalizar este exercício você terá mais clareza para determinar se o DDD é viável ou não para o seu projeto. Lembre-se de tomar as decisões com foco na simplicidade, entrega e manutenção. Muitas vezes sofremos da vontade incontrolável de implementar todos os conceitos de nossos estudos, porém estamos colocando em risco o dinheiro da empresa e nossa própria carreira.
Erros comuns
Agora que está muito claro o que é o DDD e se ele é viável para seu projeto, gostaria de alertar sobre os erros mais comuns cometidos pela maioria dos desenvolvedores.
#1 – Permitir que o meio de persistência influencie diretamente nas entidades.
Quando utilizamos ORM’s para mapear o banco e nossas entidades muitas vezes somos obrigados a “infectar” nossos modelos com necessidades do ORM utilizado. Evite ao máximo tomar decisões que impactem em suas entidades e que no final servem apenas para atender as necessidades do meio de persistência.
#2 – Não se envolver com os Domain Experts
Ignorar o conhecimento de negócio dos Domain Experts é uma falha grave, busque envolvê-los diretamente no projeto e nas decisões. Documentar alguns processos com BDD é uma ótima maneira de aproximar todos os envolvidos em uma conversa fluente e clara.
Se na sua empresa não existe o “Domain Expert” não tem problema, com certeza existe alguém que conheça bem do negócio, traga esta pessoa para participar ativamente no projeto.
#3 – Ignorar a Linguagem Ubíqua
A Linguagem Ubíqua é a linguagem do negócio, deixar de extraí-la e mapeá-la poderá acarretar em sérios problemas de comunicação que irão refletir diretamente no entendimento dos requisitos e no código fonte. Será um grande problema no futuro.
#4 – Não identificar os limites dos contextos
Pular a parte da modelagem estratégica é um dos maiores erros que se pode cometer ao implementar o DDD. Isto tornará sua aplicação extremamente complexa e monolítica, é o princípio de uma grande bola de lama (Big Ball of Mud).
#5 – Escrever entidades anêmicas
O uso de entidades anêmicas é sinal de uma grande falta de entendimento no comportamento de uma entidade, quebra o próprio conceito da OOP que diz que um objeto deve possuir estados e comportamentos. Uma entidade no mínimo deve saber se auto-validar para garantir sua consistência, logo não existem motivos para escrever entidades anêmicas.
#6 – Assumir que toda lógica é lógica de domínio
Nem toda validação é responsabilidade do domínio. Por exemplo o tratamento de acesso e permissões de usuário, isto é responsabilidade da aplicação. Deixar todas as validações por conta do domínio também é uma falha, num cenário Web a camada de apresentação também pode realizar as validações necessárias para filtrar as requisições no servidor.
#7 – Focar demais na infra-estrutura
Implementar um projeto com DDD significa trabalhar com foco no negócio, iniciar o projeto pela modelagem do banco de dados e preocupando-se com os meios de persistência são erros que podem pode gerar impactos negativos nos seus modelos de domínio. A camada de infra-estrutura serve para suportar responsabilidades que não são do domínio, foque nas implementações de infra-estrutura conforme a necessidade surgir.
Resumindo
Implementar o DDD em seu projeto pode ser uma ótima decisão que proporcionará mais facilidades para atender aos complexos processos de negócio. Tornará a equipe mais colaborativa e focada no que é realmente mais importante.
Não se aventure no escuro! Antes de iniciar um projeto em DDD é necessário ter um bom conhecimento teórico e prático em todos os conceitos abordados neste artigo. Eu recomendo fortemente a leitura de livros, cursos e demais conteúdos que irão lhe preparar para que este desafio não se torne um grande desastre.
E por fim evite de utilizar o DDD como referência para arquitetura em camadas, agora você já sabe que não é disto que se trata o DDD.
Caso esteja interessado em conhecer mais sobre o DDD, Padrões de Arquitetura como CQRS, Event Sourcing e boas práticas de desenvolvimento não deixe de conferir a ementa do meu curso:
Vamos continuar a troca de experiências, deixe seu comentário abaixo. Se gostou e concorda com o artigo, compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.
CQRS é uma daquelas siglas que está cada vez mais presente em nossas leituras, muitas vezes encontramos o CQRS sendo citado em conteúdos sobre DDD ou padrões de arquitetura escaláveis.
CQRS é um conceito muito importante e você precisa conhecer. Eu costumo dizer que todo arquiteto possui uma “caixa de ferramentas” e o CQRS é o tipo de ferramenta que precisa estar presente na sua caixa.
O que é CQRS?
CQRS significa Command Query Responsibility Segregation. Como o nome já diz, é sobre separar a responsabilidade de escrita e leitura de seus dados.
CQRS é um pattern, um padrão arquitetural assim como Event Sourcing, Transaction Script e etc. O CQRS não é um estilo arquitetural como desenvolvimento em camadas, modelo client-server, REST e etc.
Onde posso aplicar o CQRS?
Antes de entrar neste ponto vamos entender os cenários clássicos do dia a dia e depois veremos como o CQRS poderia ser aplicado como solução.
Hoje em dia não desenvolvemos mais aplicações para 10 usuários simultâneos, a maioria das novas aplicações nascem com premissas de escalabilidade, performance e disponibilidade. Como uma aplicação pode funcionar bem com 10 ou 10.000 usuários simultaneamente? É uma tarefa complexa criar um modelo conceitual que atenda essas necessidades.
Imagine um sistema de SAC onde diversos atendentes num call-center consultam e modificam as informações de um cadastro de clientes enquanto outra área operacional da empresa também trabalha com os mesmos dados simultaneamente. Os dados do cliente são modificados constantemente e nenhuma das áreas tem tempo e paciência para esperar os possíveis “locks” da aplicação, o cliente quer ser atendido com agilidade.
No mesmo cenário a aplicação pode possuir picos diários ou sazonais de acessos. Como impedir o tal “gargalo” e como manter a disponibilidade da aplicação em qualquer situação?
– Ah! Vamos escalar nossa aplicação em N servidores. Podemos migrar para a nuvem (cloud-computing ex. Azure) e criar um script de elasticidade (Autoscaling) para escalar conforme a demanda.
O conceito de escalabilidade da aplicação vai resolver alguns problemas de disponibilidade como por exemplo suportar muitos usuários simultaneamente sem comprometer a performance da aplicação.
Mas será que só escalar os servidores de aplicação resolve todos os nossos problemas?
Problema # 1
Deadlocks, timeouts e lentidão, seu banco pode estar em chamas.
Escalar a aplicação não é uma garantia de que a aplicação vai estar sempre disponível. Não podemos esquecer que neste suposto cenário todo processo depende também da disponibilidade do banco de dados.
Escalar o banco de dados pode ser muito mais complexo (e caro) do que escalar servidores de aplicação. E geralmente é devido o consumo do banco de dados que as aplicações apresentam problemas de performance.
Problema # 2
Para se obter um dado muitas vezes é necessário passar por um conjunto complexo de regras de negócio que irá filtrar a informação antes dela ser exibida, além disso existem os ORM’s que mapeiam o banco de dados em objetos de domínio realizando consultas com joins em diferentes tabelas para retornar todo conjunto de dados necessários.
Tudo isto custa um tempo precioso até que o usuário receba a informação esperada.
Problema # 3
Um conjunto limitado de dados é consultado e alterado constantemente por uma grande quantidade de usuários simultaneamente conectados. Um dado exibido na tela já pode ter sido alterado por outro. Numa visão realista é possível afirmar que toda informação exibida já pode estar obsoleta.
Ponto de partida
Ter N servidores consumindo um único banco de dados que serve de leitura e gravação pode ocasionar muitos locks nos dados e com isso ocasionar diversos problemas de performance, assim como todo o processo da regra de negócio que vai obter os dados de exibição cobra um tempo a mais no processamento. No final ainda temos que considerar que o dado exibido já pode estar desatualizado.
É este o ponto de partida do CQRS. Já que uma informação exibida não é necessariamente a informação atual então a obtenção deste dado para a exibição não necessita ter sua performance afetada devido a gravação, possíveis locks ou disponibilidade do banco.
O CQRS prega a divisão de responsabilidade de gravação e escrita de forma conceitual e física. Isto significa que além de ter meios separados para gravar e obter um dado os bancos de dados também são diferentes. As consultas são feitas de forma síncrona em uma base desnormalizada separada e as gravações de forma assíncrona em um banco normalizado.
Este é um fluxo simplificado do CQRS que não leva em consideração as camadas de aplicação, domínio e infra, comandos / eventos e enfileiramento de mensagens.
O CQRS não é um padrão arquitetural de alto-nível, podemos entender como uma forma de componentizar parte de sua aplicação. Podemos entender então que a utilização do CQRS não precisa estar presente em todos os processos de sua aplicação. Numa modelagem baseada em DDD um Bounded Context pode implementar o CQRS enquanto os demais não.
Não existe uma única maneira de implementar o CQRS na sua aplicação, pode ser feito de uma forma simples ou muito complexa, depende da necessidade. Independente de como for implementado o CQRS sempre acarreta numa complexidade extra e por isso é necessário avaliar os cenários em que realmente são necessários trabalhar com este padrão.
Entendendo melhor o CQRS
A ideia básica é segregar as responsabilidades da aplicação em:
Command – Operações que modificam o estado dos dados na aplicação.
Query – Operações que recuperam informações dos dados na aplicação.
Numa arquitetura de N camadas poderíamos pensar em separar as responsabilidades em CommandStack e QueryStack.
QueryStack
A QueryStack é muito mais simples que a CommandStack, afinal a responsabilidade dela é recuperar dados praticamente prontos para exibição. Podemos entender que a QueryStack é uma camada síncrona que recupera os dados de um banco de leitura desnormalizado.
Este banco desnormalizado pode ser um NoSQL como MongoDB, Redis, RavenDB etc.
O conceito de desnormalizado pode ser aplicado com “one table per view” ou seja uma consulta “flat” que retorna todos os dados necessários para ser exibido em uma view (tela) específica.
O uso de consultas “flats” em um banco desnormalizado evita a necessidade de joins, tornando as consultas muito mais rápidas. É preciso aceitar que haverá a duplicidade de dados para poder atender este modelo.
CommandStack
O CommandStack por sua vez é potencialmente assíncrono. É nesta separação que estão as entidades, regras de negócio, processos e etc. Numa abordagem DDD podemos entender que o Domínio pertence a esta parte da aplicação.
O CommandStack segue uma abordagem behavior-centric onde toda intenção de negócio é inicialmente disparada pela UI como um caso de uso. Utilizamos o conceito de Commands para representar uma intenção de negócio. Os Commands são declarados de forma imperativa (ex. FinalizarCompraCommand) e são disparados assincronamente no formato de eventos, são interpretados pelos CommandHandlers e retornam um evento de sucesso ou falha.
Toda vez que um Command é disparado e altera o estado de uma entidade no banco de gravação um processo tem que ser disparado para os agentes que irão atualizar os dados necessários no banco de leitura.
Sincronização
Existem algumas estratégias para manter as bases de leitura e gravação sincronizadas é necessário escolher a que melhor atende ao seu cenário:
Atualização automática – Toda alteração de estado de um dado no banco de gravação dispara um processo síncrono para atualização no banco de leitura.
Atualização eventual – Toda alteração de estado de um dado no banco de gravação dispara um processo assíncrono para atualização no banco de leitura oferecendo uma consistência eventual dos dados.
Atualização controlada – Um processo periódico e agendado é disparado para sincronizar as bases.
Atualização sob demanda – Cada consulta verifica a consistência da base de leitura em comparação com a de gravação e força uma atualização caso esteja desatualizada.
A atualização eventual é uma das estratégias mais utilizadas, pois parte do princípio que todo dado exibido já pode estar desatualizado, portanto não é necessário impor um processo síncrono de atualização.
Enfileiramento
Muitas implementações de CQRS podem exigir um “Bus” para processamento de Commands e Events. Nesse caso teremos uma implementação conforme a seguinte ilustração.
A implementação do CQRS quebra o conceito monolítico clássico de uma implementação de arquitetura em N camadas onde todo o processo de escrita e leitura passa pelas mesma camadas e concorre entre si no processamento de regras de negócio e uso de banco de dados.
Este tipo de abordagem aumenta a disponibilidade e escalabilidade da aplicação e a melhoria na performance surge principalmente pelos aspectos:
Todos comandos são assíncronos e processados em fila, assim diminui-se o tempo de espera.
Os processos que envolvem regras de negócio existem apenas no sentido da inclusão ou alteração do estado das informações.
As consultas na QueryStack são feitas de forma separada e independente e não dependem do processamento da CommandStack.
É possível escalar separadamente os processos da CommandStack e da QueryStack.
Uma outra vantagem de utilizar o CQRS é que toda representação do seu domínio será mais expressiva e reforçará a utilização da linguagem ubíqua nas intenções de negócio.
Toda a implementação do CQRS pattern pode ser feito manualmente, sendo necessário escrever diversos tipos de classes para cada aspecto, porém é possível encontrar alguns frameworks de CQRS que vão facilitar um pouco a implementação e reduzir o tempo de codificação.
Apesar da minha preferência ser sempre codificar tudo por conta própria eu encontrei alguns frameworks bem interessantes que servem inclusive para estudo e melhoria do entendimento no assunto.
#1 Mito – CQRS e Event Sourcing devem ser implementados juntos.
O Event Sourcing é um outro pattern assim como o CQRS. É uma abordagem que nos permite guardar todos os estados assumidos por uma uma entidade desde sua criação. O Event Sourcing tem uma forte ligação com o CQRS e é facilmente implementado uma vez que temos também o CQRS, porém é possível implementar Event Sourcing independente do CQRS e vice-versa.
Escreverei sobre Event Sourcing em breve num outro artigo.
#2 Mito – CQRS requer consistência eventual
Negativo. Como abordado anteriormente o CQRS pode trabalhar com uma consistência imediata e síncrona.
#3 Mito – CQRS depende de Fila/Bus/Queues
CQRS é dividir as responsabilidades de Queries e Commands, a necessidade de enfileiramento vai surgir dependendo de sua implementação, principalmente se for utilizar a estratégia de consistência eventual.
#4 Mito – CQRS é fácil
Não é fácil. O CQRS também não é uma ciência de foguetes. A implementação vai exigir uma complexidade extra em sua aplicação além de um claro entendimento do domínio e da linguagem ubíqua.
#5 Mito – CQRS é arquitetura
Não é! Conforme foi abordado o CQRS é um pattern arquitetural e pode ser implementado em uma parte específica da sua aplicação para um determinado conjunto de dados apenas.
Caso esteja interessado em conhecer mais sobre o CQRS, Event Sourcing, DDD e boas práticas de desenvolvimento não deixe de conferir a ementa do meu curso:
Vamos continuar a troca de experiências, deixe seu comentário abaixo, se gostou e concorda com o artigo compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.
Bounded Context é um conceito muito importante do DDD e pode ser a solução para a boa modelagem do seu domínio.
Bounded Context é um conceito tão importante quanto o entendimento da separação de responsabilidades das camadas do DDD. Você tem utilizado este conceito em sua aplicação? Cuidado! Você pode estar construindo uma grande bola de lama.
Bounded Context é um conceito razoavelmente fácil de se explicar, porém pode ser extremamente complexo de se implementar. Tudo depende da visão global do domínio da aplicação.
O que significa visão global do domínio?
Ter uma visão global significa enxergar toda a extensão de seu domínio e eu não estou me referindo as camadas, estou me referindo ao negócio.
Como ter uma visão global do negócio se a aplicação ainda não foi desenvolvida ou está em processo de desenvolvimento? É necessário lutar contra a IVSF “Irresistível Vontade de Sair Fazendo” e focar antes em mapear seus contextos e definir os Bounded Contexts “contextos delimitados”. Para determinar seus Bounded Contexts você precisa ter um bom conhecimento do negócio da empresa ou ter ao seu lado um Domain Expert.
Domain Expert
É a pessoa que entende do negócio da empresa e vai apoiar os times de desenvolvimento na modelagem do domínio, definição das regras de negócio e etc. O domain expert também é responsável por definir a Ubiquitous Language “Linguagem Ubíqua”.
Context Map
Para a aplicação ter um bom design, uma fácil manutenção / extensibilidade e o domínio ser bem modelado é necessário focar em modelagem estratégica e para isso é importante preocupar-se com a integridade do modelo conforme o diagrama do Context Map apresenta.
Todos os conceitos do Context Map são importantes, é necessário compreender muito bem de cada um deles para termos condição de realizar uma boa modelagem.
Big Ball of Mud
A grande bola de lama. Você pode ter uma em suas mãos neste momento.
Este conceito aborda vários aspectos negativos de sua aplicação, desde código macarrônico que fere os princípios do SOLID e Clean Code até uma entidade com muitas responsabilidades em um único contexto. Analise a imagem a seguir:
A entidade Produto possui diversos comportamentos, cada um destes comportamentos está ligado a uma intenção da aplicação, todas as intenções são relativas ao produto em si, porém imagine a complexidade desta classe, quantas equipes de desenvolvimento estão compartilhando a mesma classe em comum.
A entidade Produto atende aspectos de Aquisição, Venda, Entrega, Estoque e etc.
Esse tipo de modelagem pode ser considerada um exemplo de Big Ball of Mud, pois qualquer manutenção nessa entidade pode ocasionar impactos sérios em diversos pontos da aplicação, é praticamente impossível de gerenciar as mudanças.
Como resolver ou evitar este tipo de cenário? O DDD não é sobre dividir a aplicação em camadas responsáveis, o DDD é sobre modelar corretamente o domínio do seu negócio. Se sua aplicação possui uma única camada de domínio e esta camada concentra todas as entidades do seu negócio você pode estar cometendo um grande erro de modelagem de domínio. Para aplicações que possuem domínios muito complexos o ideal é aplicar o conceito de Bounded Context.
Bounded Context
Os contextos delimitados ou bounded contexts buscam delimitar o seu domínio complexo em contextos baseados nas intenções do negócio. Isto significa que você deve delimitar as intenções de suas entidades com base no contexto que ela pertence. Analise a imagem a seguir:
O domínio foi subdividido em seis pedaços, ou melhor, em seis bounded contexts, um para cada intenção de negócio (Vendas, Entregas, Estoque etc.). Agora cada bounded context possui uma entidade Produto. Cada versão da entidade Produto é diferente nos seis bounded contexts existentes. A entidade Produto possui comportamentos que atendem necessidades específicas de seu bounded context, a única coisa em comum entre todas as entidades Produto é sua identidade, o ProdutoId no caso. A identidade em comum vai ajudar na persistência e na comunicação entre os bounded contexts.
Mudando um pouco de cenário imagine uma entidade chamada Funcionário, esta entidade representa o colaborador da empresa dentro da aplicação. No bounded context “Recursos Humanos” a entidade Funcionário possui uma modelagem que atende comportamentos como férias, salário, rescisão etc. No bounded context “TI” esta entidade possui uma modelagem que atende comportamentos como login, troca de senha, permissões etc.
Quando se pergunta sobre um funcionário no departamento de TI este está ligado a um usuário e suas responsabilidades dentro do sistema, quando se pergunta sobre um funcionário dentro do RH este está ligado a um colaborador da empresa. É a mesma pessoa, porém dentro da aplicação possui intenções diferentes e é baseada nas intenções que o seu domínio deve ser delimitado em contextos. Não tem necessidade nenhuma a entidade Funcionário do o bounded context de TI ter acesso a salário, reajustes e etc.
Os Bounded Contexts fornecem aos membros das equipes de desenvolvimento um claro entendimento do que deve ser consistido e desenvolvido independentemente.
Representar a mesma entidade em diversos bounded contexts não seria duplicar o código? De forma alguma. Duplicar código é ter a mesma responsabilidade em trechos de código diferentes. Neste caso existe uma segregação de comportamentos e intenções de uma entidade conforme o contexto em que ela está. Não importa se a entidade é persistida na mesma tabela ou em tabelas diferentes, neste caso ambos os cenários são aceitos.
Características importantes de um bounded context
Cada bounded context pode possuir sua própria abordagem de arquitetura. Camadas de aplicação, persistência, infra-estrutura e etc.
A arquitetura de um bounded context não precisa estar necessariamente no padrão DDD (Domain Model) pode ser um modelo mais simples de 3 camadas, pode implementar CQRS, Event Sourcing e etc.
Cada bounded context pode possuir um meio próprio de persistência, sendo ele relacional, NoSQL, em memória / cache e etc.
Os bounded contexts podem se comunicar entre si de diversas maneiras, inclusive utilizando eventos de domínio “Domain Events” conectados em um “Event Bus”.
Cada bounded context possui sua própria Ubiquitous Language.
Cada bounded context pode ser desenvolvido por um time de desenvolvedores diferente. Não existe necessidade de um único time conhecer a implementação de todos os contextos, pelo contrário, por motivos de segurança o fonte de um bounded context pode ser restrito a um time específico.
Patterns de relacionamento entre bounded contexts
Existem diversos patterns que descrevem o tipo de relacionamento entre os bounded contexts:
Shared Kernel Um contexto compartilhado entre outros contextos, o shared kernel é um tipo de contexto onde N bounded contexts dependem dele, uma espécie de Core, este tipo de contexto não pode ser alterado sem consultar todos os times de desenvolvimento que dependem dele.
Customer/Supplier Contextos customer dependem de contextos supplier. A equipe downstream atua como cliente (customer) da equipe upstream (supplier). As equipes definem testes de aceitação automatizados que validam as interfaces que a equipe upstream fornecem. A equipe upstream pode então fazer alterações em seu código sem medo de quebrar alguma coisa da equipe downstream.
Conformist É o cenário onde as equipes downstream e upstream não estão mutuamente alinhadas e a equipe downstream precisa atender o negócio com o que a equipe upstream fornece mesmo não estando de acordo com as necessidades. A equipe downstream precisa aceitar este fato, se conformar com isto.
Partner Neste cenário duas equipes possuem dependências mútuas em seus contextos e precisam somar esforços de modelagem para se atenderem mutuamente.
Anti Corruption Layer Neste cenário a equipe downstream desenvolve uma camada adicional anti-corrupção para se comunicar com o contexto upstream, é o cenário típico onde o supplier é um sistema legado ou uma API mal desenvolvida.
Domain Model Pattern
É um padrão muito indicado para atender um bounded context, o domain model pattern atende diversas convenções do DDD como:
Aggregate Object Uma entidade que é a raiz agregadora de um processo do domínio que envolve mais de uma entidade.
Domain Model Uma entidade do domínio, possui estados e comportamentos, lógica de negócio, getters e setters AdHoc, etc.
Value Object Um objeto que agrega valor às entidades, não possui identidade e é imutável.
Factory Classe responsável por construir adequadamente um objeto / entidade.
Domain Service Serviço do domínio que atende partes do negócio que não se encaixam em entidades específicas, trabalha com diversas entidades, realiza persistência através de repositórios e etc.
Application Service Serviço de aplicação que orquestra ações disparadas pela camada de apresentação e fornece DTOs para comunicação entre as demais camadas e para o consumo da camada de apresentação.
Repository Uma classe que realiza a persistência das entidades se comunicando diretamente com o meio de acesso aos dados, é utilizado apenas um repositório por agregação.
External Service Serviço externo que realiza a consulta/persistência de informações por meios diversos.
E o código como fica? O mais importante é entender os conceitos, uma vez que cada um deles estão claros escrever o código é a parte mais fácil. Num próximo material eu trarei uma implementação destes conceitos.
Resumindo
O conceito de bounded context deve ser aplicado buscando delimitar os comportamentos do domínio com base em suas intenções, dando claro entendimento do que precisa ser desenvolvido de forma independente e compartilhada.
Trabalhar com DDD não significa abordar uma arquitetura dividida em camadas responsáveis sendo uma delas a camada de domínio, significa trabalhar a complexidade do negócio em uma modelagem que atenda as necessidades de forma clara, coesa e respeitando as boas práticas de desenvolvimento.
Vaughn Vernon em seu livro Implementing Domain-Driven Design aborda o conceito de arquitetura hexagonal utilizando DDD, CQRS, Event Sourcing, Domain Events, SOA e etc. Neste livro é possível entender que cada bounded context pode possuir sua própria arquitetura hexagonal ou implementar alguma outra abordagem de arquitetura como já foi citado anteriormente neste artigo.
O DDD desde 2003 vem evoluindo bastante e já não está mais preso nos conceitos iniciais, está mais aberto e flexível, permitindo diversas abordagens de arquitetura entre outras implementações como eventos e mensagerias para integrações.
Se você estiver interessado em conhecer mais e aprender como desenvolver aplicações com uma arquitetura responsável utilizando DDD, TDD, BDD, aplicando os princípios SOLID, diversos design patterns e escrevendo testes de unidade conheça meus cursos:
Vamos continuar a troca de experiências, deixe seu comentário abaixo, se gostou e concorda com o artigo compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.
O ASP.NET Identity é uma ótima solução para a gestão dos usuários em sua aplicação, porém seu design é diretamente acoplado ao ASP.NET MVC e também devemos evitar de utilizar suas referências no domínio da aplicação.
Desacoplando o ASP.NET Identity. Finalmente após muitos pedidos eu consegui colocar a mão na massa e desenvolver um projeto modelo para ser usado como referência.
Como sabemos o ASP.NET Identity depende diretamente do EF (ou outros), Owin e System.Web, logo todas as implementações do ASP.NET Identity ficam de alguma forma acopladas em nossa camada de apresentação. Quando possuímos uma aplicação onde o usuário é uma peça importante no negócio existe a necessidade de representá-lo na camada de domínio, porém é altamente recomendado que a camada de domínio não conheça tecnologias terceiras (Identity, EF, outros).
Como resolver esse problema?
Foi após alguns estudos que cheguei no modelo que considero ser a “melhor abstração possível”, pois para o ASP.NET Identity funcionar na camada de apresentação é obrigatório levar algumas dependências, porém desde que esteja isolado em outra camada já conseguiremos ótimas boas vantagens.
Aviso: Caso você não conheça o ASP.NET Identity, DDD e Injeção de dependência sugiro que comece pelos tutoriais:
Neste tutorial você encontrará as seguintes tecnologias utilizadas
ASP.NET MVC 5
ASP.NET Identity 2
Entity Framework 6
Fluent API
Injeção de Dependência com Simple Injector
Owin
RestSharp para implementação do Twilio API (SMS)
Existem dois grandes objetivos nesta proposta, o primeiro objetivo é a reutilização, pois com um uma única solução (camada) de gestão de usuários é possível atender N aplicações, facilitando o gerenciamento e manutenção.
O segundo objetivo é permitir que a camada de domínio represente o usuário de forma abstraída, logo não existe referência direta do IdentityUser no domínio, porém é possível consultar os dados de usuário e incluir / modificar as informações salvas.
O primeiro passo é abstrair tudo, mover as dependências do Identity para uma camada isolada, depois injetar no MVC os objetos necessários, para essa implementação eu utilizei o Simple Injector, que como o próprio nome diz, é bem simples, rápido e atendeu bem a necessidade.
Com o Identity isolado e injetado devidamente na camada de apresentação (MVC) basta criar uma camada de acesso a dados que irá fornecer meios do domínio consumir dados do usuário de forma abstraída. A grande sacada está no uso correto do Fluent API e claro muita injeção de dependência.
A solução desde modelo não visa atender completamente a filosofia do DDD, pois procurei simplificar o bastante para deixar simples de modificar, por exemplo adicionando uma camada de Application. Portanto quem utilizar esta arquitetura deve estar ciente que algumas coisas a mais ainda precisam ser feitas.
Gostaria de ressaltar também que o template de projeto ASP.NET MVC que o Visual Studio fornece possui vários problemas de design de código. As controllers de Account e Manage do usuário precisaram ser parcialmente re-escritas para atender a injeção do Identity, problemas como mais de um construtor público e utilização do pattern (ou anti pattern?) Service Locator foram encontrados e retirados.
Para quem quiser acompanhar em detalhes técnicos toda a implementação dessa solução eu preparei o vídeo a seguir.
* Assine meu canal no Youtube 🙂
O código fonte desta solução está disponível no GitHub, caso queira propor alguma melhoria faça um Pull Request, será muito bem recebido.
Se você estiver interessado em conhecer mais e aprender como desenvolver aplicações Web com arquitetura baseada em DDD, aplicando os princípios SOLID, diversos Design Patterns e escrevendo testes de unidade inscreva-se em meu curso:
Vamos continuar a troca de experiências, deixe seu comentário abaixo, se gostou e concorda com o artigo compartilhe com seus colegas para transmitirmos o conhecimento para o máximo de pessoas possíveis.
Quem nunca disse ou ouviu isso? Vamos entender os motivos por trás dessa icônica frase.
Vamos precisar reescrever essa aplicação do zero! É a frase de rendição, é sinal que as coisas foram para o caminho errado. Dias, meses, anos de desenvolvimento que serão jogados fora por falta de um bom planejamento.
Quando chegamos nessa situação geralmente estamos diante de alguns dos cenários a seguir:
A tecnologia da aplicação é antiga demais e os novos recursos demandam mais tecnologia.
A arquitetura da aplicação é uma engenhoca complexa e sem sentido.
Devido a diversas implementações e manutenções do passado feitas de forma irresponsável ou sem padrão a aplicação se tornou a famosa “colcha de retalhos”, qualquer tipo de mudança é sofrível e demorada.
A cada mudança surgem diversos bugs em todas as partes da aplicação, pois existe muito acoplamento entre suas divisões, “tudo está acoplado com tudo”.
O time que trabalha na aplicação atualmente não concorda com a forma com que foi escrita e não está satisfeito em trabalhar nela.
Algumas situações muitas vezes não possuem uma escapatória, por exemplo o envelhecimento natural da aplicação. É muito caro manter uma aplicação sempre alinhada com a última versão da tecnologia, logo, algumas vezes prefere-se optar por saltos (atualizar a cada X tempo) ou simplesmente reescreve-la depois de alguns anos.
O cenário de envelhecimento da aplicação é um dos menores, geralmente a aplicação morre muito antes da hora. Eu trabalhei em uma empresa onde assisti a mesma aplicação ser reescrita 1 vez ao ano durante 3 anos. O que ocorre de tão errado assim?
Existem muitos motivos para tudo dar errado:
Equipe tecnicamente despreparada.
Falta de um líder técnico ou arquiteto que conduza o time e evite a deformação da aplicação.
Escopo mal planejado, falta de entendimento da entrega.
Prazo mal calculado.
Falta de visão. Típico caso do sisteminha de cadastro que virou um “ERP”.
Gestor de equipe que pula etapas e/ou não valoriza (não conhece) boas práticas, impondo uma metodologia que funciona apenas na teoria dele.
Motivos para dar errado são muitos, fazer software da forma correta é infinitamente mais complexo que simplesmente entregar software. É necessário ter uma boa equipe, um bom líder e um bom gestor.
Software é um bem durável assim como uma TV, Geladeira ou Carro, paga-se muito caro para desenvolve-lo e muito caro para mante-lo. A diferença é que o software precisa ser flexível e adaptar-se às constantes mudanças de escopo, logo não pode possuir uma engenharia fechada como uma TV e geralmente é ai onde mora a maioria dos problemas.
Para garantir um bom funcionamento da aplicação de forma que ela se adapte às diversas mudanças de escopo com o mínimo de esforço necessário e sem gerar outros problemas secundários, é preciso projetar a aplicação, não basta simplesmente escrever a aplicação e entregar.
Alguns pontos importantes devem sempre ser abordados:
Processo
Processo de desenvolvimento de software é um tema extenso demais, portanto deixarei apenas alguns pontos em destaque que devem ser levados bastante em consideração.
Processo de desenvolvimento de software é um processo diferente de outros, porém infelizmente ainda existem gestores que imaginam que desenvolver uma aplicação é o mesmo que construir um prédio, um navio ou um avião.
Durante a construção de um prédio com a obra em estágio avançado é praticamente impossível aplicar mudanças que mudam o aspecto do projeto, existe uma engenharia fechada e projetada para ser daquela forma, qualquer mudança poderia impactar em ter que iniciar novamente do zero.
Uma aplicação bem projetada deve ser flexível e estar aberta para mudanças de escopo com menor esforço necessário, portanto não devemos nos apegar em simplesmente entregar o que foi inicialmente levantado.
Uma das formas mais práticas de atender a necessidade do cliente é a entrega contínua, releases parciais para acompanhamento e feedback do cliente, é muito mais fácil ajustar uma mudança de escopo com a aplicação 30% desenvolvida do que se estivesse 100%.
Focar em definir bem os requisitos das entregas mais próximas é muito melhor que tentar documentar 100% do projeto antes mesmo de iniciar a codificação.
Sim estamos falando de metodologias ágeis como o Scrum por exemplo, um projeto de software é diferente de um projeto de construção civil e merece um framework de desenvolvimento interativo e incremental.
Planejamento
É necessário analisar qual será o porte da aplicação, não existe uma receita para todo tipo de aplicação, se ela for grande e complexa é necessário abordar alguma prática de arquitetura, por exemplo DDD.
Se a aplicação for simples é necessário evitar exageros e complexidades que não beneficiam as funcionalidades da aplicação.
Orientações como o KISS e o YAGNI dizem para evitar os exageros e manter a aplicação simples, é fato, mas isso não significa que você deve fazer tudo na camada de apresentação por exemplo, uma mínima divisão de responsabilidades é essencial para a qualidade de qualquer tipo de aplicação.
Arquitetura
Arquitetura é a base de tudo, é muito importante fazer a escolha ideal e evitar a famosa “arquitetura BOLOVO” de 3 camadas (Modelo, Business e DataAccess).
Hoje em dia está claro que apesar de funcionar e ser simples esse modelo traz muitos problemas.
É muito importante ter uma pessoa no papel de arquiteto, que conheça diversas estratégias de arquitetura e saiba aplicar bem os inúmeros patterns disponíveis.
Uma vez que possui sua arquitetura definida a aplicação deve se manter dentro do aspecto da arquitetura original, corromper camadas para acessar mais facilmente o banco ou para resolver mais rapidamente um problema pode ser considerado um crime.
Descaracterizar a arquitetura de uma aplicação é o primeiro passo para a famosa “Colcha de Retalhos”. O arquiteto ou líder técnico deve ser responsável por garantir que a arquitetura não esteja sendo corrompida e que seja mantida dentro de sua proposta em toda extensão da aplicação.
Codificação
Mesmo com uma arquitetura definida a tarefa de codificação ainda é complexa e não existem motivos para ser diferente. Codificar exige muitas habilidades além do próprio conhecimento da linguagem e plataforma.
É importantíssimo valorizar os princípios básicos da orientação à objetos como o SOLID que apesar de ser básico podemos dizer que grande parte dos desenvolvedores ainda não sabem como escrever código usando injeção de dependência (DIP) para evitar o alto acoplamento. Realizam heranças sem sentido apenas pelo fato de um objeto herdar hierarquicamente de outro no mundo real. Deixam o princípio de responsabilidade única de lado (SRP) e não criam classes extensíveis (OCP) deixando o código sempre aberto.
Entre outros pontos falhos não citados, os mencionados acima já são responsáveis por grande parte dos problemas existentes em nossas aplicações. Codificar não é uma tarefa menos grandiosa como arquitetar um sistema, pelo contrário, precisamos valorizar mais o programador que possui esses conhecimentos.
Padrões de codificação como nomenclaturas e regras são importantes para manter a expressividade e facilidade de leitura do código. Antigamente programadores se gabavam por fazer códigos que somente eles entendiam, isso não deve existir, o código é uma série de instruções que devem ser de leitura clara e leve, muitas vezes dispensando uma documentação própria.
Uma técnica de codificação que vem ganhando popularidade é o TDD, apesar do TDD ter seu nome diretamente relacionado com testes o TDD na verdade é sobre codificação, os testes que guiam a codificação via TDD servem para garantir a qualidade do código que no final também garantem a sua testabilidade.
Testes
Testes é um assunto complexo, primeiramente devemos entender que teste de software não é apenas rodar a aplicação e ver se tudo está funcionando.
Existe mais de um tipo de teste, podendo ser de unidade, de integração e etc.
Os testes de unidade são necessários quase sempre, podemos dizer que 80% de cobertura de código através dos testes é um resultado bem positivo. Testes de unidade são muito importantes para garantir a boa escrita do código e validar a sua funcionalidade durante o desenvolvimento. Se durante a codificação um teste falhar pode significar que alguma coisa foi feita de forma que o código deixou de funcionar como deveria, logo evitará o deploy prematuro em produção e que o cliente tenha a infelicidade de descobrir um novo bug.
Descobrir um bug em tempo de codificação é N vezes mais barato que descobrir um bug em produção, evita retrabalho, horas extras, replanejamento de deploy, insatisfação com o cliente etc.
Muitas pessoas alegam que escrever testes de unidade fazem o software custar mais caro, pois aumenta mais o esforço de codificação. Isto parece fazer sentido se olharmos apenas no sentido da entrega, mas mesmo assim não faz. Os testes exigem mais esforço de codificação, porém conforme a cobertura de testes aumenta a velocidade de entrega aumenta junto, pois os testes facilitam muito as futuras modificações.
Contabilizar o esforço apenas baseado na entrega é uma falha. Devemos sempre levar em conta a manutenção e evolução, e quando falhamos nesse aspecto estamos iniciando o ponto onde a aplicação vai se tornar cada vez mais complexa para se testar, evoluir, receber manutenções.
Costumo dizer que programamos uma aplicação durante 6 meses e a mantemos durante 3 anos ou mais, sendo assim nosso foco deveria ser na manutenção e evolução e não apenas na entrega. Todo lucro obtido numa entrega mal planejada será perdido durante a manutenção, podendo inclusive chegar ao negativo, e esse é um dos principais motivos que levam diversos projetos falharem e se transformarem em prejuízo quando deveriam continuar gerando lucros.
Os testes de unidade são de responsabilidade do desenvolvedor, os testes de integração e etc podem ser de responsabilidade de um profissional de qualidade ou simplesmente de toda a equipe (equipes multidisciplinares sabem codificar e testar).
Testes de unidade garantem a qualidade de escrita do código e de sua funcionalidade, um código que não está coberto por testes não é um código confiável. Não existe processo ágil de desenvolvimento sem testes, portanto está claro. Testar é preciso!
Quando tudo deu errado.
O planejamento da aplicação não foi bem feito, a arquitetura não foi bem desenhada para atender o propósito da aplicação, a codificação está estruturada, acoplada e sem padrão, não existem testes de unidade e realizar qualquer manutenção significa alterar diversas partes do código e ser obrigado a testar manualmente todas funcionalidades de ponta a ponta, mesmo assim sempre escapando alguns novos bugs.
As noites são longas quando é necessária alguma manutenção, o deploy em produção é uma aventura preocupante, a satisfação de trabalhar no projeto já não existe mais e a pressão não vale a pena.
O gestor do projeto não aceita mais tantos bugs e exige mais assertividade, questionando inclusive a capacidade técnica da equipe, o cliente não aceita mais os prazos para ajustes simples e não quer pagar a mais por isso também.
Não dá mais! Vamos precisar reescrever essa aplicação do zero.
Se você estiver interessado em conhecer mais e aprender como desenvolver aplicações Web com arquitetura baseada em DDD, aplicando os princípios SOLID, diversos Design Patterns e escrevendo testes de unidade inscreva-se em meu curso:
Vamos continuar a troca de experiências, deixe seu comentário abaixo, se gostou e concorda com o artigo compartilhe com seus colegas para transmitirmos essa mensagem para o máximo de pessoas possíveis. 😉
O ASP.NET Brasil Conference 2015 foi um grande sucesso. 400 participantes, palestras de alto nível e muitos prêmios sorteados. Acompanhe as gravações.
Gostaria de agradecer aos presentes no ASP.NET Brasil Conference, neste grande evento eu atuei como organizador e fiquei extremamente satisfeito com o resultado e o feedback dos participantes.
Evidentemente eu gostaria de realizar um evento para 5.000, 10.000 pessoas mas por diversas questões é um número muito grande para se trabalhar. Mas quem não pode ir ora por não encontrar mais ingressos disponíveis ora por não ter tido oportunidade não precisa se chatear, nosso conteúdo foi 100% gravado e está sendo distribuído de forma gratuita.
Caso esteja interessado em aprender a desenvolver aplicações Web Corporativas com as melhores práticas de mercado DDD, Patterns, SOLID e etc, confira meu curso:
Vivemos um novo momento no desenvolvimento de software onde a abstração do relacionamento da aplicação com o banco de dados tende a ser cada vez maior, hoje o banco de dados não é mais o centro da aplicação.
Este é um assunto muito polêmico e que interessa tanto aos desenvolvedores de software como aos especialistas em banco de dados (DBA’s e etc).
Quem desenvolve software a mais de 5 anos pôde acompanhar uma nova tendência crescendo rapidamente, que é a forma na qual a aplicação se relaciona com o banco de dados. Infelizmente ainda hoje os profissionais aprendem na faculdade ou no ambiente de trabalho um modelo de desenvolvimento que já está no passado, e é esta minha motivação em escrever este artigo.
Há muitos anos nosso processo de desenvolvimento vem ocorrendo de uma maneira clássica (e hoje em dia inadequada), o DBA desenvolve toda a modelagem do banco de dados elencando as entidades (tabelas) e seus relacionamentos conforme a necessidade e seu próprio entendimento do negócio, após isso aplicam-se as regras de normalização, valida-se o MER (modelo-entidade-relacional) e pronto! Basta escrever as Stored Procedures, Triggers e Views com as regras de negócio e entregar para o desenvolvedor desenvolver a aplicação que vai consumir o banco.
Então a aplicação é uma camada “casca” que envolve o banco de dados, consumindo e inserindo novas informações?
Isso parece meio ridículo para um desenvolvedor de software hoje em dia, mas esse modelo “3 camadas” já foi muito (e ainda é) pregado e defendido por ai. A primeira camada é a UI (user interface) a segunda é o “core” da aplicação que consome diretamente o banco de dados e a terceira é o próprio banco.
Um dos pontos muito defendidos nessa distribuição de 3 camadas era que a máquina do usuário não precisava possuir muito recurso computacional e nem conhecer as regras de negócio uma vez que poderia haver uma engenharia reversa da aplicação instalada.
Esse argumento não é mais válido hoje em dia dado a infinidade de recursos computacionais e tecnológicos que possuímos (Web, SOA, etc.).
Um outro forte argumento para a centralização no banco de dados é de que o servidor de banco de dados é uma máquina muito rica em recursos computacionais e consegue processar as regras de negócio com mais velocidade do que um servidor de aplicação.
Esse argumento além de passado ocasiona diversos problemas que serão abordados a seguir.
Regras de negócio em Stored Procedures é uma má prática
Poderia elencar diversos adjetivos para a utilização de regras de negócios em stored procedures, porém vamos considerar apenas uma má prática que deve ser evitada a todo custo. Listarei alguns problemas/dificuldades muito frequentes com a adoção desta prática:
A linguagem TSQL (Transact SQL) é muito limitada em comparação as linguagens atuais, requer mais código e dificulta a interpretação/entendimento, requer mais esforço aumentando o tempo de codificação.
É muito difícil escrever testes de unidade para regras de negócio em stored procedures.
O mecanismo que gera o plano de execução para as stored procedures não trabalha bem com condições (IF, Case, etc) podendo ocasionar uma lentidão muito grande ao chamar uma stored procedure com diversos fluxos.
Quebra o conceito de responsabilidade única, a regra pode estar parte no banco, parte na stored procedure. Além de aumentar o esforço na analise e detecção de problemas.
Estimula a escrita de milhares de linhas de código em apenas uma store procedure, algumas delas com mais de 4.000 linhas são bem fáceis de se encontrar por ai. O esforço de manutenção é centenas de vezes maior, além do risco de introdução de novos bugs.
Existem diversos contras além dos listados, porém estes citados são base de muitos problemas comuns no dia a dia. Não entenda que as stored procedures não devem ser utilizadas, até devem dependendo do cenário, porém sem regras de negócio.
E sobre o uso das Triggers?
Dentre um determinado conjunto de decisões que podem ocasionar sérios problemas entre aplicação e banco de dados, uma delas é o uso de Triggers.
Triggers podem ser úteis em alguns cenários, porém existem outras opções mais eficazes. Não há nada que uma trigger faça que não possa ser substituído por outro recurso (externo do controle do banco de dados) e que funcione de forma mais controlada, elegante, e lógico, testável.
O servidor de banco de dados é muito mais rápido que o servidor de aplicação
Fato! E deve ser mesmo, pois a questão é, quantos bancos de dados é possível escalar lado a lado? Manter bancos de dados sincronizados não é uma tarefa simples e não custa nada barato. Na maioria das aplicações existe um único banco de dados ativo (outros de contingência) e N servidores de aplicação consumindo o mesmo banco. É muito mais fácil escalar o servidor de aplicação e muito mais barato (basta conferir os valores de licenciamento de um SGBD). A própria aplicação de atualizações nos servidores de aplicação é muito mais simples do que no servidor de banco de dados.
Não é por que o banco de dados tem capacidade computacional superior que ele deve ser responsável pelo negócio em si, o banco de dados é um repositório de dados, ele deve ter alta disponibilidade para entregar com a maior velocidade possível as consultas. Utilizar os recursos computacionais do banco de dados para validar o negócio é queimar dinheiro e ocasionar problemas de performance.
O mundo é muito maior que o relacionamento entre tabelas no banco de dados
Podemos dizer que hoje o modelo de programação orientada a objetos dominou o mercado de software, sim existem ainda outros modelos (procedural, funcional, etc) porém a grande parte do mercado aderiu a famosa OOP.
Programar orientado a objetos é uma forma muito mais rica de mapear o mundo real do que as simples formas de relacionamento presentes de banco de dados relacionais, existem bancos de dados NoSQL, orientados a objetos, baseados em grafos e etc, porém não são populares como os relacionais (MSSQL, Oracle, MySQL, etc).
Se a orientação a objetos é uma forma de mapeamento mais rica que o modelo entidade-relacionamento dos bancos de dados, deveríamos então modelar nossa aplicação baseadas em classes e não em tabelas? Exato!
Partindo desta decisão como ficaria o relacionamento de uma aplicação orientada a objetos consumindo um banco de dados relacional?
Alguém já resolveu esse problema faz muitos anos, são os famosos ORM’s
O ORM (Object-relational mapping) faz justamente este trabalho, mapeando entidades de software (classes) para entidades de bancos de dados (tabelas). Com isto é possível que uma única tabela de banco de dados seja representada por N classes e vice-versa.
Os ORM’s abstraem o banco de dados
Abstrair o banco de dados significa parar de se preocupar com a modelagem do banco na hora de modelar o sistema, a modelagem pode ser feita em UML utilizando diagramas de classes entre outros diversos tipos de diagramas, no final o mapeamento das suas entidades de software estarão muito melhores definidas do que se tivessem sido feitas com base no MER de banco de dados relacionais.
O ORM cuida para que o seu mapeamento orientado a objetos faça sentido com o mapeamento entidade relacionamento.
A abstração não para por ai, através dos ORM’s é possível desligar a dependência tecnológica do banco de dados na aplicação, a aplicação não estará mais acoplada a uma tecnologia de banco de dados, trocar um banco de dados de uma aplicação não é nada trivial, porém se abstrairmos/desacoplarmos a aplicação do banco através do uso de um ORM essa tarefa seria infinitamente mais fácil.
Produtividade! Desenvolvedores não precisam codificar todas as consultas em linguagem TSQL, o ORM promove um novo modelo de consumo do banco de dados, o ORM fica encarregado de gerar as queries para comunicar-se com o banco, quando necessário o desenvolvedor pode fornecer uma query específica para ser utilizada no banco através do ORM.
Escreva sua aplicação e ganhe banco de dados
O modelo Code First já está presente faz alguns anos no mercado e seu uso vem crescendo de forma gigantesca, uma vez que possuímos o ORM para abstrair o banco de dados, por que não deixar com ele também a criação e atualização do banco?
Isso mesmo, basta mapear todo seu universo do negócio em classes, escrevê-las e no final gerar o banco de dados que se adeque a modelagem da aplicação. Afinal o banco de dados é um repositório de dados.
O Entity Framework em sua nova versão (7) trabalhará apenas com o modelo Code First, para desenvolvimento de aplicações com base de dados já existentes será possível utilizar os recursos de engenharia reversa. A atualização do banco pode ser feita também através de recursos como Migrations.
E o DBA? Morreu?
Muito pelo contrário! Quem vai garantir o perfeito funcionamento do banco? Backup, segurança, implementações de recursos de performance, acompanhar problemas em tempo real e prover rápidas soluções, dividir tabelas especiais em discos independentes e etc… Isso tudo é trabalho do DBA.
Uma coisa que está errada faz muito tempo é que a modelagem de banco de dados é de responsabilidade do DBA, criar mais um campo na tabela, escrever uma stored procedure e outras atividades desse tipo eram de decisão do DBA. Quantas vezes um desenvolvedor já precisou explicar para o DBA o por que precisava de um campo a mais numa tabela, ou por que uma stored procedure devia ser alterada. Isso não faz sentido.
Em um time de desenvolvimento o número de DBA’s é muito menor do que o número de desenvolvedores, nesse cenário o DBA iria trabalhar apenas para atender as necessidades dos desenvolvedores, deixar estas responsabilidades com o DBA é impedir de que ele faça seu real trabalho que é manter o banco de dados altamente disponível utilizando todo seu potencial.
Alguns DBA’s são contra o uso de ORM’s por que ferem seu sentimento de responsabilidade por algo que não deveria ser de sua responsabilidade. Os ORM’s aos poucos estão dando de volta aos desenvolvedores o que era para ser seu desde sempre, o domínio da modelagem e desenvolvimento da aplicação. E claro, numa situação de dúvida não custa nada perguntar a opinião do DBA.
Não importa onde estão os dados
Hoje os bancos de dados relacionais não são a única fonte de dados, os dados podem estar disponíveis através do consumo de serviços (SOA, WCF, WebAPI), podem estar em bancos NoSQL (MongoDB, Redis, etc), podem estar em bancos de dados In-Memory (Hekaton, MemSQL) ou simplesmente sendo abstraídos pelo seu ORM.
Por exemplo no DDD (Domain-Driven-Design) temos uma abstração tão grande que realmente não importa de onde vem os dados, a aplicação não fica dependente disto (apenas na camada de configuração de dados). É neste sentido que devemos caminhar, escrever aplicações focadas no domínio do negócio, que por si próprias saibam resolver qualquer problema e que possam ser expostas através de serviços para que sejam consumidas através de qualquer plataforma, device e etc…
Hoje todo gestor de desenvolvimento fala e se interessa por métodos ágeis, não existe agilidade sem teste, testes de unidade e testes de integração, abstrair o banco de dados é uma forma de promover a possibilidade dos testes, da análise do código escrito, da validação da regra de negócio, poder realizar um Mock de um repositório sem bater no banco de dados e etc. Num mundo onde dependemos de um banco de dados presente para realizar um teste de unidade, com certeza esse não é um mundo muito ágil.
Os banco de dados relacionais irão tornar-se obsoletos no futuro?
Essa mesma pergunta foi feita quando surgiram os bancos de dados orientados a objetos, bancos de dados de grafos, NoSQL.
Os bancos de dados relacionais estão acompanhando a evolução tecnológica, tornando-se cada vez mais performáticos, com mais recursos para alta disponibilidade e etc, porém de outro lado estão surgindo conceitos como Big Data que impulsiona a utilização de bancos NoSQL, redes sociais que utilizam muito o conceito de banco de dados de grafos, entre diversas novidades que não param de surgir.
Acredito que cada modelo de aplicação possui um tipo (ou tipos) de banco de dados que melhor atendem o cenário. O importante é saber como e quando utilizar cada um deles.
Para finalizar
O mundo muda constantemente e cada vez mais rápido, conceitos aprendidos na faculdade anos atrás ou até mesmo hoje em dia podem estar em constante desuso. Políticas antigas de empresas ou até mesmo profissionais conservadores de padrões ultrapassados são muito comuns no nosso mercado.
É importante estar antenado e preparado para quebrar paradigmas, pesquisar e aprofundar-se em melhores práticas de desenvolvimento de software, sempre prezando reusabilidade, performance, escalabilidade e com certeza qualidade garantida por testes de unidade.
Além deste artigo eu participei de um bate papo muito construtivo sobre este assunto com meus colegas do AspNetCast, assista o vídeo também.
* Assine meu canal no Youtube
Vamos continuar a troca de conhecimentos, deixe seu comentário abaixo, pode ser dúvida, elogio ou sua opinião que é sempre muito importante 😉