No QCon SF, Suudhan Rangarajan apresentou a Netflix Play API e o porquê de uma arquitetura evolucionária. Pontos principais: serviços que possuem uma identidade única são mais fáceis de atualizar; gastar tempo identificando decisões-chave que precisam ser feitas ao se construir um serviço; e projetar uma "arquitetura evolucionária" usando ferramentas específicas traz muitos benefícios.
No QCon SF, Suudhan Rangarajan apresentou a "Netflix Play API: Por que construímos uma arquitetura evolucionária". Os pontos principais da palestra incluem:
- Serviços que possuem uma identidade/responsabilidade única são mais fáceis de manter e atualizar.
- Engenheiros deveriam gastar tempo identificando decisões-chave que precisam ser feitas ao se construir um serviço, determinando quais destas são "Tipo 1" (deliberação completa) ou "Tipo 2" (experimentação rápida).
- Projetar uma "arquitetura evolucionária" usando ferramentas específicas, como "fitness functions", traz muitos benefícios.
Rangarajan, engenheiro sênior de software na Netflix, começou sua apresentação falando sobre 2 marcos de negócio dentro da Netflix em 2016 que também trouxeram um enorme impacto de engenharia. O primeiro marco foi o lançamento do "#netflixeverywhere" em Janeiro, que permitiu aos usuários em diversos países onde a Netflix antes não era disponível, que agora pudessem se cadastrar e assistir conteúdos. O segundo marco foi o lançamento da nova funcionalidade "Download / Go" em Novembro, permitindo que os usuários baixassem conteúdo em qualquer dispositivo para assistir offline. Ambos os lançamentos aumentaram a tensão na "Play API" que era responsável por, entre outras coisas, iniciar o streaming de conteúdo para os usuários. Isso resultou em inúmeras interrupções de serviço e também em uma redução da frequência de deploy, com um aumento de rollbacks que, como citado por Forsgren, Humble e Kim no livro "Accelerate", são métricas-chave correlacionadas com a performance de entrega de software.
Com uma visão geral da arquitetura anterior do serviço da Play API, foi possível ver que os dispositivos dos usuários se conectam ao serviço de proxy da API rodando no máximo (presumidamente a porta de entrega Zuul API) que comunica com um serviço monolítico de API que contém várias APIs, incluindo a própria Play API. Esse serviço de API, por sua vez, se comunicava com os microservices específicos do domínio para lidar com os fluxos de solicitações dos usuários.
O restante da palestra se dividiu em três partes, além de discutir o contexto e princípios orientadores relativos às melhorias recentes feitas no serviço monolítico de API. As três partes foram sobre: identidade de serviço - explorar por que o serviço existe; identificar as decisões de tipo 1 e tipo 2 - determinar quais decisões terão um impacto grande de longo prazo e que requerem um enorme investimento adiantado; e a evolutividade - explorar como construir um serviço que pode evoluir ao longo do tempo com requisitos que se alteram e constantes.
Sobre identidade de serviço, Rangarajan sugeriu que os engenheiros devem "começar pelo porquê"; perguntar por que um serviço existe para determinar sua responsabilidade. Para a Play API, a cadeia de motivação veio da Netflix querendo "liderar a revolução da Internet TV para entreter bilhões de pessoas em todo o mundo", para maximizar o engajamento do consumidor desde a assinatura até a transmissão, até "ativar a funcionalidade de aquisição, descoberta e reprodução 24/7". O público foi lembrado do princípio da responsabilidade única e advertiu que, ao conduzir este exercício, deve-se ter cuidado com as "múltiplas identidades agregadas em um único serviço", pois isso pode levar a um serviço criado com antipadrões arquiteturais de baixa coesão e alto acoplamento. Assim, a primeira grande mudança proposta pelo time da Play API foi dividir o serviço de API monolítico existente em um modelo "serviço de API por função". A Play API seria reconstruída e implantada como um microservice - e isso foi feito como uma decisão "Tipo 1".
Acreditamos em uma identidade única simples para nossos serviços. A identidade se relaciona e complementa as identidades da organização, time e seus serviços semelhantes.
Sobre as decisões de Tipo 1 e Tipo 2, explicou-se com citações de fontes sobre este modelo de tomada de decisão de Jeff Bezos. As decisões de Tipo 1 são altamente consequenciais e possuem impacto de longo alcance, assim, essas decisões precisam ser feitas metodicamente e engajando a consulta entre grupos. As decisões do Tipo 2 são facilmente alteráveis e não possuem implicações de longo alcance, portanto, essas decisões devem ser feitas rapidamente e por "indivíduos com alta capacidade de julgamento ou pequenos grupos". Os três tipos de decisões Tipo 1 identificadas pelo time da Play API incluem acoplamento apropriado, comunicações síncronas vs assíncronas, e arquitetura de dados.
Algumas decisões geram consequências e são irreversíveis ou quase irreversíveis - como um caminho de mão única - e estas decisões devem ser feitas metodicamente, com cuidado, devagar, com grande deliberação e consulta [...]. Podemos chamar essas decisões de Tipo 1.
Olhando primeiro para o acoplamento apropriado, Rangarajan afirmou que existem efetivamente dois tipos de bibliotecas compartilhadas ao projetar uma arquitetura baseada em microservice: bibliotecas que fornecem funções comuns e bibliotecas de clientes usadas para comunicações entre serviços. Ao usar bibliotecas compartilhadas com funções comuns - por exemplo, o "pacote de utilitários" - é fácil introduzir o "acoplamento binário" excessivo, o que dificulta a manutenção e a atualização dessa biblioteca. Com as bibliotecas de comunicação do cliente, também pode ser fácil introduzir o "acoplamento operacional", por exemplo, onde a funcionalidade bem-intencionada de fallback fornecida em uma biblioteca pode consumir recursos excessivos e causar uma falha em cascata. Também é fácil introduzir o "acoplamento de linguagem" com bibliotecas de comunicação, por exemplo, se um time de serviço upstream prover apenas uma biblioteca Java.
A combinação desses problemas em conjunto com a identificação e discussão dos requisitos atuais, levou o time da Play API a decidir trabalhar ativamente para minimizar o uso de bibliotecas "utilitárias" compartilhadas nos novos serviços que planejavam criar. O time também decidiu usar gRPC em vez de REST via JSON e HTTPS para comunicação de serviço a serviço, o que permitia que métodos e entidades de RPC fossem definidos através de Protocol Buffers e bibliotecas de clientes/SDKs geradas automaticamente em vários idiomas. Resumindo, o conselho para essa decisão Tipo 1 de "acoplamento apropriado" foi "considerar os clientes autogerados 'terceiros' com comunicação bidirecional e minimizar a reutilização do código através dos limites de serviço".
O segundo tipo de decisão Tipo 1, comunicações síncronas vs assíncronas, foi discutida em seguida. Após a deliberação, o time decidiu que não tinha uma necessidade além da interação do tipo solicitação/resposta entre a Play API e os serviços de suporte e, portanto, implementaram um controlador de solicitações de bloqueio com I/O sem bloqueio para chamadas de saída entre serviços.
Enquanto a discussão se voltava para o terceiro tipo de decisão Tipo 1 encontrada pelo time, "arquitetura de dados", Rangarajan advertiu que "sem uma arquitetura de dados intencional, os dados se tornam seu próprio monolito". Na arquitetura anterior da Play API, vários serviços acessavam a mesma fonte de dados, o que resultava em alto acoplamento e reduzia os caminhos para a evolução dos serviços e do esquema de dados subjacente. Sem disfarces, Rangarajan sorriu largamente e citou David Wheeler dizendo que "todos os problemas em ciência da computação podem ser resolvidos por outro nível de indireção" e afirmou que após a discussão e análise, a equipe da Play API introduziu um carregador de dados intermediário e uma camada de armazenamento de dados que efetivamente implementou uma visão materializada entre o serviço e as fontes de dados subjacentes.
Em resumo, o conselho para a decisão do Tipo 1 relacionada à arquitetura de dados foi:
Isole "Dados" de "Serviço". No mínimo, certifique-se de que as fontes de dados sejam acessadas por meio de uma camada de abstração, de modo que ela deixe espaço para extensões posteriores.
O último conselho nesta parte da palestra foi em relação às decisões do Tipo 2, que devem ser tomadas "escolhendo um caminho, experimentando e iterando". Os princípios orientadores para a tomada de decisão na construção de um serviço é concentrar-se na identificação do tipo de decisão a ser enfrentada:
Identifique suas decisões Tipo 1 e Tipo 2; Gaste 80% do seu tempo debatendo e se alinhando nas decisões Tipo 1.
O final da palestra enfocou os princípios da arquitetura "big picture", e começou com uma citação do livro "Construindo Arquiteturas Evolutivas", de Neal Ford, Rebecca Parsons e Patrick Kua: "uma arquitetura evolucionária suporta mudanças guiadas e incrementais como um primeiro princípio entre múltiplas dimensões". Rangarajan argumentou que os resultados das decisões do Tipo 1 discutidos anteriormente, em última análise, levaram a uma arquitetura de microservices com acoplamento apropriado, que suportava o tipo de evolução necessária. "Fitness Functions" podem ser usadas para monitorar e orientar mudanças futuras, e também permitir que a discussão seja focada em torno das inevitáveis compensações que devem ser feitas ao projetar a arquitetura, por ex. para a equipe da Play API, simplicidade acima da confiabilidade (por exemplo, fallbacks podem causar problemas em cascata), e escalabilidade acima da taxa de transferência (por exemplo, cache extensivo proporcionava alto desempenho, mas não escalava devido ao tempo gasto para o aquecimento inicial do cache).
Rangarajan concluiu a palestra afirmando que ao longo do ano, desde que as novas alterações foram feitas, não houve incidentes de produção e o time estava próximo de sua meta de implantação, com uma média de 4,5 implantações por semana, com apenas dois rollbacks.
O vídeo completo e a transcrição da palestra "Netflix Play API: Por que construímos uma arquitetura evolucionária", estão disponíveis no site do InfoQ.