Uma parte do design orientado a domínio sobre a qual não parece haver muitos detalhes é como e por que você deve isolar seu modelo de domínio de sua interface. Estou tentando convencer meus colegas de que essa é uma boa prática, mas não pareço estar fazendo muito progresso ...
Eles usam entidades de domínio onde quiserem nas camadas de apresentação e interface. Quando eu argumento para eles que eles deveriam usar modelos de exibição ou DTOs para isolar a camada de Domínio da camada de interface, eles rebatem que não vêem o valor de negócios em fazer algo assim, porque agora você tem um objeto de IU para manter bem como o objeto de domínio original.
Portanto, estou procurando algumas razões concretas que posso usar para fazer backup disso. Especificamente:
- Por que não devemos usar objetos de domínio em nossa camada de apresentação?
(se a resposta for a óbvia, 'desacoplamento', então explique por que isso é importante neste contexto) - Devemos usar objetos ou construções adicionais para isolar nossos objetos de domínio da interface?
fonte
Respostas:
Simplesmente, o motivo é de implementação e deriva. Sim, sua camada de apresentação precisa saber sobre seus objetos de negócios para ser capaz de representá-los adequadamente. Sim, inicialmente parece que há muita sobreposição entre a implementação dos dois tipos de objetos. O problema é que, com o passar do tempo, as coisas vão sendo adicionadas em ambos os lados. A apresentação muda e as necessidades da camada de apresentação evoluem para incluir coisas que são completamente independentes de sua camada de negócios (cor, por exemplo). Enquanto isso, seus objetos de domínio mudam com o tempo e, se você não tiver o desacoplamento apropriado de sua interface, corre o risco de bagunçar sua camada de interface ao fazer mudanças aparentemente benignas em seus objetos de negócios.
Pessoalmente, acredito que a melhor maneira de abordar as coisas é por meio do paradigma de interface estritamente imposto; ou seja, sua camada de objeto de negócios expõe uma interface que é a única maneira pela qual ele pode ser comunicado; nenhum detalhe de implementação (ou seja, objetos de domínio) sobre a interface é exposto. Sim, isso significa que você deve implementar seus objetos de domínio em dois locais; sua camada de interface e em sua camada BO. Mas essa reimplementação, embora possa inicialmente parecer um trabalho extra, ajuda a reforçar o desacoplamento que economizará TONELADAS de trabalho em algum momento no futuro.
fonte
Eu mesma lutei com isso. Há casos em que faz sentido usar um DTO na apresentação. Digamos que eu queira mostrar uma lista suspensa de empresas em meu sistema e preciso de sua id para vincular o valor.
Bem, em vez de carregar um CompanyObject que pode ter referências a assinaturas ou quem sabe o que mais, eu poderia enviar de volta um DTO com o nome e id. Este é um bom uso IMHO.
Agora veja outro exemplo. Eu tenho um objeto que representa uma estimativa, esta estimativa pode ser composta de mão de obra, equipamento etc, pode ter muitos cálculos que são definidos pelo usuário que pegam todos esses itens e os somam (cada estimativa pode ser diferente com diferentes tipos de cálculos). Por que eu deveria modelar este objeto duas vezes? Por que não posso simplesmente fazer com que minha IU faça uma enumeração dos cálculos e os exiba?
Eu geralmente não uso DTOs para isolar minha camada de domínio de minha IU. Eu os uso para isolar minha camada de domínio de um limite que está fora do meu controle. A ideia de que alguém colocaria informações de navegação em seu objeto de negócio é ridícula, não contamine seu objeto de negócio.
A ideia de que alguém colocaria validação em seu objeto de negócio? Bem, eu digo que isso é uma coisa boa. Sua IU não deve ter a responsabilidade exclusiva de validar seus objetos de negócios. Sua camada de negócios DEVE fazer sua própria validação.
Por que você colocaria o código de geração de IU em um objeto busienss? No meu caso, tenho objetos separados que geram o código da IU separadamente da IU. Eu tenho vários objetos que renderizam meus objetos de negócios em Xml, a ideia de que você tem que separar suas camadas para evitar esse tipo de contaminação é tão estranha para mim porque por que você colocaria o código de geração de HTML em um objeto de negócios ...
Editar Como penso um pouco mais, há casos em que as informações da IU podem pertencer à camada de domínio. E isso pode obscurecer o que você chama de camada de domínio, mas trabalhei em um aplicativo multilocatário, que tinha um comportamento muito diferente tanto na aparência da IU quanto no fluxo de trabalho funcional. Dependendo de vários fatores. Nesse caso, tínhamos um modelo de domínio que representava os locatários e sua configuração. Sua configuração passou a incluir informações da IU (rótulos para campos genéricos, por exemplo).
Se eu tivesse que projetar meus objetos para torná-los persistentes, também deveria ter que duplicar os objetos? Lembre-se de que se deseja adicionar um novo campo, agora você tem dois locais para adicioná-lo. Talvez isso levante outra questão se você usar DDD, todas as entidades persistentes são objetos de domínio? Eu sei que no meu exemplo eles eram.
fonte
Você faz isso pelo mesmo motivo que mantém o SQL fora de suas páginas ASP / JSP.
Se você mantiver apenas um objeto de domínio, para uso na camada de apresentação E de domínio, esse objeto logo se tornará monolítico. Ele começa a incluir o código de validação da IU, o código de navegação da IU e o código de geração da IU. Em seguida, você logo adiciona todos os métodos da camada de negócios em cima disso. Agora sua camada de negócios e UI estão todas misturadas, e todas elas estão bagunçando a camada de entidade de domínio.
Você quer reutilizar aquele widget de IU bacana em outro aplicativo? Bem, você tem que criar um banco de dados com este nome, esses dois esquemas e essas 18 tabelas. Você também deve configurar o Hibernate e o Spring (ou seus frameworks de escolha) para fazer a validação do negócio. Oh, você também deve incluir essas 85 outras classes não relacionadas porque elas são referenciadas na camada de negócios, que por acaso está no mesmo arquivo.
fonte
Discordo.
Acho que a melhor maneira é começar com os objetos de domínio em sua camada de apresentação ATÉ QUE FAZ SENTIDO FAZER DE OUTRO MODO.
Ao contrário da crença popular, "Objetos de domínio" e "Objetos de valor" podem coexistir alegremente na camada de apresentação. E esta é a melhor maneira de fazer isso - você obtém o benefício dos dois mundos, duplicação reduzida (e código clichê) com os objetos de domínio; e a adaptação e simplificação conceitual do uso de objetos de valor em solicitações.
fonte
A resposta depende da escala de sua aplicação.
Aplicativo CRUD simples (criar, ler, atualizar, excluir)
Para aplicativos básicos crud, você não tem nenhuma funcionalidade. Adicionar DTO em cima de entidades seria uma perda de tempo. Isso aumentaria a complexidade sem aumentar a escalabilidade.
Aplicativo não CRUD moderadamente complicado
Nesse tamanho de aplicativo, você terá poucas entidades com verdadeiro ciclo de vida e alguma lógica de negócios associada a eles.
Adicionar DTOs neste caso é uma boa ideia por alguns motivos:
Aplicativo Corporativo Complicado
Uma única entidade pode precisar de várias formas de apresentação. Cada um deles precisará de um conjunto diferente de campos. Neste caso, você encontra os mesmos problemas do exemplo anterior, além da necessidade de controlar a quantidade de campos visíveis para cada cliente. Ter um DTO separado para cada cliente ajudará você a escolher o que deve ser visível.
fonte
Estamos usando o mesmo modelo no servidor e na interface do usuário. E é uma dor. Temos que refatorá-lo algum dia.
Os problemas são principalmente porque o modelo de domínio precisa ser cortado em pedaços menores para ser capaz de serializá-lo sem ter todo o banco de dados referenciado. Isso torna mais difícil o uso no servidor. Links importantes estão faltando. Alguns tipos também não são serializáveis e não podem ser enviados ao cliente. Por exemplo, 'Tipo' ou qualquer classe genérica. Eles precisam ser não genéricos e o Tipo precisa ser transferido como string. Isso gera propriedades extras para serialização, elas são redundantes e confusas.
Outro problema é que as entidades na IU realmente não se encaixam. Estamos usando ligação de dados e muitas entidades têm muitas propriedades redundantes apenas para fins de interface do usuário. Além disso, existem muitos 'BrowsableAttribute' e outros no modelo de entidade. Isso é muito ruim.
No final, acho que é apenas uma questão de qual caminho é mais fácil. Pode haver projetos em que funciona bem e não há necessidade de escrever outro modelo DTO.
fonte
É sobre dependências em sua maior parte. A estrutura funcional central da organização tem seus próprios requisitos funcionais e a IU deve permitir que as pessoas modifiquem e visualizem o núcleo; mas o núcleo em si não deve ser necessário para acomodar a IU. (Se for necessário, geralmente é uma indicação de que o núcleo não foi projetado de maneira adequada)
Meu sistema de contabilidade tem uma estrutura e conteúdo (e dados) que devem modelar a operação da minha empresa. Essa estrutura é real e existe independentemente do software de contabilidade que eu uso. (Inevitavelmente, um determinado pacote de software contém estrutura e conteúdo por si só, mas parte do desafio é minimizar essa sobrecarga.)
Basicamente, uma pessoa tem um trabalho a fazer. O DDD deve corresponder ao fluxo e ao conteúdo do trabalho. DDD trata de tornar explícito todos os trabalhos que precisam ser executados e de forma completa e independente possível. Então, a IU provavelmente facilita a realização do trabalho da forma mais transparente possível, da forma mais produtiva possível.
As interfaces são sobre as entradas e visualizações fornecidas para o núcleo funcional invariante e modelado corretamente.
fonte
Droga, eu juro que disse persistência.
De qualquer forma, é mais uma instância da mesma coisa: a lei de Parnas diz que um módulo deve manter um segredo, e o segredo é um requisito que pode mudar. (Bob Martin tem uma regra que é outra versão disso.) Em um sistema como este, a apresentação pode mudar independentemente do domínio . Como, por exemplo, uma empresa que mantém preços em euros e usa o francês nos escritórios da empresa, mas quer apresentar preços em dólares com texto em mandarim. O domínio é o mesmo; a apresentação pode mudar. Portanto, para minimizar a fragilidade do sistema - ou seja, o número de coisas que devem ser alteradas para implementar uma mudança nos requisitos - você separa as preocupações.
fonte
Sua apresentação pode fazer referência à sua camada de domínio, mas não deve haver ligação direta de sua interface com o usuário para seus objetos de domínio. Os objetos de domínio não se destinam ao uso da interface do usuário, pois geralmente, se projetados de maneira adequada, são baseados em comportamentos e não em representações de dados. Deve haver uma camada de mapeamento entre a IU e o Domínio. MVVM, ou MVP, é um bom padrão para isso. Se você tentar vincular diretamente sua IU ao domínio, provavelmente criará muita dor de cabeça para si mesmo. Eles têm dois propósitos diferentes.
fonte
Talvez você não esteja conceituando a camada de IU em termos amplos o suficiente. Pense em termos de várias formas de resposta (páginas da web, resposta de voz, cartas impressas, etc.) e em termos de vários idiomas (inglês, francês etc.).
Agora, suponha que o mecanismo de fala do sistema de chamada telefônica seja executado em um tipo de computador completamente diferente (Mac, por exemplo) do computador que executa o site (Windows, talvez).
Claro que é fácil cair na armadilha "Bem, na minha empresa só nos preocupamos com inglês, rodamos nosso site em LAMP (Linux, Apache, MySQL e PHP) e todos usam a mesma versão do Firefox". Mas e daqui a 5 ou 10 anos?
fonte
Consulte também a seção "Propagação de dados entre camadas" a seguir, que acredito apresentar argumentos convincentes:
http://galaxy.andromda.org/docs/andromda-documentation/andromda-getting-started-java/java/index.html
fonte
Com a ajuda de ferramentas como o ' Value Injecter ' e o conceito de 'Mapeadores' na camada de apresentação ao trabalhar com visualizações, é muito mais fácil entender cada pedaço de código. Se você tiver um pouco de código, não verá as vantagens imediatamente, mas quando seu projeto crescer mais e mais, você ficará muito feliz trabalhando com as visualizações para não ter que entrar na lógica dos serviços, repositórios para entender o modelo de visão. View Model é outro guarda no vasto mundo da camada anticorrupção e vale seu peso em ouro em um projeto de longo prazo.
A única razão pela qual não vejo nenhuma vantagem em usar o modelo de visualização é se seu projeto é pequeno e simples o suficiente para ter visualizações vinculadas diretamente a cada propriedade do seu modelo. Mas se no futuro, o requisito mudar e alguns controles nas visualizações não forem vinculados ao modelo e você não tiver um conceito de modelo de visualização, você começará a adicionar patches em muitos lugares e começará a ter um código legado que você não vai apreciar. Claro, você pode fazer alguma refatoração para transformar seu modelo de visualização em modelo de visualização e seguir o princípio YAGNI enquanto não adiciona código se você não precisa dele, mas para mim, é muito mais uma prática recomendada que devo seguir para adicionar um camada de apresentação expondo apenas objetos de modelo de visualização.
fonte
Aqui está um exemplo real de por que considero uma boa prática separar entidades de domínio da visualização.
Há alguns meses, criei uma IU simples para mostrar os valores de nitrogênio, fósforo e potássio em uma amostra de solo por meio de uma série de 3 medidores. Cada medidor tinha uma seção vermelha, verde e vermelha, ou seja, você poderia ter muito pouco ou muito de cada componente, mas havia um nível verde seguro no meio.
Sem pensar muito, modelei minha lógica de negócios para fornecer dados para esses 3 componentes químicos e uma planilha de dados separada, contendo dados sobre os níveis aceitos em cada um dos 3 casos (incluindo qual unidade de medida estava sendo usada, ou seja, moles ou porcentagem). Eu então modelei minha IU para usar um modelo muito diferente, esse modelo se preocupava com rótulos de medidores, valores, valores de limite e cores.
Isso significa que, quando mais tarde tive que mostrar 12 componentes, simplesmente mapeei os dados extras em 12 novos modelos de visualização de medidor e eles apareceram na tela. Também significava que eu poderia reutilizar o controle de medidor facilmente e fazer com que exibissem outros conjuntos de dados.
Se eu tivesse acoplado esses medidores diretamente em minhas entidades de domínio, não teria nenhuma das flexibilidade acima e quaisquer modificações futuras seriam uma dor de cabeça. Encontrei problemas muito semelhantes ao modelar calendários na IU. Se houver um requisito para um compromisso do calendário ficar vermelho quando houver mais de 10 participantes, a lógica de negócios para lidar com isso deve permanecer na camada de negócios e todo o calendário na IU precisa saber, é que foi instruído a ficar vermelho, não deve ser necessário saber por quê.
fonte
A única razão sensata para adicionar mapeamento adicional entre semântica generalizada e semântica específica do domínio é que você tem (acesso a) um corpo de código (e ferramentas) existente que é baseado em uma semântica generalizada (mas mapeável) distinta da semântica do seu domínio.
Projetos orientados por domínio funcionam melhor quando usados em conjunto com um conjunto ortogonal de estruturas de domínio funcionais (como ORM, GUI, fluxo de trabalho, etc.). Lembre-se sempre de que é apenas nas adjacências da camada externa que a semântica de domínio precisa ser exposta. Normalmente, é o front-end (GUI) e o back-end persistente (RDBM, ORM). Todas as camadas intermediárias projetadas de maneira eficaz podem e devem ser invariantes no domínio.
fonte