De um problema de produto a uma plataforma reutilizável.
Não partimos com a intenção de construir uma plataforma de backend. Quando começámos a desenvolver o ExME, a prioridade era simples: avançar depressa. Precisávamos das capacidades habituais de backend — autenticação, acesso à base de dados, gestão de ficheiros, envio de email e APIs — sem passar meses a construir infraestrutura antes de termos um produto.
Na altura, o DreamFactory encaixava bem. Era open source, não tinha custo de licença, e dava-nos muitas das capacidades de que precisávamos de origem. Para um produto em fase inicial, esse compromisso fazia sentido. Depois o ExME cresceu. E os pressupostos por trás dessa decisão começaram a mudar.
Quando o problema muda, a arquitetura tem de mudar
À medida que o ExME crescia em utilizadores e em complexidade, começámos a encontrar problemas de estabilidade e de escalabilidade. O problema não era simplesmente o sistema estar “lento”. O problema mais relevante era a eficiência de recursos. Estávamos a ver um consumo relativamente alto de recursos para a quantidade de pedidos a serem processados. A arquitetura que tinha sido perfeitamente adequada quando o produto era mais pequeno estava a tornar-se cada vez mais difícil de escalar com eficiência.
Ao mesmo tempo, os nossos requisitos de segurança dos dados estavam a tornar-se mais exigentes. Em particular, implementar o nível de segurança ao nível da linha e de controlo de acesso aos dados que queríamos exigia soluções de contorno cada vez mais complexas. Depois houve outra mudança: o modelo comercial da tecnologia deixou de corresponder à razão pela qual a tínhamos escolhido originalmente.
Nesse ponto, continuar com a mesma solução significava aceitar compromissos em várias áreas ao mesmo tempo:
- escalabilidade;
- consumo de recursos;
- segurança e controlo de acesso aos dados;
- complexidade operacional;
- e custo.
Foi nesse ponto que deixámos de perguntar:
“Como é que fazemos esta solução funcionar?”
e passámos a perguntar:
“O que é que precisamos, de facto, do nosso backend?”
Construir apenas o que precisávamos
A resposta não era construir mais uma framework genérica de backend. Queríamos algo focado nos requisitos com que estávamos mesmo a lidar:
- autenticação e autorização;
- acesso controlado aos dados;
- performance previsível;
- gestão de ficheiros;
- comunicação por email;
- logging estruturado;
- configuração;
- escalabilidade;
- e, importante, consistência entre aplicações.
Construímos o novo backend com Spring Boot, juntamente com um portal de configuração que permite gerir grande parte do comportamento da plataforma sem implementar repetidamente a mesma funcionalidade em cada aplicação. O objetivo não era criar mais uma stack tecnológica só por a ter. O objetivo era centralizar capacidades que se estavam a tornar comuns nas nossas aplicações.
Do backend do ExME ao Fabric
Inicialmente, o novo backend existia porque o ExME precisava dele. Isso mudou relativamente depressa. À medida que desenvolvíamos outras aplicações, íamos encontrando os mesmos requisitos:
- Como devem os utilizadores autenticar-se?
- Como deve uma aplicação aceder a um determinado dado?
- Como devem os ficheiros ser guardados?
- Como devem ser executadas operações que envolvem várias ações de backend?
- Como devem as falhas ser registadas?
- Como devem as aplicações ser configuradas de forma consistente?
Já tínhamos soluções para muitos destes problemas. Havia pouca razão para os resolver outra vez, de forma independente. Por isso o backend deixou de ser o backend do ExME e passou a ser algo mais geral: uma plataforma reutilizável para os nossos projetos de software.
Essa distinção importa. Reutilizar código é útil. Reutilizar decisões de arquitetura bem compreendidas é muito mais valioso.
A plataforma está a tornar-se um ecossistema
O Fabric está a evoluir para um conjunto de componentes especializados, em vez de uma aplicação grande responsável por tudo. Neste momento, três componentes definem a direção dessa arquitetura.
Fabric
│
┌──────────────────┼──────────────────┐
│ │ │
Heimdall Saga Ekko
Authentication Operations Communication
│ │ │
Identity Synchronous Real-time
Security execution + Push
│
Multiple steps
Cada componente tem uma responsabilidade específica.
Heimdall — Identidade e autenticação
O Heimdall é responsável pelo lado da identidade e da autenticação do ecossistema. Fornece a base comum para aplicações que precisam de saber:
- quem é o utilizador;
- se o utilizador está autenticado;
- que acesso está disponível;
- e como essa identidade se propaga pelo backend.
Esta é uma distinção importante para nós. A autenticação não devia ser algo que cada aplicação implementa de forma independente. É infraestrutura.
Saga — executar uma operação
O componente conhecido internamente como Pilot também evoluiu para além da ideia de um simples worker. O seu trabalho é executar operações compostas por vários passos.
Por exemplo: Enviar um email ao utilizador 2. Essa operação pode conter:
Operation: sendEmail
Step 1
→ Database
→ Get email address for user 2
Step 2
→ Email
→ Send message
Outra operação pode ser: Atualizar o avatar do utilizador 4.
Operation: uploadFileForAvatar
Step 1
→ Storage
→ Upload file
Step 2
→ Database
→ Store the resulting file reference
Os passos individuais podem ter tipos diferentes e interagir com capacidades diferentes do backend. Isto dá-nos um modelo de execução comum, em vez de implementar estas operações em várias fases separadamente dentro de cada aplicação. Estamos neste momento a usar este modelo para operações síncronas. A arquitetura também deixa espaço para uma capacidade futura de execução assíncrona, que se torna cada vez mais relevante à medida que o número e a complexidade das operações crescem.
Ekko — comunicar com os utilizadores
O Ekko é a próxima parte do ecossistema que estamos a desenvolver. A sua responsabilidade é a comunicação entre as nossas aplicações e os seus utilizadores, em particular quando a comunicação tem de acontecer em tempo real.
O modelo pretendido não é simplesmente: “Enviar uma notificação.” Está mais perto de:
Application
│
▼
Ekko
│
├── Real-time communication
│
└── Push notification fallback
Se um utilizador estiver ligado ativamente, a comunicação pode acontecer pelo canal em tempo real. Se não estiver, o sistema pode recorrer a notificações push. Isto cria mais uma capacidade que as aplicações não deviam ter de implementar de forma independente.
Para onde a plataforma vai
Não vemos o Fabric como um produto acabado. Na verdade, uma das características úteis da plataforma é que o seu roadmap está a ser conduzido por problemas que encontramos de facto. A direção inclui neste momento áreas como:
Autenticação e autorização
Continuar a evoluir o modelo de identidade e de controlo de acesso entre aplicações.
Execução de operações
Alargar o conjunto de passos disponíveis e melhorar o controlo da execução, o tratamento de erros e a observabilidade.
Comunicação em tempo real
Concluir o Ekko e a sua integração com o recurso a notificações push.
Processamento assíncrono
Introduzir execução em segundo plano para operações que não devem ficar presas a um pedido síncrono. Isso vai introduzir outro conjunto de preocupações:
- filas;
- retries;
- tratamento de falhas;
- idempotência;
- estado da execução;
- monitorização.
Não são detalhes de implementação que se possam simplesmente acrescentar mais tarde sem pensar neles. Fazem parte da arquitetura.
Observabilidade
À medida que a plataforma se torna infraestrutura partilhada, perceber o que está a fazer torna-se cada vez mais importante. O logging é apenas o ponto de partida. Precisamos de conseguir perceber:
- o que aconteceu;
- quando aconteceu;
- onde aconteceu;
- o que falhou;
- e o que aconteceu a seguir.
Porque é que construímos isto nós próprios?
Esta é provavelmente a pergunta que devemos fazer de forma explícita. A lição não é:
“Construir em vez de comprar.”
Não acreditamos nisso. A decisão original de usar um backend existente era razoável. Construir autenticação, gestão de ficheiros, envio de email e APIs de raiz antes de o ExME ter validado os seus próprios requisitos não teria sido necessariamente uma melhor decisão de engenharia.
A lição foi outra. A arquitetura certa pode mudar à medida que o problema muda. No início, usar uma plataforma existente deu-nos velocidade. Mais tarde, a mesma plataforma introduziu restrições de escalabilidade, consumo de recursos, segurança e custo.
Construir o nosso próprio backend passou a justificar-se porque já não estávamos a resolver um problema isolado. Estávamos a resolver a mesma classe de problemas em várias aplicações. Isso mudou a economia e a arquitetura.
A plataforma não é o produto
Há outro princípio por trás do Fabric que é talvez mais importante do que a própria tecnologia. Não queremos que as aplicações existam simplesmente para demonstrar a plataforma. O contrário é que é verdade. A plataforma existe para tornar as aplicações mais fáceis de construir, operar e fazer evoluir.
Isso significa que não queremos ditar tecnologia só porque calhámos de ter construído algo internamente. Se uma tecnologia diferente for melhor para um problema concreto, devemos usá-la. A plataforma deve retirar complexidade desnecessária, não criar uma nova forma dela.
De um problema a uma plataforma
A evolução pode portanto resumir-se de forma bastante simples:
ExME
│
│ Need to move quickly
▼
Existing backend platform
│
│ ExME grows
│
├── Scalability constraints
├── Resource consumption
├── Security requirements
├── Data access complexity
└── Commercial model changes
│
▼
Fabric
│
├── Heimdall
│ Identity & Authentication
│
├── Saga
│ Operation execution
│
└── Ekko
Real-time communication
+ Push fallback
│
▼
Reusable platform
│
└── Multiple applications
Não partimos com um plano para construir uma plataforma de backend. Partimos a tentar resolver um problema. A plataforma surgiu quando percebemos que o problema não era único do ExME.
E essa é provavelmente a lição de arquitetura mais importante desta história: Boa arquitetura não é tomar a decisão certa uma vez. É reconhecer quando o problema mudou — e estar disposto a mudar com ele.