Por que devo isolar minhas entidades de domínio de minha camada de apresentação?

85

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:

  1. 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)
  2. Devemos usar objetos ou construções adicionais para isolar nossos objetos de domínio da interface?
Mark Rogers
fonte
esta questão deve estar no wiki.
Syed Tayyab Ali
@ m4bwav - Deve ser um wiki porque está formulado de forma a convidar a discussão ao invés de uma única resposta correta.
Rob Allen
1
@ m4bwav: Acho que sua pergunta saiu mais como um artigo de opinião do que uma pergunta real ... Tentei corrigir isso (você pode querer editá-lo mais), mas esteja ciente de que, sem os devidos cuidados, isso pode parecer estar trollando.
Shog9
5
Ok, backup, estou fazendo uma pergunta legítima, como isso ofenderia alguém? Quem estou almejando?
Mark Rogers
@ m4bwav: você está mirando em seu espantalho. O "grande número de pessoas" com quem você discute isso em sua pergunta.
Shog9

Respostas:

48

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.

Paul Sonier
fonte
2
O que você quer dizer com "implemente seus objetos de domínio em dois locais?"
jlembke
10
Isso me parece bobo. Por que fazer o trabalho extra agora que PODE economizar trabalho no futuro? 9 em cada 10 vezes, você nunca precisará fazer a alteração que economizaria "TONELADAS" de trabalho.
Beep beep de
13
@LuckyLindy: 99 vezes em 100 (na verdade mais), usar meu cinto de segurança não é necessário para evitar que eu me machuque. No entanto, no único caso em que eu realmente preciso, isso (provavelmente) evitará que eu seja morto ou gravemente ferido. Um grama de prevenção vale um quilo de cura. Suspeito que sua opinião sobre isso mudará depois que você tiver mais experiência.
Paul Sonier
19

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.

JoshBerke
fonte
Os rótulos sendo diferentes para locatários diferentes não indicariam um idioma onipresente diferente para cada locatário? Acho que deve haver o conceito de um metamodelo em que um domínio é compartilhado entre os locatários com uma camada de tradução para sua interpretação do metamodelo.
Kell
16

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.

digitaljoel
fonte
13

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.

Daniel Alexiuc
fonte
Obrigado por sua contribuição, vejo de onde você vem. Embora não esteja dizendo que esta não seja outra das infinitas maneiras de criar um projeto de sucesso, parece ir contra o estilo "Domain-Driven Design", que é para projetos maiores e mais complexos que são mais difíceis de manter a longo prazo.
Mark Rogers
Não, isso está errado, e exatamente por que tantos sites acabam vulneráveis ​​à injeção de sql.
Remi
7

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.

insira a descrição da imagem aqui


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:

  • A camada de apresentação pode ver apenas o subconjunto de campos que a entidade possui. Você encapsula entidades
  • Sem acoplamento entre backend e frontent
  • Se você tem métodos de negócios dentro de entidades, mas não em DTOs, então adicionar DTOs significa que o código externo não pode arruinar o estado de sua entidade.

insira a descrição da imagem aqui


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.

insira a descrição da imagem aqui

Marcin Szymczak
fonte
4

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.

Stefan Steinegger
fonte
2
Se for usar ligação de dados, execute uma consulta linq e faça a vinculação a um tipo anônimo. Isso permite nivelar e alterar a hierarquia. Você também pode implementar filtragem e classificação muito bem com isso.
JoshBerke
@Josh: Obrigado pelo conselho. Isso pode funcionar parcialmente. Não sou um programador de GUI e não estou muito envolvido em conceitos de GUI. O problema estará nos casos em que os dados são manipulados e enviados de volta ao servidor.
Stefan Steinegger
3

É 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.

dkretz
fonte
3

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.

Charlie Martin
fonte
2

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.

Jlembke
fonte
1

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?

JonnyBoats
fonte
1

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.

Samuel
fonte
1

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ê.

Adrian Thompson Phillips
fonte
-1

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.

alphazero
fonte
parágrafo 1: não crie abstrações desnecessárias (por exemplo, componentes reutilizáveis), a menos que você realmente os compartilhe entre aplicativos distintos. parágrafo 2: Eu me pergunto como as GUIs genéricas funcionam em tantos domínios diferentes. Observação: Esta indústria está tão quebrada que não é mais engraçada ...
alphazero