The source content for blog.juliobiason.me
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
5.9 KiB

+++
title = "Microserviços: Artefato = Entrada + Estado"
date = 2019-12-26
[taxonomies]
tags = ["microserviços", "artefatos", "estado"]
+++
Projetar microserviços é um pouco complicado porque temos que pensar sobre
as coisas que cada "domínio" vai ocupar. Uma discussão entre os
desenvolvedores aqui sobre nossos projetos de microserviços me levou a
repensar como pensar microserviços.
<!-- more -->
Isso pode soar um pouco estranho para aqueles que já estão trabalhando com
microserviços -- ou que conseguiram ter uma boa visão da construção de
microserviços -- mas quando foi citado "artefato" na discussão, "caiu a ficha"
com outras coisas que eu estava pensando sobre o tópico.
Um fato que continua me confundindo é que a literatura sobre microserviços
começa a falar sobre "separação de domínios" e como definir cada domínio.
Embora haja alguns truques -- como "se é um substantivo, é um domínio" -- nada
é realmente tão óbvio. Alguns domínios são, na verdade, sub domínios de um
domínio maior, e aí você fica se perguntando se deve separar esses domínios ou
mantê-los num único microserviço, já que separá-los iria, invariavelmente,
criar microserviços acoplados (algo que você quer evitar quando está usando
microserviços).
E é aí que "artefato" encaixou no resto das coisas. Por algum tempo, eu tive a
impressão que microserviços tem que ser construídos "de trás pra frente", no
sentido de que primeiro você precisa pensar nas coisas que você _precisa_ e
depois verificar o que você _tem_ -- em outras palavras, você pensa primeiro
nas saídas do microserviço e depois olha o que tem de entrada. E um "artefato"
é, no final, simplesmente a saída do microserviço.
No nosso caso, nós estamos lidado com jogos. Cada jogo tem uma narração, tem
um placar, tem estatística e tem uma escalação. Mesmo que essa explicação
caia na regra do "é um substantivo!", na verdade ela reflete a saída do nosso
sistema: nos temos uma requisição que retorna a narração atual do jogo (que
pode ser atualizada por polling ou -- como estamos trabalhando agora -- feito
"push" diretamente para os clientes); uma requisição para retornar o placar
(que, de novo, pode ser por "polling" ou "push"); uma requisição que retorna
as estatísticas, que não são atualizadas ou exibidas de forma tão frequente,
e por isso não precisam de atualizações visuais constantes; e assim por
diante. Cada um desses é um microserviço diferente, porque cada um desses é um
artefato diferente.
Para clarificação: nossos artefatos são mantidos em um banco Firestore, que os
clientes fazem as requisições diretamente, mas que na maior parte do tempo vão
simplesmente receber as notificações de alteração dos dados. Mas outra forma
de manter esses dados é ter serviços separados, que responde às requisições
dos clientes -- que é bem próximo da forma que CQRS é descrito (bom, quer
dizer, seria CQRS se o microserviço recebesse comandos; eu não vou dizer que
são CQRS se o microserviço estão lidando com eventos diretamente).
Bom, se esses são os artefatos, onde é que o "estado" entra nessa história? O
estado é o conjunto de informações que o microserviço precisa ter para
produzir o artefato. Por exemplo, na narração, cada vez que uma nova narração
entra, ela precisa entrar na lista de narrações do jogo para que seja
produzida a narração da partida inteira. O estado também pode ajudar o
microserviço a remover narrações duplicadas.
Um efeito "legal" do estado é que você pode, pelo menos na teoria, perceber
que mesmo com uma nova entrada, se não houve alteração do estado, então não
vai haver alteração do artefato e não é preciso ter nenhuma saída.
Outra coisa a se ter em mente sobre o estado é que ele não precisa ser mantido
em memória; você pode usar qualquer tipo de armazenamento: mantenha as
narrações num banco de dados, no disco, na memória em cache ou todos os
anteriores. Decida usar o que ficar mais _fácil_ de ser manipulado para
produzir o artefato. Uma coisa a se manter em mente sobre isso é "Se esse
microserviço morrer, ele vai conseguir voltar ao mesmo estado quando for
reiniciado?"
E, finalmente, as entradas. Essas podem parecer meio óbvias a princípio (o seu
microserviço está gerando dados do nada?), mas mantenha em mente que uma
entrada pode ser a origem de dados de mais de um microserviço. Por exemplo,
uma narração pode ser consumida pelo microserviço de narrações para produzir a
narração inteira da partida, mas também ser consumida pelo microserviço de
placar, que fica escutando narrações de gols para atualizar seu estado (se a
narração não for de gol, não há alteração de placar, não há alteração de
estado e não há geração do artefato).
Voltando aos artefatos, não se preocupe se mais de um microserviço faz
basicamente a mesma coisa que outra, mas gera um artefato completamente
diferente. Como exemplo, imagine que você quer que sejam feitas notificações
por push quando acontece um gol. Embora seja um serviço bem parecido com o
microserviço de placar, ele produz um artefato diferente (a notificação por
push vs a requisição de atualização de placar) e, por isso, deveria ser um
microserviço completamente diferente. Até pode soar meio desnecessário (ter
serviços fazendo a mesma coisa, duas vezes), mas isso desacopla as coisas se
você precisar mais informações no placar (por exemplo, adicionando o nome de
cada jogador que fizeram gols) ou mudar o consumidor do artefato (por exemplo,
mudando a implementação do push para, ao invés de fazer as chamadas
diretamente paras a APIs da Apple e Google, fazer chamadas para um serviço que
já faça tudo isso, como o da Azure).
Essa mudança na minha forma de pensar como construir microserviços me ajudou a
pensar nos nossos microserviços no trabalho, e também está me ajudando a
repensar algumas saídas em um projeto pessoal (que eu espero terminar e
mostrar no ano que vêm).