Maneiras de compartilhar DTO entre microsserviços?

33

Meu cenário é o seguinte.

Estou projetando um sistema projetado para receber dados de vários tipos de sensores, converter e depois persistir para que ele seja usado por vários serviços de análise e front-end posteriormente.

Estou tentando projetar cada serviço para ser o mais independente possível, mas estou tendo alguns problemas. A equipe decidiu um DTO que gostaríamos de usar. Os serviços voltados para o exterior (destinatários de dados do sensor) receberão os dados de maneira exclusiva e depois os converterão em um objeto JSON (o DTO) e os enviarão para o Message Broker. Os consumidores das mensagens saberão exatamente como ler as mensagens de dados do sensor.

O problema é que estou usando o mesmo DTO em alguns serviços diferentes. Uma atualização deve ser implementada em vários locais. Obviamente, nós o projetamos de tal maneira que alguns campos extras ou ausentes no DTO aqui e não há muito problema até que os serviços sejam atualizados, mas ele ainda está me incomodando e me faz sentir como se estivesse Cometer um erro. Poderia facilmente se transformar em dor de cabeça.

Estou arquitetando o sistema errado? Se não, quais são algumas maneiras de contornar isso, ou pelo menos para aliviar minhas preocupações?

nbaughman
fonte
Que tipo de DTOs você está compartilhando e qual protocolo você está usando entre serviços? Não há problema em compartilhar, por exemplo, o protoarquivo para gRPC ou o avroesquema para Kafka e gerar os DTOs nos dois serviços, mas eu não compartilharia uma biblioteca compartilhada entre dois projetos.
Vincent Savard
Strings JSON codificadas e AMQP. Eu preferiria não usar nada específico do idioma.
Nbaughman

Respostas:

38

Meu conselho? Não compartilhe esses DTOs entre os aplicativos em qualquer tipo de biblioteca. Ou pelo menos não faça isso agora.

Eu sei, parece muito contra-intuitivo. Você está duplicando código, certo? Mas essa não é uma regra comercial, portanto você pode ser mais flexível.

O serviço que envia o DTO precisa ser rígido em seu contrato de mensagens, como uma API Rest. O serviço não pode alterar o DTO de maneira que possa interromper os outros serviços que já estão consumindo as informações do DTO.

Quando um novo campo é adicionado ao DTO, você atualiza apenas os outros serviços que consomem esse DTO se eles precisarem do novo campo. Caso contrário, esqueça. Usando JSON como tipo de conteúdo, você tem a flexibilidade de criar e enviar novos atributos sem interromper o código dos serviços que não mapeiam esses novos campos em suas versões reais do DTO.

Mas se essa situação realmente o estiver incomodando, você pode seguir a Regra dos Três :

Existem duas "Regras de Três" na reutilização: (a) É três vezes mais difícil construir componentes reutilizáveis ​​que componentes de uso único e (b) um componente reutilizável deve ser testado em três aplicações diferentes antes de ser suficientemente geral para aceitar em uma biblioteca de reutilização.

Portanto, tente esperar um pouco mais antes de compartilhar este DTO entre os serviços.

Dherik
fonte
1
Muito apreciado. Essa é realmente uma das poucas preocupações principais que tenho adiante. Não é o suficiente para me manter acordada à noite, mas o suficiente para me preocupar.
Nbaughman
4
DTOs duplicados (em serviços diferentes e muito independentes) não violam o DRY. É isso aí.
21418 Laiv
3
Presumivelmente, não há razão para não copiar o código-fonte do DTO diretamente de um projeto para outro, como uma operação pontual, embora, provavelmente, todas as partes não necessárias no novo projeto devam ser excluídas.
bdsl
1
Mesmo um serviço completo pode ser excluído, sem causar grandes problemas ao sistema inteiro. Idealmente.
Laiv
4
Para esclarecer a essência da resposta, ao desenvolver microsserviços, cada serviço deve ser desenvolvido como se não conhecesse os outros serviços, exceto os contratos reais que possam exigir.
Jonathan van de Veen
12

Quando se trata de microsserviços, os ciclos de vida de desenvolvimento dos serviços também devem ser independentes. *

SLDC diferentes e diferentes equipes de desenvolvimento

em um sistema real de EM, pode haver várias equipes envolvidas no desenvolvimento do ecossistema, cada uma delas encarregada de um ou mais serviços. Por sua vez, essas equipes podem estar localizadas em escritórios, cidades, países, planos ... Talvez eles nem se conheçam, o que dificulta o compartilhamento de conhecimento ou código (se possível). Mas isso pode ser muito conveniente, porque o código compartilhado também implica um tipo de raciocínio de compartilhamento e algo importante a lembrar é que, o que faz sentido para uma equipe específica, não precisa ser para outra equipe. Por exemplo, dado o Cliente do DTO , ele pode ser diferente dependendo do serviço em execução, porque os clientes são interpretados (ou vistos) de maneira diferente de cada serviço.

Necessidades diferentes, tecnologias diferentes

SLDCs isolados também permitem que as equipes escolham a pilha que melhor se adapta às suas necessidades. A imposição de DTOs implementados em uma tecnologia específica limita a capacidade das equipes de escolher.

DTOs não são regras de negócios nem contratos de serviços

O que os DTOs são realmente? Objetos simples com nenhum outro objetivo além de mover dados de um lado para outro. Sacos de caçadores e caçadores. Não é o tipo de "conhecimento" que vale a pena reutilizar, em geral porque não há conhecimento. Sua volatilidade também os torna maus candidatos ao acoplamento.

Ao contrário do que a Dherik declarou, deve ser possível que um serviço altere seus DTOs sem precisar fazer outros serviços para alterar ao mesmo tempo. Os serviços devem ser leitores tolerantes, escritores tolerantes e tolerantes a falhas . Caso contrário, eles causam acoplamentos de forma que a arquitetura de serviço não faz sentido. Mais uma vez, e ao contrário da resposta de Dherik, se três serviços precisarem exatamente dos mesmos DTOs, é provável que algo tenha dado errado durante a decomposição dos serviços.

Negócios diferentes, interpretações diferentes

Embora possa haver (e haverá) conceitos transversais entre os serviços, isso não significa que devemos impor um modelo canônico para forçar todos os serviços a interpretá-los da mesma maneira.

Estudo de caso

Digamos que nossa empresa tenha três departamentos, Atendimento ao Cliente , Vendas e Remessa . Diga a cada um desses lançamentos um ou mais serviços.

O Atendimento ao Cliente, devido ao seu idioma de domínio , implementa serviços em torno do conceito de clientes, onde os clientes são pessoas . Por exemplo, os clientes são modelados como nome , sobrenome , idade , sexo , email , telefone etc.

Agora, digamos, Vendas e Remessa modelam seus serviços de acordo com os respectivos idiomas de domínio. Nesses idiomas, o conceito de cliente também aparece, mas com uma diferença sutil. Para eles, os clientes não são (necessariamente) pessoas . Para vendas , os clientes são um número de documento de um cartão de crédito e um endereço de cobrança , para transporte de um nome completo e endereço de entrega também.

Se forçarmos o Sales and Shipping a adotar o modelo de dados canônicos do Atendimento ao Cliente , os forçaremos a lidar com dados desnecessários que podem acabar introduzindo complexidade desnecessária se eles tiverem que manter toda a representação e manter os dados do cliente sincronizados com o atendimento ao cliente .

Links Relacionados


* Aqui é onde estão os pontos fortes dessa arquitetura

Laiv
fonte
Obrigado! Os estudos de caso são, na verdade, o que me ajudou a determinar se os DTOs devem ser compartilhados ou não. Agora tenho certeza do motivo pelo qual não queria compartilhá-los.
Igor
8

Estou tentando projetar todos os serviços para serem o mais independentes possível

Você deve publicar eventos . Eventos são certos tipos de mensagens que representam um fato sólido sobre algo que aconteceu em um determinado momento.

Cada serviço deve ter uma responsabilidade muito bem definida e deve ter a responsabilidade de publicar os eventos relacionados a essa responsabilidade.

Além disso, você deseja que seus eventos representem eventos relacionados a negócios, não eventos técnicos. Por exemplo, prefira OrderCancelledevento a um OrderUpdatedcomstatus: "CANCELLED" .

Dessa forma, quando um serviço precisa reagir a um pedido cancelado, ele precisa apenas ouvir esse tipo específico de mensagem, que carrega apenas dados relevantes para esse evento. Por exemplo, um OrderCancelledprovavelmente só precisa de um order_id. Qualquer serviço que precise reagir a isso já armazenou o que precisa saber sobre o pedido em seu próprio armazenamento de dados.

Mas se o serviço tivesse apenas OrderUpdatedeventos para ouvir, seria necessário interpretar o fluxo de eventos e agora dependia da ordem de entrega concluir corretamente quando um pedido era cancelado.

No seu caso, no entanto, como você está publicando dados do sensor, pode fazer sentido ter um serviço, ouvir os eventos e publicar um novo fluxo de "eventos de negócios", por exemplo TemperatureThresholdExceeded,TemperatureStabilised .

E tenha cuidado ao criar muitos microsserviços. Os microsserviços podem ser uma ótima maneira de encapsular a complexidade, mas se você não descobrir limites de serviço adequados, sua complexidade estará na integração de serviços. E isso é um pesadelo para manter.

É melhor ter poucos serviços grandes demais do que ter muitos serviços pequenos demais.

Pete
fonte
Eu definitivamente concordo. Os dados do sensor entram diretamente em um microsserviço que analisa a mensagem e a transforma em um formato acordado em toda a organização antes de publicá-la no broker. Teremos alguns serviços que lêem a mensagem e a mantêm em um banco de dados, e outros que executam análise da mensagem e fazem suas próprias coisas com ela. Cada serviço, por natureza da empresa, não tem muito o que fazer, levando a serviços (espero) bastante simples.
Nbaughman
2
@ Nickdb93 - No seu caso, como você está publicando dados do sensor, pode fazer sentido ter um serviço, ouvir os eventos e publicar um novo fluxo de "eventos de negócios", por exemplo, TemperatureThresholdExceeded, TemperatureStabilised. (adicionado a resposta)
Pete