O design orientado a domínio é um padrão anti-SQL?


44

Estou mergulhando no DDD (Domain driven design) e, enquanto me aprofundo nele, há algumas coisas que não entendo. Pelo que entendi, um ponto principal é dividir a lógica de domínio (lógica de negócios) da infraestrutura (banco de dados, sistema de arquivos etc.).

O que me pergunto é: o que acontece quando tenho consultas muito complexas, como uma Consulta de cálculo de recursos materiais? Nesse tipo de consulta, você trabalha com operações de conjunto pesado, o tipo de coisa para a qual o SQL foi projetado. Fazer esses cálculos dentro da Camada de Domínio e trabalhar com muitos conjuntos é como jogar fora a tecnologia SQL.

Fazer esses cálculos na infraestrutura também não pode acontecer, porque o padrão DDD permite alterações na infraestrutura sem alterar a Camada de Domínio e sabendo que o MongoDB não possui os mesmos recursos, por exemplo, SQL Server, isso não pode acontecer.

Isso é uma armadilha do padrão DDD?


34
Embora o SQL seja projetado para lidar com álgebra de conjuntos relacionais, não é um dia divertido quando você percebe que metade da lógica de negócios está oculta em algumas funções do SQL difíceis de refatorar e ainda mais difíceis de testar. Então, mover isso para a camada de domínio em que ele pode brincar com seus amigos parece atraente para mim. Isso está descartando uma boa parte da tecnologia SQL? Claro, mas o SQL é muito mais fácil de gerenciar quando você está usando apenas SELECT / JOIN.
Jared Goguen 08/04

30
@JaredGoguen, mas isso pode ser porque você não é um especialista em SQL e não por causa da tecnologia
Leonardo Mangano

2
@ JimmyJames, o que tentei dizer é que, se o DDD for bem implementado, ele permitirá alterar as camadas com o mínimo de esforço, como alternar do SQL Server para o MongoDB. Mas, se houver consultas complexas no SQL, é possível que não seja possível mudar para o MongoDB devido às diferenças técnicas. Eu acho que disse uma coisa óbvia.
Leonardo Mangano

7
... is like throwing away the SQL technologySó porque uma determinada tecnologia pode fazer algo não significa que é a melhor escolha. É uma evidência anedótica, mas conheci muitas empresas que costumavam armazenar lógica de negócios no banco de dados e estão migrando para longe dele devido às dores de cabeça de manutenção de longo prazo que causam. Simplificando, mas os bancos de dados são destinados ao armazenamento de dados e as linguagens de programação são destinadas à transformação de dados. Eu não gostaria de usar um banco de dados para a lógica de negócios mais do que gostaria de tentar usar meu aplicativo para armazenar meus dados diretamente.
Conor Mancone

8
O próprio SQL é um ótimo exemplo de DDD. Quando confrontados com a organização de dados relacionados, as pessoas primeiro especificaram um idioma para fazê-lo: SQL. Implementação realmente não importa muito. Um administrador de banco de dados não precisa conhecer C / C ++ para consultar o banco de dados. Da mesma forma, quando se depara com a tarefa de agendar eventos, alguém cria a sintaxe CRON (mhdmw), um modelo de domínio simples que se encaixa em 99% dos problemas de agendamento. O principal do DDD não é criar classes ou tabelas, etc. É entender o seu problema e criar um sistema para trabalhar no domínio do seu problema
slebetman

Respostas:


39

Atualmente, é provável que você veja leituras (consultas) manipuladas de maneira diferente das gravações (comandos). Em um sistema com uma consulta complicada, é improvável que a própria consulta passe pelo modelo de domínio (o principal responsável por manter a consistência das gravações ).

Você está absolutamente certo de que devemos renderizar ao SQL o que é SQL. Portanto, projetaremos um modelo de dados otimizado em torno das leituras, e uma consulta desse modelo de dados geralmente adotará um caminho de código que não inclui o modelo de domínio (com a possível exceção de alguma validação de entrada - garantindo que os parâmetros na consulta são razoáveis).


13
+1 Boa resposta, mas você deve dar a esse conceito seu nome próprio, Segregação de Consulta de Comando.
Mike apoia Monica em

6
@ Mike Ter modelos completamente diferentes de leitura e escrita é mais parecido com o CQRS do que com o CQS.
Andy

3
O "modelo de leitura" não é o modelo de domínio (ou parte dele)? Não sou especialista em CQRS, mas sempre achei que o modelo de comando é bem diferente do modelo de domínio clássico, mas não o modelo de leitura. Então, talvez você possa dar um exemplo disso?
Doc Brown

Demorei muito para perceber que o High Performance Mark estava chamando a atenção para um erro de digitação.
VoiceOfUnreason

@DocBrown - aqui está minha tentativa de esclarecer para você -> cascadefaliure.vocumsineratio.com/2019/04/…
VoiceOfUnreason

21

Pelo que entendi, um ponto principal é dividir a lógica de domínio (lógica de negócios) da infraestrutura (banco de dados, sistema de arquivos etc.).

Esta é a base do mal-entendido: o objetivo do DDD não é separar as coisas ao longo de uma linha rígida como "isso está no servidor SQL, portanto não deve ser BL", o objetivo do DDD é separar domínios e criar barreiras entre eles que permitem que os internos de um domínio sejam completamente separados dos internos de outro domínio e definam externos compartilhados entre eles.

Não pense em "estar no SQL" como a barreira BL / DL - não é isso que é. Em vez disso, pense em "este é o fim do domínio interno" como a barreira.

Cada domínio deve ter APIs externas que permitam trabalhar com todos os outros domínios: no caso da camada de armazenamento de dados , ele deve ter ações de leitura / gravação (CRUD) para os objetos de dados que armazena. Isso significa que o próprio SQL não é realmente a barreira, os componentes VIEWe PROCEDUREsão. Você nunca deve ler diretamente da tabela: esse é o detalhe da implementação que o DDD nos diz que, como consumidor externo, não devemos nos preocupar.

Considere o seu exemplo:

O que me pergunto é: o que acontece quando tenho consultas muito complexas, como uma Consulta de cálculo de recursos materiais? Nesse tipo de consulta, você trabalha com operações de conjunto pesado, o tipo de coisa para a qual o SQL foi projetado.

É exatamente o que deveria estar no SQL e não é uma violação do DDD. É para isso que criamos o DDD . Com esse cálculo no SQL, isso se torna parte do BL / DL. O que você faria é usar uma exibição separada / procedimento armazenado / o que você tem e manter a lógica de negócios separada da camada de dados, pois essa é sua API externa. De fato, sua camada de dados deve ser outra Camada de Domínio DDD, onde sua camada de dados possui suas próprias abstrações para trabalhar com as outras camadas de domínio.

Fazer esses cálculos na infraestrutura também não pode acontecer, porque o padrão DDD permite alterações na infraestrutura sem alterar a Camada de Domínio e sabendo que o MongoDB não possui os mesmos recursos, por exemplo, SQL Server, isso não pode acontecer.

Esse é outro mal-entendido: ele diz que os detalhes da implementação podem mudar internamente sem alterar outras camadas do domínio. Não diz que você pode substituir apenas uma peça de infraestrutura inteira.

Novamente, lembre-se de que o DDD trata de ocultar internals com APIs externas bem definidas. A localização dessas APIs é uma questão totalmente diferente e o DDD não define isso. Ele simplesmente define que essas APIs existem e nunca devem mudar .

O DDD não está configurado para permitir a substituição ad-hoc do MSSQL pelo MongoDB - esses são dois componentes de infraestrutura totalmente diferentes.

Em vez disso, vamos usar uma analogia para o que o DDD define: carros a gás versus carros elétricos. Ambos os veículos têm dois métodos completamente diferentes para criar propulsão, mas eles têm as mesmas APIs: um on / off, um acelerador / freio e rodas para impulsionar o veículo. DDD diz que devemos ser capazes de substituir o motor (a gás ou elétrico) em nosso carro. Não diz que podemos substituir o carro por uma motocicleta, e isso é efetivamente o que é MSSQL → MongoDB.


11
Obrigada pelo esclarecimento. Para mim é um tópico muito difícil, todos têm um ponto de vista diferente. A única coisa que não concordo é a comparação entre MSSQL (carro) e MongoDB (motocicleta), para mim a comparação certa é que esses são dois motores diferentes para o mesmo carro, mas é apenas uma opinião.
Leonardo Mangano

8
@LeonardoMangano Ah, mas não são. MSSQL é um banco de dados relacional, MongoDB é um banco de dados de documentos. Sim, "banco de dados" descreve os dois, mas é o mais longe possível. As técnicas de leitura / gravação são completamente diferentes. Em vez do MongoDB, você poderia usar o Postgre ou o MySQL como uma alternativa, e isso seria uma comparação válida.
410_Gone 8/04

3
"Você nunca deve ler diretamente da mesa ..." Loucura.
jpmc26 9/04

"Você nunca deve ler diretamente da tabela ..." Essa é uma regra que vim implementar sozinha depois de uma década escrevendo software que faz interface com bancos de dados e sofre com a dor inicial de tentar seguir os tutoriais estruturados em torno de padrões de design populares.
Lucifer Sam

@LuciferSam Aye. Isso facilita muito o gerenciamento da separação entre os detalhes da implementação e os limites do domínio. Um "objeto" no domínio pode ser representado por 5 tabelas; portanto, usamos uma Visualização para encapsular esse objeto.
410_Gone

18

Se você já esteve em um projeto em que a organização que paga para hospedar o aplicativo decide que as licenças da camada de banco de dados são muito caras, você apreciará a facilidade com a qual pode migrar seu banco de dados / armazenamento de dados. Tudo considerado, embora isso aconteça, não acontece com frequência .

Você pode obter o melhor dos dois mundos, por assim dizer. Se você considerar a execução de funções complexas no banco de dados como uma otimização, poderá usar uma interface para injetar uma implementação alternativa do cálculo. O problema é que você precisa manter a lógica em vários locais.

Desvio de um padrão arquitetural

Quando você se encontra em desacordo com a implementação de um padrão puramente ou se desvia em alguma área, você tem que tomar uma decisão. Um padrão é simplesmente uma maneira de fazer coisas para ajudar a organizar seu projeto. Nesse momento, reserve um tempo para avaliar:

  • Esse é o padrão certo? (muitas vezes é, mas às vezes é apenas um mau ajuste)
  • Devo desviar dessa maneira?
  • Até onde eu me desviei até agora?

Você verá que alguns padrões de arquitetura são adequados para 80-90% do seu aplicativo, mas não tanto para os bits restantes. O desvio ocasional do padrão prescrito é útil por razões de desempenho ou logísticas.

No entanto, se você achar que seus desvios cumulativos representam muito mais de 20% da arquitetura de seu aplicativo, provavelmente é apenas um ajuste inadequado.

Se você optar por continuar com a arquitetura, faça um favor e documente onde e por que você se desviou da maneira prescrita de fazer as coisas. Quando você obtém um novo membro entusiasmado da sua equipe, pode apontá-los para a documentação que inclui as medições de desempenho e justificativas. Isso reduzirá a probabilidade de solicitações repetidas para corrigir o "problema". Essa documentação também ajudará a desincentivar desvios desenfreados.


Eu evitaria o uso de frases como "esse é o padrão certo" nas respostas. Já é difícil o suficiente para que as pessoas sejam específicas quando escrevem suas perguntas e, por sua própria admissão "às vezes é um mau ajuste", o que sugere que não, não é o padrão certo.
Robert Harvey

@RobertHarvey, participei de projetos em que o padrão usado não era adequado para o aplicativo, o que causou falhas em determinadas métricas de qualidade. Certamente não é a norma, mas quando isso acontece, você tem a difícil decisão de mudar as arquiteturas ou manter o código da calçada no aplicativo. Quanto mais cedo você determinar o ajuste inadequado, mais fácil será consertar. É por isso que sempre incluo esse pensamento ao avaliar casos extremos. Junto com a última bala, às vezes você não percebe o quão ruim é o ajuste até ver o acúmulo de desvios.
Berin Loritsch

7

A lógica de manipulação de conjuntos em que o SQL é bom pode ser integrada ao DDD sem problemas.

Digamos, por exemplo, que eu precise conhecer algum valor agregado, contagem total de produtos por tipo. Fácil de executar em sql, mas lento se eu carregar todos os produtos na memória e adicionar todos eles.

Eu simplesmente apresento um novo objeto Domínio,

ProductInventory
{
    ProductType
    TotalCount
    DateTimeTaken
}

e um método no meu repositório

ProductRepository
{
    List<ProductInventory> TakeInventory(DateTime asOfDate) {...}
}

Claro, talvez agora eu esteja contando com meu DB tendo certas habilidades. Mas tecnicamente ainda tenho a separação e, desde que a lógica seja simples, posso argumentar que não é 'lógica de negócios'


Bem, até agora eu me lembro. Repositórios também devem receber Querycomo parâmetros. repository.find(query);. Eu li o mesmo, mas com o Specs. That opens a door to leave Query` como uma abstração e / QueryImplou a implementação de consulta específica na camada de infraestrutura.
Laiv 08/04

5
Oh Deus, eu sei que algumas pessoas fazem isso, mas eu acho horrível. Você pode ver esse tipo de coisa como um passo adiante nessa estrada. Mas acho que pode ser tomado com cautela.
Ewan

I know some people do thatalgumas pessoas são fundamentais e sua estrutura. SpringFramework tem muito disso :-). De qualquer forma, como o @VoiceOfUnreason sugeriu, a chave do DDD é manter a consistência dos escritos. Não tenho certeza sobre forçar o design com modelos de domínio cuja única finalidade é consultar ou parametrizar consultas. Isso pode ser abordado fora do domínio com estruturas de dados (pocos, pojos, dtos, mapeadores de linhas, o que for).
Laiv 08/04

obviamente, precisamos de algum tipo de inquisição para ajudar essas pessoas a voltar à sanidade. Mas estou usando minhas armas. A exposição parcial do datalayer é aceitável quando objetiva para uma melhor aplicação, onde o que é ou não um "Objeto de Domínio" é subjetivo
Ewan

11
O @LeonardoMangano depende da sua aplicação e implementação. O principal a perceber é que você pode reinterpretar seu domínio para torná-lo viável.
Ewan

3

Uma das maneiras possíveis de resolver esse dilema é pensar no SQL como uma linguagem assembly: você raramente codifica diretamente nele, mas onde o desempenho importa, é necessário entender o código produzido pelo seu C / C ++ / Golang / Rust e talvez até escreva um pequeno trecho no assembly, se você não puder alterar o código na linguagem de alto nível para produzir o código de máquina desejado.

Da mesma forma, no domínio de bancos de dados e SQL, várias bibliotecas SQL (algumas das quais são ORM ), por exemplo, SQLAlchemy e Django ORM para Python, LINQ for .NET, fornecem abstrações de nível mais alto, mas usam código SQL gerado sempre que possível para obter desempenho. Eles também fornecem alguma portabilidade em relação ao banco de dados usado, possivelmente com desempenho diferente, por exemplo, no Postgres e no MySQL, devido a algumas operações que utilizam um SQL específico ao banco de dados mais ideal.

E, assim como nas linguagens de alto nível, é fundamental entender como o SQL funciona, mesmo que seja apenas para reorganizar as consultas feitas com as bibliotecas SQL mencionadas acima, para conseguir a eficiência desejada.

PS: Prefiro fazer um comentário, mas não tenho reputação suficiente para isso.


2

Como sempre, essa é uma daquelas coisas que depende de vários fatores. É verdade que há muito que você pode fazer com o SQL. Também há desafios em usá-lo e algumas limitações práticas dos bancos de dados relacionais.

Como observa Jared Goguen nos comentários, o SQL pode ser muito difícil de testar e verificar. Os principais fatores que levam a isso são que ele não pode (em geral) ser decomposto em componentes. Na prática, uma consulta complexa deve ser considerada in toto. Outro fator complicador é que o comportamento e a correção do SQL dependem muito da estrutura e do conteúdo dos seus dados. Isso significa que testar todos os cenários possíveis (ou mesmo determinar o que são) geralmente é inviável ou impossível. A refatoração do SQL e a modificação da estrutura do banco de dados também são problemáticas.

O outro grande fator que levou ao afastamento do SQL são os bancos de dados relacionais que tendem a escalar apenas verticalmente. Por exemplo, quando você cria cálculos complexos no SQL para executar no SQL Server, eles serão executados no banco de dados. Isso significa que todo esse trabalho está usando recursos no banco de dados. Quanto mais você faz no SQL, mais recursos o banco de dados precisará, tanto em termos de memória quanto de CPU. Geralmente, é menos eficiente fazer essas coisas em outros sistemas, mas não há limite prático para o número de máquinas adicionais que você pode adicionar a essa solução. Essa abordagem é mais barata e mais tolerante a falhas do que construir um servidor de banco de dados monstro.

Esses problemas podem ou não se aplicar ao problema em questão. Se você conseguir resolver seu problema com os recursos disponíveis do banco de dados, talvez o SQL seja adequado para o espaço do problema. Você precisa considerar o crescimento, no entanto. Pode ser bom hoje, mas daqui a alguns anos, o custo da adição de recursos adicionais pode se tornar um problema.


Não é a alternativa a um banco de dados de monstros, simplesmente um número monstruoso e diversidade de sistemas auxiliares? Que resiliência os sistemas auxiliares têm, se todos eles ficam suspensos no sistema principal? E se a justificativa for simplesmente a limitação tecnológica do sistema principal, isso geralmente será uma otimização prematura para a maioria dos sistemas de negócios. Em geral, o SQL pode ser gravado de maneira dissociada, se necessário.
Steve

@ Steve Eu acho que onde você errou aqui está assumindo que deve haver um sistema único e central do qual os outros "ficam".
JimmyJames

@Steve Para dar um exemplo, você pode substituir um banco de dados inteiro do sistema por um único banco de dados não-SQL (não estou dizendo que essa sempre é a escolha certa, apenas que pode ser feita.) Esse banco de dados pode ser armazenado em vários sistemas, mesmo regiões geográficas. Esse banco de dados não é auxiliar, é uma substituição por atacado do banco de dados SQL.
JimmyJames

@JimmyJames, concordou, mas quando não existe um sistema principal, isso pode criar seus próprios problemas ao analisar dependências e manter a consistência dos dados. Essa é a razão dos monólitos em primeiro lugar - eles criam um certo tipo de simplicidade e, portanto, certos tipos de análise e eficiência de manutenção. Soluções não monolíticas apenas trocam alguns problemas ou custos por outros.
Steve

@jmoreno Jogar recursos em algo para mancar junto com ele não é o que eu chamaria de boa engenharia: "para lidar com o enorme volume de dados do site e executar 9.000 instâncias de memcached para acompanhar o número de transações o banco de dados deve servir ". Você considera o custo de seus projetos ou acha que alguém gastará dinheiro para tornar suas preferências pessoais viáveis?
JimmyJames

2

Isso é uma armadilha do padrão DDD?

Deixe-me primeiro esclarecer alguns equívocos.

DDD não é um padrão. E realmente não prescreve padrões.

O prefácio do livro DDD de Eric Evan afirma:

Os principais designers de software reconheceram a modelagem e o design de domínio como tópicos críticos por pelo menos 20 anos, mas surpreendentemente pouco foi escrito sobre o que precisa ser feito ou como fazê-lo. Embora nunca tenha sido formulada com clareza, uma filosofia surgiu como uma corrente oculta na comunidade de objetos, uma filosofia que chamo de design orientado a domínio.

[...]

Um recurso comum aos sucessos era um modelo de domínio rico que evoluiu através de iterações de design e se tornou parte da estrutura do projeto.

Este livro fornece uma estrutura para a tomada de decisões de design e um vocabulário técnico para discutir o design de domínio. É uma síntese das melhores práticas amplamente aceitas, juntamente com minhas próprias idéias e experiências.

Portanto, é uma maneira de abordar o desenvolvimento de software e a modelagem de domínio, além de um vocabulário técnico que suporta essas atividades (um vocabulário que inclui vários conceitos e padrões). Também não é algo completamente novo.

Outra coisa a ter em mente é que um modelo de domínio não é a implementação de OO que pode ser encontrada em seu sistema - essa é apenas uma maneira de expressá-lo ou de expressar parte dele. Um modelo de domínio é a maneira como você pensa sobre o problema que está tentando resolver com o software. É como você entende e percebe as coisas, como você fala sobre elas. É conceitual . Mas não em um sentido vago. É profundo e refinado, e é o resultado de muito trabalho e coleta de conhecimento. É ainda mais refinado e provavelmente evoluiu ao longo do tempo, e envolve considerações de implementação (algumas das quais podem restringir o modelo). Deve ser compartilhado por todos os membros da equipe (e especialistas em domínio envolvidos) e deve orientar como você implementa o sistema, para que o sistema o reflita de perto.

Nada disso é inerentemente pró ou anti-SQL, embora os desenvolvedores de OO talvez sejam geralmente melhores em expressar o modelo nas linguagens de OO, e a expressão de muitos conceitos de domínio seja melhor suportada pelo OOP. Mas algumas vezes partes do modelo devem ser expressas em um paradigma diferente.

O que me pergunto é: o que acontece quando tenho consultas muito complexas [...]?

Bem, de um modo geral, existem dois cenários aqui.

No primeiro caso, algum aspecto de um domínio realmente exige uma consulta complexa, e talvez esse aspecto seja melhor expresso no paradigma SQL / relacional - portanto, use a ferramenta apropriada para o trabalho. Reflita esses aspectos em seu domínio e a linguagem usada na comunicação de conceitos. Se o domínio for complexo, talvez isso faça parte de um subdomínio com seu próprio contexto limitado.

O outro cenário é que a necessidade percebida de expressar algo no SQL é resultado de um pensamento restrito. Se uma pessoa ou equipe sempre foi orientada para o banco de dados em seus pensamentos, pode ser difícil para eles, devido à inércia, ver uma maneira diferente de abordar as coisas. Isso se torna um problema quando a maneira antiga falha em atender às novas necessidades e requer algum pensamento imediato. O DDD, como uma abordagem do design, é em parte sobre maneiras de encontrar o caminho para sair dessa caixa, reunindo e destilando o conhecimento sobre o domínio. Mas todo mundo parece ignorar essa parte do livro e se concentra em alguns dos vocabulários e padrões técnicos listados.


0

O Sequel se tornou popular quando a memória era cara, porque o modelo de dados relacionais oferecia a possibilidade de normalizar seus dados e armazená-los efetivamente no sistema de arquivos.

Agora a memória é relativamente barata, para que possamos pular a normalização e armazenar no formato em que a usamos ou até duplicar muitos dos mesmos dados por questão de velocidade.

Considere o banco de dados como um dispositivo simples de E / S , cuja responsabilidade é armazenar dados no sistema de arquivos - sim, eu sei que é difícil imaginá-lo, porque escrevemos muitos aplicativos com lógica de negócios importante gravada em consultas SQL - mas tente imaginar que o SQL Server é apenas mais uma impressora.

Você incorporou o gerador de PDF ao driver da impressora ou adicionou um gatilho que imprimirá a página de log de cada pedido de venda impresso em nossa impressora?

Suponho que a resposta será não, porque não queremos que nosso aplicativo seja acoplado ao tipo de dispositivo específico (nem mesmo falando sobre a eficiência dessa ideia)

Nos anos 70-90, o banco de dados SQL era eficiente, agora? - Não tenho certeza, em alguns cenários, a consulta de dados assíncrona retorna os dados necessários mais rapidamente do que várias junções na consulta SQL.

O SQL não foi projetado para consultas complicadas, foi projetado para armazenar dados de maneira eficiente e, em seguida, fornecer interface / linguagem para consultar dados armazenados.

Eu diria que construir seu aplicativo em torno do modelo de dados relacionais com consultas complicadas é abuso do mecanismo de banco de dados. É claro que os provedores de mecanismos de banco de dados ficam felizes quando você acopla firmemente sua empresa ao produto deles - eles terão todo o prazer em fornecer mais recursos que tornam isso mais forte.


11
Mas continuo pensando que o SQL é muito melhor para cálculos definidos do que qualquer outra linguagem. Do meu ponto de vista. seu exemplo é invertido, usar C # para operações de conjunto muito complexas com milhões de linhas e junções envolvidas está usando a ferramenta errada, mas eu posso estar errado.
Leonardo Mangano

@ LeonardoMangano, alguns exemplos: com o c #, posso dividir milhões de linhas e calculá-lo em paralelo, posso recuperar dados de forma assíncrona e executar cálculos "no tempo" quando os dados são retornados, com o c # posso fazer cálculos com pouco uso de memória, enumerando a linha por linha. Ter uma lógica complexa no código fornecerá muitas opções de como fazer cálculos.
Fabio
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.