1. Introdução
Nos artigos anteriores, descrevemos: escopo , fundamentos metodológicos , um exemplo de arquitetura e estrutura . Neste artigo, gostaria de descrever como descrever os processos, sobre os princípios de coleta de requisitos, como os requisitos de negócios diferem dos funcionais, como passar dos requisitos para o código. Fale sobre os princípios do uso do Caso de Uso e como eles podem nos ajudar. Explore exemplos de opções de implementação para padrões de design do Interactor e da camada de serviço.

Os exemplos fornecidos no artigo são fornecidos com a nossa solução LunaPark , que o ajudará nas primeiras etapas das abordagens descritas.
Separe os requisitos funcionais dos requisitos de negócios.
Repetidas vezes, acontece que muitas idéias de negócios não se transformam realmente no produto final pretendido. Isso geralmente ocorre devido à incapacidade de entender a diferença entre os requisitos de negócios e os requisitos funcionais, o que leva a uma coleta inadequada de requisitos, documentação desnecessária, atrasos no projeto e grandes falhas no projeto.
Ou às vezes nos deparamos com situações em que, embora a solução final atenda às necessidades dos clientes, mas de alguma forma as metas de negócios não são atingidas.
Portanto, é imperativo separar os requisitos de negócios dos requisitos funcionais até você começar a defini-los. Vamos dar um exemplo.
Suponha que estamos escrevendo um pedido para uma empresa de entrega de pizzas e decidimos criar um sistema de rastreamento de correio. Os requisitos de negócios são os seguintes:
"Introduzir um sistema baseado na Web e um sistema móvel de rastreamento de funcionários que captura mensageiros em suas rotas e melhora a eficiência, monitorando a atividade dos mensageiros, sua ausência no trabalho e na produtividade do trabalho".
Aqui, podemos distinguir vários recursos característicos que indicam que esses são requisitos do negócio:
- os requisitos de negócios são sempre escritos do ponto de vista do cliente;
- Esses são requisitos amplos e de alto nível, mas ainda orientados para partes;
- eles não são objetivos da empresa, mas ajudam a empresa a atingir objetivos;
- responda às perguntas " por que " e "o que ". O que a empresa deseja receber? E por que ela precisa disso?
Requisitos funcionais são ações que o sistema deve executar para implementar os requisitos de negócios. Assim, os requisitos funcionais estão relacionados à solução ou software desenvolvido. Formulamos os requisitos funcionais para o exemplo acima:
- o sistema deve exibir a longitude e latitude do funcionário via GPS / GLONASS;
- o sistema deve exibir as posições dos funcionários no mapa;
- o sistema deve permitir que os gerentes enviem notificações para seus subordinados de campo.
Destacamos os seguintes recursos:
- os requisitos funcionais são sempre escritos do ponto de vista do sistema;
- eles são mais específicos e detalhados;
- é graças ao cumprimento dos requisitos funcionais que é desenvolvida uma solução eficaz que atende às necessidades do negócio e aos objetivos do cliente;
- responda a pergunta " como ". Como o sistema resolve os requisitos de negócios.
Algumas palavras devem ser ditas sobre requisitos não funcionais (também conhecidos como "requisitos de qualidade"), que impõem restrições ao design ou implementação (por exemplo, requisitos de desempenho, segurança, disponibilidade, confiabilidade). Tais requisitos respondem à pergunta "o que " deve ser o sistema.
Desenvolvimento é a tradução de requisitos de negócios em funcionais. A programação aplicada é a implementação de requisitos funcionais e o sistema - não funcional.
Casos de uso
A implementação de requisitos funcionais geralmente é a mais complexa em sistemas comerciais. Em uma arquitetura pura, os requisitos funcionais são implementados através da camada de Caso de Uso .
Mas, para começar, quero recorrer à fonte. Ivar Jacobson - o autor da definição de Caso de Uso , um dos autores da UML, e da metodologia RUP, em seu artigo Caso de Uso 2.0 - O Hub de Desenvolvimento de Software identifica 6 princípios para o uso de casos de uso:
- torná-los simples através da narrativa;
- tenha um plano estratégico, esteja ciente de todo o cenário;
- foco no significado;
- alinhar o sistema em camadas;
- entregue o sistema passo a passo;
- atender às necessidades da equipe.
Consideramos brevemente cada um desses princípios, eles são úteis para uma melhor compreensão. Abaixo está minha tradução gratuita, com abreviações e inserções, eu recomendo fortemente que você se familiarize com o original.
Simplicidade através da narrativa
A narração faz parte da nossa cultura; Essa é a maneira mais fácil e eficaz de transferir conhecimento e informações de uma pessoa para outra. Essa é a melhor maneira de comunicar o que o sistema deve fazer e ajudar a equipe a se concentrar em objetivos comuns.
Os casos de uso refletem os objetivos do sistema. Para entender o Caso de Uso, contamos uma história. A história conta como atingir uma meta e resolver problemas que surgem ao longo do caminho. Os casos de uso, como um livro de histórias, fornecem uma maneira de identificar e cobrir todas as histórias diferentes, porém relacionadas, de uma maneira simples e abrangente. Isso facilita a coleta, distribuição e compreensão dos requisitos do sistema.
Este princípio se correlaciona com a linguagem Ubiques a partir da abordagem DDD.
Compreendendo o quadro geral
Independentemente de qual sistema você está desenvolvendo, grande, pequeno, software, hardware ou empresa, é muito importante entender o panorama geral. Sem entender o sistema como um todo, você não pode tomar as decisões corretas sobre o que incluir no sistema, o que descartar, quanto custará e quais benefícios trará.
Ivar Jacobson sugere usar o diagrama de casos de uso , que é muito conveniente para coletar requisitos. Se os requisitos forem compilados e claros, o mapa de contexto de Eric Evans é a melhor opção. Freqüentemente, a abordagem Scrum é interpretada para que as pessoas não dediquem tempo a um plano estratégico, considerando o planejamento, mais de duas semanas depois, de uma relíquia do passado. A propaganda de Jeff Sutherland caiu sobre o fluxo de água, e as pessoas que concluíram o treinamento de duas semanas para o Scrum Masters, que tiveram permissão para gerenciar projetos, fizeram o seu trabalho. Mas o senso comum reconhece a importância do planejamento estratégico. Não há necessidade de fazer um plano estratégico detalhado, mas deveria ser.
Foco no valor
Ao tentar entender como o sistema será usado, é sempre importante focar no valor que ele fornecerá a seus usuários e outras partes interessadas. O valor é formado apenas quando o sistema é usado. Portanto, é muito melhor focar em como o sistema será aplicado do que em longas listas de recursos ou recursos que ele pode oferecer.
Os casos de uso fornecem esse foco, ajudando você a se concentrar em como o sistema será usado por um usuário específico para atingir seu objetivo. Os casos de uso abrangem várias maneiras de usar o sistema: aqueles que atingem seus objetivos com sucesso e aqueles que resolvem quaisquer dificuldades que surjam.
Além disso, o autor fornece um esquema maravilhoso, que deve ser prestado a maior atenção:

O diagrama mostra um caso de uso, “Retirada de dinheiro em um caixa eletrônico”. A maneira mais fácil de atingir a meta é descrita em Direção básica (Fluxo básico). Outros casos são descritos como fluxo alternativo. Essas instruções ajudam na narrativa, estruturam o sistema e na escrita de testes.
Camadas
A maioria dos sistemas exige muito trabalho antes de estarem prontos para uso. Eles têm muitos requisitos, a maioria dos quais depende de outros requisitos, devem ser implementados antes que os requisitos sejam atendidos e avaliados.
É um grande erro criar esse sistema de cada vez. O sistema deve ser construído a partir de peças, cada uma com um valor claro para os usuários.
Essas idéias ressoam com abordagens ágeis e com idéias de domínio .
Lançamento passo a passo do produto
A maioria dos sistemas de software evoluiu ao longo de muitas gerações. Eles não são produzidos de cada vez; eles são criados como uma série de lançamentos, cada um dos quais é construído em um release anterior. Mesmo os lançamentos em si geralmente não saem de uma vez, mas desenvolvem-se através de uma série de versões intermediárias. Cada etapa fornece uma versão clara e utilizável do sistema. É assim que todos os sistemas devem ser criados.
Atenda às necessidades da equipe
Infelizmente, não há solução universal para problemas de desenvolvimento de software; equipes diferentes e situações diferentes exigem estilos diferentes e diferentes níveis de detalhe. Independentemente dos métodos e técnicas que você escolher, verifique se eles são adaptáveis o suficiente para atender às necessidades atuais da equipe.
Eric Evans, em seu livro, recomenda que você não gaste muito tempo descrevendo todos os processos por meio da UML. Basta usar qualquer esquema visual. Equipes diferentes, projetos diferentes exigem um nível diferente de detalhes, pois o próprio autor da UML fala sobre isso.
Implementação
Na arquitetura pura, Robert Martin define os seguintes casos de uso :
Esses casos de uso orquestram o fluxo de dados de e para as entidades e direcionam essas entidades a usar suas Regras de Negócios Críticas para atingir os objetivos do caso de uso.
Vamos tentar traduzir essas idéias em código. Vamos relembrar o esquema a partir do terceiro princípio de uso dos Casos de Uso e tomá-lo como base. Considere um processo de negócios realmente complexo: “Cozinhando uma torta de repolho”.
Vamos tentar decompor:
- verifique a disponibilidade do produto;
- tirá-los do estoque;
- amasse a massa;
- deixe a massa subir;
- prepare o recheio;
- faça uma torta;
- assar uma torta.
Implementamos toda essa sequência por meio do Interator , e cada etapa será implementada por meio de uma função ou Objeto Funcional na Camada de Serviço.
Sequência de ações (Interator)

Eu recomendo iniciar o desenvolvimento de um processo de negócios complexo com a Sequência de Ações . Mais precisamente, não é assim, você deve determinar o domínio do domínio ao qual o processo de negócios pertence. Esclareça todos os requisitos de negócios. Identifique todas as entidades envolvidas no processo. Documente os requisitos e definições de cada Entidade na base de conhecimento.
Pinte tudo no papel em etapas. Às vezes você precisa de um diagrama de sequência. Seu autor é o mesmo que inventou o Caso de Uso - Ivar Jacobson. O diagrama foi inventado por ele quando estava desenvolvendo um sistema de manutenção de rede telefônica para Erickson, baseado no circuito de relés. Eu realmente gosto deste diagrama, e o termo Sequência , na minha opinião, é mais expressivo do que o termo Interator . Mas, tendo em vista a maior prevalência deste último, usaremos o termo familiar - Interator .
Uma pequena dica ao descrever um processo de negócios é uma boa ajuda para você, a principal regra do gerenciamento de documentos pode se tornar: “Como resultado de qualquer atividade comercial, um documento deve ser elaborado”. Por exemplo, estamos desenvolvendo um sistema de descontos. Com um desconto, de fato, do ponto de vista comercial, concluímos um acordo entre a empresa e o cliente. Todas as condições devem ser definidas neste contrato. Ou seja, no domínio DiscountSystem, você terá Entites :: Contract. Não vincule o desconto ao cliente, mas crie um Contrato de entidade , que descreve as regras para seu fornecimento.
Voltemos à descrição do nosso processo de negócios, depois que ele se tornou transparente para todas as pessoas envolvidas no seu desenvolvimento, e todo o seu conhecimento foi corrigido. Eu recomendo que você comece a escrever código com a sequência de ações .
O Modelo de Design de Sequência é responsável por:
- sequência de ações ;
- coordenação dos dados transmitidos entre as ações ;
- processar erros cometidos por ações durante sua execução;
- retorno do resultado do conjunto de ações comprometidas;
- IMPORTANTE : a responsabilidade mais importante desse padrão de design é a implementação da lógica de negócios.
Eu gostaria de me debruçar sobre a última responsabilidade com mais detalhes se tivermos algum tipo de processo complexo - devemos descrevê-la de maneira que fique claro o que acontece sem entrar em detalhes técnicos. Você deve descrevê-lo da maneira mais expressiva possível . Confie esta classe ao membro mais experiente da sua equipe.
Vamos voltar ao assunto: vamos tentar descrever o processo de sua preparação através do Interactor .
Implementação
Dou um exemplo de implementação com nossa solução LunaPark , que apresentamos em um artigo anterior.
module Kitchen module Sequences class CookingPieWithabbage < LunaPark::Interactors::Sequence TEMPERATURE = Values::Temperature.new(180, unit: :cel) def call! Services::CheckProductsAvailability.call list: ingredients dough = Services::BeatDough.call from: Repository::Products.get(beat_ingredients) filler = Services::MakeabbageFiller.call from: Repository::Products.get(filler_ingredients) pie = Services::MakePie.call dough, with: filler bake = Services::BakePie.new pie, temp: TEMPERATURE sleep 5.min until bake.call pie end private attr_accessor :beat_ingredients, :filler_ingredients attr_accessor :pie def ingredients_list beat_ingredients_list + filler_ingredients_list end end end end
Como podemos ver, a call!
descreve toda a lógica comercial do processo de assar bolos. E é conveniente usar para entender a lógica do aplicativo.
Além disso, podemos descrever facilmente o processo de assar torta de peixe, substituindo o MakeabbageFiller
pelo MakeFishFiller
. Assim, mudamos muito rapidamente o processo de negócios, sem modificações significativas no código. Além disso, podemos deixar as duas sequências ao mesmo tempo, dimensionando os casos de negócios.
Arranjos
- Método de
call!
é um método necessário e descreve a ordem das ações . - Cada parâmetro de inicialização pode ser descrito através de um setter ou
attr_acessor
:
class Foo < LunaPark::Interactors::Sequence
- O restante dos métodos deve ser privado.
Exemplo de uso
beat_ingredients = [ Entity::Product.new :flour, 500, :gr, Entity::Product.new :oil, 50, :gr, Entity::Product.new :salt, 1, :spoon, Entity::Product.new :milk, 150, :ml, Entity::Product.new :egg, 1, :unit, Entity::Product.new :yeast, 1, :spoon ] filler_ingredients = [ Entity::Product.new :cabbage, 500, :gr, Entity::Product.new :salt, 1, :spoon, Entity::Product.new :pepper, 1, :spoon ] cooking = CookingPieWithabbage.call( beat_ingredients: beat_ingredients, filler_ingredients: filler_ingredients )
O processo é representado através do objeto e temos todos os métodos necessários para chamá-lo - a chamada teve êxito, ocorreu algum erro durante a chamada e, em caso afirmativo, qual?
Tratamento de erros
Se agora recordarmos o terceiro princípio do aplicativo Caso de Uso, prestamos atenção ao fato de que, além da linha Principal , também tínhamos direções alternativas . Estes são os erros que devemos tratar. Considere um exemplo: certamente não queremos que os eventos continuem assim, mas não podemos fazer nada a respeito, a dura realidade é que as tortas queimam periodicamente.
O Interactor intercepta todos os erros herdados da LunaPark::Errors::Processing
.
Como podemos acompanhar o bolo? Para fazer isso, defina o erro BakePie
ação BakePie
.
module Kitchen module Errors class Burned < LunaPark::Errors::Processing; end end end
E durante o cozimento, verifique se nossa torta não está queimada:
module Kitchen module Services class BakePie < LunaPark::Callable def call
Nesse caso, a interceptação de erros funcionará e poderemos lidar com eles nos
.
Erros não herdados do Processing
são percebidos como erros do sistema e serão interceptados no nível do servidor. Salvo indicação em contrário, o usuário receberá um 500 ServerError.
Prática de uso
1. Tente descrever todas as chamadas no método de chamada!
Você não deve implementar cada ação em um método separado, isso torna o código mais inchado. Você precisa olhar a classe inteira várias vezes para entender como ela funciona. Mime a receita para assar uma torta:
module Service class CookingPieWithabbage < LunaPark::Interactors::Sequence def call! check_products_availability make_cabbage_filler make_pie bake end private def check_products_availability Services::CheckProductsAvailability.call list: ingredients end
Use a chamada de ação diretamente na sala de aula. Do ponto de vista do ruby, essa abordagem pode parecer incomum, por isso parece mais legível:
class DrivingStart < LunaPark::Interactors::Sequence def call! Service::CheckEngine.call Service::StartUpTheIgnition.call car, with: key Service::ChangeGear.call car.gear_box, to: :drive Service::StepOnTheGas.call car.pedals[:right] end end
2. Se possível, use o método de classe de chamada
3. Não crie objetos funcionais para digitar o código; procure de acordo com a situação
Camada de serviço

O Interator, como dissemos, descreve a lógica de negócios no nível mais alto. A camada de serviço ( camada de serviço) já revela os detalhes da implementação dos requisitos funcionais. Se estamos falando de fazer uma torta, então, no nível do Interator, simplesmente dizemos “amasse a massa”, sem entrar em detalhes sobre como amassar. O processo de amassar é descrito no nível de serviço . Vamos voltar à fonte original, o grande livro azul :
No domínio aplicado, existem operações que não conseguem encontrar um local natural em um objeto do tipo Entidade ou Objeto de Valor. Eles são inerentemente não objetos, mas atividades. Mas como a base do nosso paradigma de modelagem é a abordagem de objetos, tentaremos transformá-los em objetos.
Nesse ponto, é fácil cometer um erro comum: abandone a tentativa de colocar a operação em um objeto adequado para ela e, portanto, faça a programação procedural. Mas se você forçar uma operação em um objeto com uma definição estranha a ele, isso fará com que o próprio objeto perca sua pureza, dificultando a compreensão e a refatoração. Se você implementar muitas operações complexas em um objeto simples, pode se tornar incompreensível o que, o que você está fazendo não está claro o que. Tais operações geralmente envolvem outros objetos da área de assunto e a coordenação entre eles é realizada para executar uma tarefa conjunta. A responsabilidade adicional cria cadeias de dependência entre objetos, misturando conceitos que podem ser considerados independentemente.
Ao escolher um local para a implementação de um funcional, use sempre o bom senso. Sua tarefa é tornar o modelo mais expressivo. Vejamos um exemplo: "Precisamos cortar madeira":
module Entities class Wood def chop
Este método será um erro. A lenha não se corta, precisamos de um machado:
module Entities class Axe def chop(sacrifice)
Se usarmos um modelo de negócios simplificado, isso será suficiente. Mas, se o processo precisar ser modelado com mais detalhes, precisaremos de uma pessoa que corte essa lenha e, talvez, algum log que será usado como suporte para o processo.
module Entities class Human def chop_firewood(wood, axe, chock)
Como você provavelmente já adivinhou, essa não é uma boa ideia. Nem todos nós estamos empenhados em cortar madeira, isso não é um dever direto de uma pessoa. Frequentemente, vemos como estão sobrecarregados os modelos no Ruby on Rails, que contêm uma lógica semelhante: obter descontos, adicionar mercadorias à cesta, retirar dinheiro para a balança. Essa lógica não se aplica à entidade, mas ao processo em que essa entidade está envolvida.
module Services class ChopFirewood
Depois de descobrirmos a lógica que armazenamos nos Serviços, tentaremos implementar uma delas. Na maioria das vezes, os serviços são implementados através de métodos ou objetos funcionais.
Objetos Funcionais
Um objeto funcional atende a um requisito funcional. Na sua forma mais primitiva, um objeto funcional possui um único método público - call
.
module Serivices class Sum def initialize(x, y) @x = x @y = y end def call x + y end def self.call(x,y) new(x,y).call end private attr_reader :x, :y end end
Tais objetos têm várias vantagens: são concisos, são muito simples de testar. Existe uma desvantagem: esses objetos podem se tornar um número grande. Existem várias maneiras de agrupar objetos semelhantes; em parte de nossos projetos, os dividimos por tipo:
- Objeto de serviço (Serviço) - um objeto, cria um novo objeto;
- Comando (Comando) - altera o objeto atual;
- Guardião (Guarda) - retorna um erro se algo der errado.
Objeto de serviço
Em nossa implementação, Serviço - implementa um requisito funcional e sempre retorna um valor.
module KorovaMilkBar module Services class FindMilk < LunaPark::Callable GLASS_SIZE = Values::Unit.wrap '200g' def initialize(fridge:) @fridge = fridge end def call fridge.shelfs.find { |shelf| shelf.has?(GLASS_SIZE, of: :milk) } end private attr_reader :fridge end end end FindMilk.call(fridge: the_red_one)
Comando
Em nossa implementação, Command - executa uma ação , modifica o objeto, se true retornar true. De fato, a equipe não cria um objeto, mas modifica um existente.
module KorovaMilkBar module Commands class FillGlass < LunaPark::Callable def initialize(glass, with:) @glass = glass @content = with end def call glass << content true end private attr_reader :fridge end end end glass = Glass.empty milk = Milk.new(200, :gr) glass.empty?
Guardião (Guarda)
O vigia realiza uma verificação lógica e, em caso de falha, gera um erro de processamento. Esse tipo de objeto não afeta a Direção Principal de nenhuma maneira, mas nos muda para a Direção Alternativa se algo der errado.
Ao servir leite, é importante garantir que ele seja fresco:
module KorovaMilkBar module Guards class IsFresh < LunaPark::Callable def initialize(product) @products = products end def call products.each do |product| raise Errors::Rotten, "
Você pode achar conveniente separar objetos funcionais por tipo. Você pode adicionar seu próprio, por exemplo, Builder - cria um objeto com base em parâmetros.
Arranjos
- O método de
call
é o único método público obrigatório. - O método
initialize
é o único método público opcional. - O restante dos métodos deve ser privado.
- Erros lógicos devem ser herdados da
LunaPark::Errors::Processing
.
Tratamento de erros
Existem 2 tipos de erros que podem ocorrer durante a operação de uma ação .
Erros de tempo de execução
Tais erros podem ocorrer como resultado da violação da lógica de processamento.
Por exemplo:
- ao criar um email de usuário é reservado;
- quando você tenta beber leite, acaba;
- outro microsserviço rejeitou a ação (por um motivo lógico, e não porque o serviço não está disponível).
Com toda a probabilidade, o usuário desejará saber sobre esses erros. Além disso, esses são provavelmente os erros
que podemos prever.
Tais erros devem ser herdados do LunaPark::Errors::Processing
Erros de sistema
Erros que ocorreram como resultado de uma falha do sistema.
Por exemplo:
- o banco de dados não funciona;
- algo dividido por zero.
Provavelmente, não podemos prever esses erros e não podemos dizer nada ao usuário, exceto que tudo está muito ruim, e enviar aos desenvolvedores um relatório pedindo ação. Esses erros devem ser herdados do SystemError
Há também erros de validação , que discutiremos em mais detalhes no próximo artigo.
Prática de uso
1. Use variáveis para aumentar a legibilidade
module Fishing
2. Passe objetos, não parâmetros
Tente simplificar o inicializador se o processamento de parâmetros não for o seu objetivo.
Passe objetos, não parâmetros.
module Service
3. Use o nome Actions - o verbo da ação e o objeto de influência.
4. Se possível, use o método de classe de chamada
Geralmente, uma instância da classe Actions , raramente usada, exceto a escrita, para fazer uma chamada.
5. O tratamento de erros não é uma tarefa de serviço
Módulos
Até esse momento, consideramos a implementação da camada Serviço como um conjunto de objetos funcionais. Mas podemos facilmente colocar métodos nessa camada:
module Services def sum(a, b) a + b end end
Outro problema que nos confronta é um grande número de instalações de serviço. Em vez do “modelo gordo rails”, que chegamos ao limite, obtemos a “pasta gorda de serviços”. Existem várias maneiras de organizar a estrutura para reduzir a escala da tragédia. Eric Evans resolve isso combinando várias funções em uma classe. Imagine que precisamos modelar os processos de negócios da babá, Arina Rodionovna, ela pode alimentar Pushkin e colocá-lo na cama:
class NoonService def initialize(arina_radionovna, pushkin)
Essa abordagem é mais correta do ponto de vista do POO. Mas sugerimos abandoná-lo, pelo menos nos estágios iniciais. Programadores pouco experientes começam a escrever muito código nesta classe, o que acaba levando a um aumento da conectividade. Em vez disso, você pode usar o módulo, representando a atividade como alguma abstração:
module Services module Noon class ToFeed def call!
Ao dividir em módulos, um baixo acoplamento externo (baixo acoplamento) deve ser observado com alta coesão interna (alta coesão), mas usamos módulos como Serviços ou Interatores, o que também contraria as idéias da arquitetura pura. Esta é uma escolha consciente que facilita a percepção. Pelo nome do arquivo, entendemos qual padrão de design essa ou aquela classe implementa, se para um programador experiente isso é óbvio, para um iniciante esse nem sempre é o caso. Quando sua equipe estiver pronta, descarte esse excesso.
Para citar outro pequeno trecho do grande livro azul:
Escolha os módulos que contam o histórico do sistema e contêm conjuntos coerentes de conceitos. A partir disso, muitas vezes surge uma baixa dependência dos módulos um do outro. Mas se não for assim, encontre uma maneira de mudar o modelo de maneira a separar os conceitos, ou procure o conceito que estava faltando no modelo, que poderia se tornar a base do módulo e, assim, reunir os elementos do modelo de maneira natural e significativa. Atingir baixa dependência de módulos um no outro, no sentido de que conceitos em diferentes módulos podem ser analisados e percebidos independentemente um do outro. Refine o modelo até que os limites naturais apareçam nele, de acordo com os conceitos de alto nível da área de assunto, e o código correspondente não seja dividido de acordo.
Dê os nomes dos módulos que serão incluídos no IDIOMA UNIFICADO. Ambos os módulos e seus nomes devem refletir o conhecimento e a compreensão da área de assunto.
O tópico dos módulos é amplo e interessante, mas na íntegra claramente vai além do tópico deste artigo. Da próxima vez, falaremos com você sobre Repositórios e Adaptadores . Abrimos um canal de telegrama acolhedor, onde gostaríamos de compartilhar materiais sobre o tema DDD. Suas perguntas e feedback são bem-vindos.