Construindo a NuConta Gustavo Bicalho Maurício Verardo
Agenda NuConta Microsserviços no Nubank Transferindo dinheiro entre NuContas Event-sourcing: Modularidade e Escalabilidade Consistência em sistemas distribuídos O feed de movimentações Backend for Frontends com GraphQL
NuConta
NuConta
NuConta
NuConta
NuConta
NuConta
NuConta
NuConta https://nubank.design/
Microsserviços
Microsserviços Fatura Faturas Saldo Antecipação Saldos Antecipações
Microsserviços Fatura Load Balancer Faturas Saldo Load Balancer Saldos Antecipação Load Balancer Antecipações
Microsserviços Serviço A HTTP Serviço B
Microsserviços Serviço A Serviço B Tópico do Kafka
Transferindo dinheiro entre NuContas SOUTHEAST BRAZIL REGION FROM SPACE
Transferindo dinheiro entre NuContas Envios Recebimentos Pedido de envio Recebimento Envio Saldos Depósito Liquidação
Transferindo dinheiro entre NuContas Recebimentos Envios Pedido Pedido de envio Envio Solicitado Saldos
Transferindo dinheiro entre NuContas Recebimentos Envios Pedido Pedido de envio Envio Solicitado Liquidação efetuada Saldos Liquidação
Transferindo dinheiro entre NuContas Recebimentos Envios Pedido Dinheiro enviado para NuConta Pedido de envio Envio Envio Solicitado Liquidação efetuada Saldos Liquidação
Transferindo dinheiro entre NuContas Recebimentos Envios Pedido Dinheiro enviado para NuConta Pedido de envio Recebimento Envio Envio Solicitado Dinheiro Recebido Liquidação efetuada Saldos Liquidação
Transferindo dinheiro entre NuContas Recebimentos Envios Pedido Dinheiro enviado para NuConta Pedido de envio Recebimento Envio Envio Solicitado Dinheiro Recebido Liquidação efetuada Saldos Depósito Liquidação
Características interessantes desse fluxo Recebimentos Envios Pedido Dinheiro enviado para NuConta Pedido de envio Recebimento Envio Envio Solicitado Dinheiro Recebido Liquidação efetuada Saldos Depósito Liquidação
Event Sourcing
E se o cliente quiser enviar transferências para outra instituição financeira? Recebimentos Envios Pedido Dinheiro enviado para NuConta Pedido de envio Recebimento Envio Envio Solicitado Dinheiro Recebido Liquidação efetuada Saldos Depósito Liquidação
E se o cliente quiser enviar transferências para outra instituição financeira? Envios Pedido Dinheiro enviado para outra instituição Pedido de envio Envio Envio Solicitado Liquidação efetuada Saldos Depósito Liquidação Integração com o banco central
E se o cliente receber transferências de outra instituição financeira? Recebimentos Envios Pedido Dinheiro enviado para NuConta Pedido de envio Recebimento Envio Envio Solicitado Dinheiro Recebido Liquidação efetuada Saldos Depósito Liquidação
E se o cliente receber transferências de outra instituição financeira? Integração com o Banco Central Recebimentos Dinheiro recebido do Banco Central Recebimento Dinheiro Recebido Saldos Depósito Liquidação
E se o cliente quiser pagar a fatura do cartão de crédito? Recebimentos Envios Pedido Dinheiro enviado para NuConta Pedido de envio Recebimento Envio Envio Solicitado Dinheiro Recebido Liquidação efetuada Saldos Depósito Liquidação
E se o cliente quiser pagar a fatura do cartão de crédito? Pedido Cartão de crédito Pagamento de Fatura Pagamento de fatura Pedido de Pagamento Pagamento Pagamento Solicitado Liquidação efetuada Saldos Depósito Liquidação
Event Sourcing Sald o no mês pas sad o Saldo hoje Saldos u q o m e v e o d l Sa n a no Depósito Liquidação
Event Sourcing Pedido Recebimentos Envios Dinheiro enviado para NuConta Pedido de envio Recebimento Envio Envio Solicitado Dinheiro Recebido Liquidação efetuada Saldos Depósito Liquidação
Consistência em sistemas distribuídos
O que pode dar errado? Recebimentos Envios Dinheiro enviado para NuConta Pedido Recebimento Pedido de envio Envio Envio Solicitado Dinheiro Recebido Liquidação para envio Saldos Depósito Liquidação
O que pode dar errado? Recebimentos Envios Dinheiro enviado para NuConta Pedido Recebimento Pedido de envio Envio Envio Solicitado Dinheiro Recebido Liquidação para envio Saldos Depósito Liquidação
Primeiro requisito: Processamento at-least-once Todo evento publicado é recebido e processado completamente pelos consumidores pelo menos uma vez
Primeiro requisito: Processamento at-least-once MSG MSG MSG OK! :) MSG
Primeiro requisito: Processamento at-least-once Mensagens são persistidas e replicadas no Kafka cluster, podendo ser consumidas a qualquer momento Próxima mensagem da fila será entregue de novo até que consumidor confirme que completou seu processamento Consumidor só deve confirmar o processamento depois que completar todos os efeitos colaterais
Segundo requisito: Idempotência Uma operação é idempotente se aplicá-la várias vezes é equivalente a aplicá-la uma vez Exemplos: Multiplicação por 0: 7*0 = 7*0*0 = 7*0*0*0 = 0 DELETE FROM users WHERE users.id = 186
Segundo requisito: Idempotência Cada evento em nossa arquitetura tem um UUID aleatório Cada evento derivado usa o UUID do evento anterior (origem) como chave única (chave de idempotência) Serviços garantem consistência interna: banco de dados local valida a chave única Resultado: Criar um evento a partir de outro é uma operação idempotente PedidoDeEnvio ID: 32 Liquidacao ID: 78 Origem: PedidoDeEnvio:32
Segundo requisito: Idempotência PedidoDeEnvio ID: 32 Deposito ID: 377 Origem: Recebimento:156 Liquidacao ID: 78 Origem: PedidoDeEnvio:32 Envio ID: 44 Origem: Liquidacao:78 Recebimento ID: 156 Origem: Envio:44
E quando um serviço cair? Envios Pedido Pedido de envio Envio Solicitado Saldos Liquidação
E quando um serviço cair? Envios Pedido Pedido de envio Envio Solicitado Saldos Liquidação
E se rolar um bug ou mensagem inválida? Deadletters: Tópico onde guardamos mensagens cujo processamento falhou (inclui metadados como stack-traces, timestamp, etc) Envio Pedido Pedido de envio Envio Solicitado Saldos Liquidação Deadletters xkcd.com (CC BY-NC 2.5)
E se rolar um bug ou mensagem inválida? Deadletters: Tópico onde guardamos mensagens cujo processamento falhou (inclui metadados como stack-traces, timestamp, etc) Envio Pedido Pedido de envio /dev/null Descartar Republicar Envio Solicitado Saldos Liquidação Deadletters xkcd.com (CC BY-NC 2.5)
Feed de movimentações
Dados distribuídos = muitos requests Envios Recebimentos Saldos Pedido de envio Envio Recebimento Depósito Liquidação
Modelo do backend!= visão do cliente Evento de envio no feed Valor Data Status: pendente falha sucesso Liquidação 404 Envios Pedido de envio Envio Envio 404 404 Saldos Liquidação
Ciclos de atualização lentos Lançar uma nova versão de um aplicativo numa app store pode demorar dias Muitos usuários continuam com versões antigas por muito tempo Bugfixes e otimizações demoram para chegar Frontend acoplado impede evoluções no backend
Backend For Frontend Envios BFF Recebimentos Saldos Pedido de envio Envio Recebimento Depósito Liquidação
"GraphQL is a query language designed to build client applications by providing an intuitive and flexible syntax and system for describing their data requirements and interactions." (GraphQL spec: http://facebook.github.io/graphql/)
Schema é o modelo disponível para o frontend schema { query: Query } type Query { saldo(accountid: ID!): Float feed(accountid: ID!): [Envio Recebimento] } type Recebimento { data: Date valor: Float } enum StatusEnvio { PENDENTE, FALHA, SUCESSO } type Envio { data: Date valor: Float status: StatusEnvio } Envios Recebimentos Saldos Pedido de envio Envio Recebimento Depósito Liquidação
Query define os campos que o client precisa query feedscreen($accountid: ID!) { schema { query: Query } feed(accountid: $accountid) {... on Envio { data valor status }... on Recebimento { data valor } } } type Query { saldo(accountid: ID!): Float feed(accountid: ID!): [Envio Recebimento] } POST /api/query type Recebimento { data: Date valor: Float } enum StatusEnvio { PENDENTE, FALHA, SUCESSO } type Envio { data: Date valor: Float status: StatusEnvio }
Query define os campos que o client precisa Client v1 query($accountid: ID!) { feed(accountid: $accountid) { } saldo(accountid: $accountid) } schema { query: Query } Client v2 query($accountid: ID!) { feed(accountid: $accountid) { } saldodetalhado(accountid: $accountid) { } } type Query { saldo(accountid: ID!): Float feed(accountid: ID!): [Envio Recebimento] saldodetalhado(accountid: ID!): SaldoDetalhado }
Recapitulando Event-sourcing Comunicação assíncrona via Kafka Backend for Frontend com GraphQL
Obrigado! Gustavo Bicalho Maurício Verardo https://sou.nu/vagasnu