É um padrão de projeto de software, ou padrão de arquitetura de software de baixo acoplamento, onde as funcionalidades implementadas nas aplicações devem ser disponibilizadas na forma de serviços, acessíveis normalmente via web services, é baseada nos princípios da computação distribuída, que utiliza o paradigma request/reply para estabelecer a comunicação entre os sistemas clientes e os sistemas dos serviços.Wikipedia
A variant of the SOA structural style – arranges an application as a collection of loosely-coupled services. In a microservices architecture, services are fine-grained and the protocols are lightweight.Wikipedia
O HTTP (Hypertext Transfer Protocol) é um protocolo que especifica como será a comunicação entre um navegador e um servidor web, sendo um dos principais da World Wide WebCanaltech
Representational State Transfer é um estilo de arquitetura de software que define um conjunto de restrições a serem usadas para a criação de serviços WebWikipédia
Os Web services que estão em conformidade com o estilo arquitetural REST, denominados RESTful, fornecem interoperabilidade entre sistemas de computadores na InternetWikipédia
Os Web services RESTful permitem que os sistemas solicitantes acessem e manipulem representações textuais de recursos da Web usando um conjunto uniforme e predefinido de operações sem estadoWikipédia
Linguagem de consulta e ambiente de execução voltada a servidores para as interfaces de programação de aplicações (APIs) cuja prioridade é fornecer exatamente os dados que os clientes solicitam e nada alémRedHat
Desenvolvido para tornar as APIs mais rápidas, flexíveis e intuitivas para os desenvolvedores, sendo possível implantá-lo em uma IDE conhecido como GraphiQLRedHat
Como alternativa à arquitetura REST, o GraphQL permite aos desenvolvedores construir solicitações que extraem os dados de várias fontes em uma única chamada de APIRedHat
Além disso, o GraphQL proporciona aos profissionais responsáveis pela manutenção das APIs flexibilidade para adicionar ou preterir campos, sem afetar as consultas existentesRedHat
Os desenvolvedores podem criar APIs com o método que quiserem, pois a especificação do GraphQL assegura que elas funcionem de maneira previsível para os clientesRedHat
Interprocess communication é o mecanismo fornecido pelo sistema operacional que permite que os processos se comuniquem entre eles. Esta comunicação pode envolver um processo informando outro processo sobre a ocorrência de um evento ou sobre a transferência de dados de um processo para outro.TutorialsPoint
Remote Procedure Call (RPC) é uma tecnologia de comunicação entre processos que permite a um programa de computador chamar um procedimento em outro espaço de endereçamento, implementando o modelo cliente-servidor de computação distribuídaWikipedia
Framework RPC de alta performance implementado em HTTP/2 que pode rodar em qualquer ambiente
Desenvolvido pelo Google em 2015 como a próxima geração do Stubby e liberado em 2016
Possui suporte para load balancing, tracing, health checking, autenticação e outras funcionalidades
grpc.ioMecanismo agnóstico de linguagem e plataforma para serializar dados estruturados
Semelhante a um Schema, onde definimos como os dados serão estruturados
E com geração automática de código-fonte do servidor e do cliente em diversas linguagens
developers.google.com/protocol-buffersFormato mais tradicional de comunicação, onde o cliente envia uma requisição e o servidor retorna a resposta
Assim que o cliente invoca o método stub, o servidor é notificado com os metadados do cliente para essa chamada, o nome do método e o timeout (se aplicável)
O servidor pode então enviar de volta seus próprios metadados iniciais (que devem ser enviados antes de qualquer resposta) imediatamente ou esperar pela requisição do cliente
Assim que o servidor receber a mensagem de requisição do cliente, ele faz o que for necessário e a resposta é então retornada com o status (código e mensagem opcional) e possíveis metadados finais
Cenário onde o servidor retorna um streaming de respostas ao invés de uma única
O cliente completa seu processamento depois que recebe todas as mensagens do servidor
Situação onde o cliente envia um streaming de mensagens em uma requisição
O servidor responde com uma única mensagem (com o status e possíveis metadados finais), tipicamente ‐ mas não necessariamente ‐ após ter recebido todas as mensagens do cliente
Onde há streaming de mensagens do Cliente e do Servidor
Nesse caso, cada aplicação pode implementar de sua maneira: o servidor pode responder a cada dado recebido, a cada X mensagens ou esperar até que um dos lados feche a conexão
An open-source message-broker software (sometimes called message-oriented middleware) that originally implemented the Advanced Message Queuing Protocol (AMQP) and has since been extended with a plug-in architecture to support Streaming Text Oriented Messaging Protocol (STOMP), MQ Telemetry Transport (MQTT), and other protocols
O Apache Kafka é uma plataforma distribuída de transmissão de dados que é capaz de publicar, subscrever, armazenar e processar fluxos de registro em tempo real. Essa plataforma foi desenvolvida para processar fluxos de dados provenientes de diversas fontes e entregá-los a vários clientes.Red Hat
O Kafka é muito mais que um Message Broker: com uma grande comunidade e muita estabilidade, possui muito mais funcionalidades do que a simples troca de mensagens entre produtores e consumidoresvepo.medium.com
Em um ambiente de microsserviços, algumas requisições precisarão realizar transações entre diversos serviços distintos
Como cada microsserviço possui seu próprio banco de dados, precisamos ter uma forma de controlar essas transações, parar o processo assim que houver um erro e fazer os devidos rollbacks em cada um dos serviços
Artigo publicado por Hector Garcia-Molina em 1987, focando em Long-Running Processes
Sequência de transações locais, onde cada transação irá publicar uma mensagem que irá disparar a próxima transação local do serviço seguinte
Se a transação local falhar (por regra de negócio ou outro motivo), serão executadas uma série de transações compensatórias para desfazer as mudanças realizadas até o momento
Normalmente, os registros são primeiro criados com um status Pendente
e, após a
confirmação das transações seguintes, são alterados para Ativo
Nesse caso, transações compensatórias podem simplesmente alterar esse campo para
Inativo
Cada transação local publica eventos que irão desencadear transações em outros serviços
Um objeto central (orquestrador) é responsável por invocar cada transação dos serviços
Conceito elaborado por Bertrand Meyer (criador da linguagem Eiffel e do princípio Design by Contract):
Asking a question should not change the answerWikipedia
Conceito elaborado por Bertrand Meyer (criador da linguagem Eiffel e do princípio Design by Contract):
Every method should either be a command that performs an action, or a query that returns data to the caller, but not bothWikipedia
class Employee {
public function increaseSalary(float $amount): float {
$this->salary += $amount;
return $this->salary;
}
}
Exemplo de código comumente encontrado
$employee = new Employee();
$wage = $employee->getWage();
// alguma coisa no script...
$employee->increaseSalary(500);
// $wage agora está desatualizado
Possível problema
class Employee {
public function increaseSalary(float $amount): float {
$this->salary += $amount;
return $this->salary;
}
}
Exemplo de código comumente encontrado
class Employee {
public function increaseSalary(float $amount): void {
$this->salary += $amount;
}
public function getSalary(): float {
return $this->salary;
}
}
Exemplo de código refatorado usando CQS
É um padrão arquitetural descrito pela primeira vez em 2010 por Greg Young
Separação (em objetos distintos) da responsabilidade de escrita e leitura dos dados
Os Commands precisam demonstrar a intenção do usuário em executar aquela ação
Exemplos: “Cadastrar Endereço”, “Calcular Imposto”, “Adicionar Item ao Carrinho”
class Employee {
public function increaseSalary(float $amount): void {
$this->salary += $amount;
}
public function getSalary(): float {
return $this->salary;
}
}
Exemplo visto em CQS
class IncreaseEmployeeWageCommand {
public function __construct(Employee $employee, float $amount) {
// ...
}
}
class EmployeeFinder {
// ...
}
Exemplo aplicando CQRS
Escalar a aplicação tem se tornado cada vez mais comum nos últimos tempos, mas mesmo assim ainda existe um grande gargalo: o banco de dados
Há uma grande complexidade envolvida em escalar bancos de dados — e muitas vezes um alto custo também
Além disso, para exibirmos um dado para o cliente, às vezes temos que aplicar várias regras
de negócio, realizar JOINs
(se usarmos bancos SQL) e outras ações para
formatar a informação
Tudo isto pode custar muito tempo e até mesmo causar timeouts, deadlocks e outras situações terríveis para DBAs
O uso de bancos NoSQL ajudou em muito ao guardar documentos em formatos um pouco mais parecidos ao que o usuário final irá visualizar
Ainda assim, em uma aplicação comum a frequência de escritas é muito menor do que a de leitura (em proporções até maiores que 80/20%)
Então, por que geralmente facilitamos a escrita ao invés da leitura ao arquitetar nossos bancos de dados?
1. Comece separando logicamente Commands e Queries em objetos diferentes
class EmployeeFinder() {
}
class IncreaseEmployeeWageCommand {
public function __construct(Employee $employee, float $amount) {
}
}
class PromoteEmployeeCommand {
public function __construct(Employee $employee, Position $newPosition) {
}
}
2. Crie uma base de dados somente de leitura e faça seus Commands a atualizarem assim que algo for persistido
3. Faça a atualização da base de leitura acontecer de forma assíncrona, para não atrapalhar na performance
Dependendo de sua regra de negócio, você pode começar criando um procedimento recorrente
(ex: através do Cron) para atualizar sua base em intervalos de tempo específico
3. Faça a atualização da base de leitura acontecer de forma assíncrona, para não atrapalhar na performance
Mas o mais indicado é utilizar um sistema de filas: ao receber um comando, publique uma
mensagem dizendo que o registro X
da tabela Y
precisa ser
atualizado e crie um subscriber que irá executar essa tarefa
4. Se necessário, altere a base de leitura para torná-la uma agregação de dados de diferentes tabelas para entregar à view exatamente o que ela precisa
Esse base também é conhecida como banco de dados desnormalizado e irá agir similarmente a
um DTO
5. Utilize sistema de filas para executar os comandos
Ao receber uma requisição, valide minimamente os dados e publique uma mensagem dizendo
que o comando X
precisa ser executado (com os dados recebidos)
5. Utilize sistema de filas para executar os comandos
Então, crie um subscriber para executar esses comandos e emita uma resposta
indicando a finalização
5. Utilize sistema de filas para executar os comandos
Essa resposta pode ser um evento para a UI (dependendo da linguagem que você usa),
um email para o usuário ou uma push notification... você também pode trabalhar
com long polling, websockets, service workers ou outras abordagens
Com essa prática, nosso banco de dados leitura pode ficar alguns segundos (ou até minutos, dependendo da abordagem) desatualizado
E a dúvida é: “Mas essa prática não é ruim?”
Temos uma tendência de valorizar demais a consistência de dados, mas a inconsistência é algo muito comum em nosso dia-a-dia — tanto físico quanto virtual
Dados podem ser consultados e alterados por usuários simultaneamente conectados
Os dados exibidos em uma tela já podem ter sido alterados por outro usuário
Então é possível afirmar que toda informação exibida já pode estar obsoleta