Modelando uma aplicação com Domain Driven Design
Como mapear uma idéia para resolver um problema em um código?
Ao desenvolver software no dia-a-dia nos deparamos com um dilema: como mapear uma idéia pensada de forma abstrata para resolver um problema em um código? E como fazer ele entregar um valor a quem está com o problema (e que provavelmente será o usuário do seu software)?
Primeiro levantamos as idéias e contexto do problema a resolver. Após receber as descrições delas, nós como programadores, maquinamos um bocado aqui, realizamos um teste ali, pensamos sobre o como traduzir a resolução deste problema em software e saimos fazendo o que sabemos melhor: codificar.
Horas de trabalho depois, terminamos o nosso código com sucesso e (geralmente) satisfeitos pelo nosso trabalho, afinal trabalhamos duro, pensamos muito nas arquiteturas, padrões e ferramentas que podíamos usar para entregar o melhor software possível! O que poderia dar de errado? Vamos entregar o software!
E aí começa começa "outro problema"…
O problema: gestão de expectativas
Por mais que tenhamos nos atentado aos detalhes no entedimento das idéias dos usuários, acabamos adotando uma premissa de que essas idéias discutidas serão passadas para o software como foram definidas.
Porém, por várias razões, acabamos passando essas idéias do mundo real ao software de uma forma diferente:
Existe uma razão simples para isto ocorrer: para ir do mundo real ao software estas idéias passam por pessoas, cada uma com diferentes percepções de mundo e maneiras de explicá-las. Isto é perfeitamente normal.
Assim, para poder montar um bom software, primeiro temos que compreender a parte humana em volta dele (sim, como programadores vamos ter que conversar com as pessoas 🙂). Pensando nisso, para modelarmos o software de uma maneira mais efetiva, iremos usar uma das ferramentas do DDD para mapear essa parte humana: a comunicação.
Mas antes de fazer isso, DD o quê?
Entendendo o Domain Driven Design
No Domain Driven Design (DDD), publicado por Eric Evans em 2004 em um livro do mesmo nome, é apresentado como uma prática de desenvolvimento onde estruturamos a implementação (design) do software em função de uma série de requisitos e especificações do domínio (área de interesse onde o software resolverá um problema, "as regras de negócio" do software).
Como desenvolvedores buscamos entender de forma incremental o que é o Domínio através de interações com os especialistas de domínio (usuários, analistas de negócio, stakeholders) e em conjunto, construir um modelo de negócio que será refletido na estrutura do software (classes, objetos).
Para construir este modelo uma linguagem única de comunicação do projeto é estabelecida, a linguagem ubíqua. Ela tem como objetivo descrever o que se sabe do domínio e garantir que todos os envolvidos no desenvolvimento do software tenham o mesmo entedimento sobre ele.
A estrutura desta linguagem é tão importante que os termos utilizados por ela também serão os termos que refletiremos no código do software, de forma a que possamos ler o código e assim se ter uma noção sobre quais regras de negócio ele se refere.
Um caso (quase) prático
Imagine que fomos chamados as pressas para ajudar um cliente em um projeto, sem saber sobre o que o projeto se trata. A única informação que temos é que montaremos um programa para resolver um problema de negócio desse cliente.
Primeira visita
Chegando na reunião na empresa do cliente, após uma apresentação breve, ele nos explica que este é um problema recente, que eles também estão tentando entender e que até agora só sabem da seguinte informação:
“O coiso deve ser ligado junto com o treco para conseguir a outra coisa.”
Após um momento pensando… "o quê?", partimos para entender o problema. Olhando uma segunda vez para as informações, você nota três pontos interessantes: "O coiso deve ser ligado junto com o treco para conseguir a outra coisa."
Perguntamos: "neste problema tudo gira em torno do coiso, o treco e a coisa?", ao que o cliente diz "sim".
Ótimo, esses três pontos parecem papéis que podem ser executados por objetos no nosso software, podemos estruturar eles como classes. Mas… como elas vão interagir entre si?
Olhando novamente o problema, mais uma pista: "O coiso deve ser ligado junto com o treco para conseguir a outra coisa."
Fazemos um rascunho e apresentamos para o cliente, "podemos dizer que o coiso, o treco e a coisa interagem desse jeito?"
O cliente diz: "Sim".
Perfeito, temos material para um primeiro protótipo para o cliente. Nos despedimos, explicando que em breve daremos um protótipo para que ele possa ver se a resolução do problema dele faz sentido e vamos para o nosso escritório programar. Chegamos ao seguinte programa em linhas gerais:
Segunda visita
Após mostrar o protótipo para o cliente, tentamos entender com o cliente melhor alguns pontos do problema (afinal o que diacho é o "coiso"?). Após uma conversa, ele nos diz: "executando o protótipo algumas vezes, percebi que falta o GPS e o cálculo da rota aqui".
"Hum… mas não tinha o treco e a coisa?", perguntamos. Ao que ele diz: "na verdade não, o problema que precisamos resolver é:"
“O coiso deve ser usado no GPS para conseguir calcular a rota.”
Sob uma nova ótica, percebemos que:
- o treco na verdade é o GPS;
- a coisa na verdade é a rota.
As interações também mudaram:
- o coiso é usado com o GPS e não ligado;
- a rota é calculada pelo GPS e não "conseguida".
Com estes novos fatos, começamos a ver o domínio tomando forma: um problema de navegação em mapas. E com eles temos mais insumos para refatorar o código e apresentar um novo protótipo, agora com um pouco mais de valor para o cliente. Refatorando ele, o código é estruturado da seguinte forma:
Terceira visita
O problema está ficando mais claro e estamos próximos de resolvê-lo, mas… o que é o coiso? é o abajur?
Em uma última conversa com o cliente, utilizando o protótipo novamente, chegamos a solução do mistério:
“O endereço deve ser usado no GPS para conseguir calcular a rota”
No final das contas era o endereço! Com este novo fato em mente, fazemos mais uma alteração no código:
Uma vez validado o protótipo, o cliente nos informa que realmente era este o problema que ele precisava resolver desde o início, este era o valor que precisava ser entregue para ele.
Histórias desbravadas, mas e o código?
Agora sabendo como é a dinâmica de obtenção de conhecimento do domínio, como podemos estruturar o nosso software de forma efetiva para refletir a linguagem úbiqua para o código?
Olhando diretamente para o código, podemos aplicar o DDD tático, utilizando uma série de padrões de código, building blocks, para representar elementos da linguagem úbiqua.
Os building blocks mais conhecidos são: as Entidades, os Objetos de valor, os Serviços, as Agregações, as Fábricas e os Repositórios. Atualmente eles podem ser estruturados em alguns tipos de arquitetura como a arquitetura em camadas, a arquitetura hexagonal e a arquitetura limpa (Clean Architecture).
A medida que um grupo de elementos do domínio vai ficando mais evidente, podemos dividi-lo em subdomínios, os Contextos Delimitados (Bounded Contexts), isolando a complexidade de cada subdomínio. Para lidar com estes grupos mais complexos, podemos aplicar algumas estratégias do DDD estratégico.
Tanto o DDD tático, quanto o DDD estratégico são assuntos densos por si só e serão abordados em próximos artigos.
Continua…
Este artigo foi baseado nas minhas palestras do TDC em 2019:
- TDC Floripa: Modelando sua aplicação Node.js com DDD
- TDC SP: Modelando sua aplicação Ruby com DDD
Tem curiosidade sobre estes temas? Para saber mais:
Livros:
- Domain Driven Design: Tackling Complexity in the Heart of Software
- Implementing Domain-Driven Design
- Domain-Driven Design Distilled
Talks: