Projeto final: desenhando a arquitetura de um sistema real do zero

1. Definição do Problema e Requisitos

Vamos projetar a arquitetura de uma plataforma de streaming de vídeo educacional — a "EduStream". O objetivo de negócio é oferecer cursos em vídeo com baixa latência, alta disponibilidade e recomendações personalizadas para uma base global de 10 milhões de usuários ativos mensais.

Requisitos funcionais:
- Cadastro e autenticação de usuários (alunos e instrutores)
- Catálogo de cursos com busca e filtros por categoria, duração e popularidade
- Upload e transcodificação de vídeos em múltiplas resoluções (360p, 720p, 1080p)
- Reprodução contínua com adaptação de bitrate (HLS/DASH)
- Sistema de recomendação baseado em histórico e avaliações

Requisitos não funcionais:
- Disponibilidade de 99.99% (menos de 53 minutos de downtime por ano)
- Latência de playback inferior a 2 segundos para conteúdo popular
- Escalabilidade para picos sazonais (ex: início de semestre)
- Segurança: DRM para conteúdo premium, proteção contra ataques DDoS

2. Modelagem de Domínio e Restrições Técnicas

As entidades principais do domínio são: Usuário, Curso, Vídeo, Playlist, Avaliação e Log de Visualização. O relacionamento central é entre Usuário e Vídeo, mediado por inscrições em cursos e histórico de visualização.

Restrições técnicas impostas pelo cenário real:
- Throughput: 500 mil requisições simultâneas de playback durante horário de pico
- Armazenamento: 2 PB de vídeos brutos + 5 PB de vídeos transcodificados (estimativa anual)
- Banda de upload: 100 Gbps para ingestão de novos conteúdos
- Banda de download: 1 Tbps para distribuição via CDN

Trade-offs iniciais:
- Consistência eventual para logs de visualização (sacrificamos consistência imediata por disponibilidade)
- Custo vs. performance: optamos por cache em múltiplas camadas (CDN + Redis) para reduzir latência, mesmo que encareça a infraestrutura
- Sincronia vs. assincronia: upload de vídeo é assíncrono (fila), enquanto consultas ao catálogo são síncronas (REST)

3. Arquitetura em Camadas e Estilo Arquitetural

Adotamos microsserviços com API Gateway e comunicação assíncrona via mensageria. Essa escolha permite escalar componentes individualmente e isolar falhas.

Camadas da arquitetura:

[CDN + SPA (React)] → [API Gateway (Kong)] → [Serviços] → [Bancos + Cache]

Separação de responsabilidades em serviços:
- Serviço de Autenticação: OAuth 2.0 + JWT, gerencia sessões e RBAC
- Serviço de Catálogo: CRUD de cursos e vídeos, busca elástica (Elasticsearch)
- Serviço de Upload: Recebe vídeos, valida metadados e publica eventos na fila
- Serviço de Transcodificação: Consome eventos, processa vídeos em lote
- Serviço de Recomendação: Motor baseado em eventos de visualização e cache distribuído
- Serviço de Playback: Entrega URLs assinadas para CDN, gerencia sessões de streaming

4. Componentes Críticos e Comunicação entre Serviços

Serviço de Transcodificação: pipeline com fila

O upload de um vídeo dispara um evento para o RabbitMQ. O serviço de transcodificação consome esse evento, baixa o vídeo bruto do S3, processa em múltiplas resoluções usando FFmpeg e salva os resultados de volta no S3.

# Exemplo de evento publicado no RabbitMQ
{
  "event_type": "video.uploaded",
  "video_id": "v_12345",
  "bucket": "edustream-raw",
  "object_key": "courses/matematica/aula01.mp4",
  "resolutions": ["360p", "720p", "1080p"],
  "timestamp": "2025-03-20T14:30:00Z"
}

Serviço de Recomendação: motor baseado em eventos

Cada visualização gera um evento Kafka. O serviço de recomendação consome esses eventos, atualiza embeddings de usuários no Redis e retorna recomendações via gRPC.

Comunicação síncrona vs. assíncrona:
- Síncrona (REST/gRPC): consultas ao catálogo, busca de cursos, validação de autenticação
- Assíncrona (eventos): upload de vídeo, transcodificação, notificações, atualização de recomendações

A escolha por assincronia no upload é justificada pela necessidade de processamento pesado (transcodificação pode levar minutos), que bloquearia a requisição do usuário se fosse síncrona.

5. Estratégias de Dados e Armazenamento

Banco relacional (PostgreSQL): dados estruturados e transacionais — usuários, cursos, inscrições, avaliações. Usamos replicação primária-secundária para leitura escalável.

-- Esquema simplificado do banco relacional
CREATE TABLE users (
    id UUID PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(255) UNIQUE,
    role ENUM('student', 'instructor', 'admin'),
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE courses (
    id UUID PRIMARY KEY,
    title VARCHAR(200),
    instructor_id UUID REFERENCES users(id),
    category VARCHAR(50),
    created_at TIMESTAMP DEFAULT NOW()
);

Banco NoSQL (Cassandra): logs de visualização e histórico — dados de alta cardinalidade e escrita intensa. Particionamos por user_id e ordenamos por timestamp.

Armazenamento de objetos (S3): vídeos brutos (bucket edustream-raw) e transcodificados (bucket edustream-transcoded). Usamos lifecycle policies para mover vídeos antigos para Glacier.

Cache: CDN (CloudFront) para conteúdo popular com TTL de 24 horas. Cache de aplicação (Redis) para sessões de usuário, recomendações temporárias e resultados de busca frequentes.

6. Resiliência, Escalabilidade e Observabilidade

Estratégias de resiliência:
- Circuit breaker no serviço de recomendação: se o Redis falhar, retorna lista padrão de cursos populares
- Retry com backoff exponencial para chamadas ao serviço de transcodificação (máx. 3 tentativas)
- Fallback: se o CDN não tiver o vídeo, o serviço de playback redireciona para o S3 diretamente

Escalabilidade horizontal:
- API Gateway com auto-scaling baseado em CPU (mín. 3 instâncias, máx. 20)
- Serviço de transcodificação escala com base no tamanho da fila (Kubernetes HPA)
- Banco PostgreSQL usa read replicas para consultas; escritas vão para o primário

Observabilidade:
- Logging centralizado: ELK Stack (Elasticsearch, Logstash, Kibana)
- Métricas: Prometheus coleta métricas de cada serviço (latência, taxa de erro, throughput)
- Tracing distribuído: Jaeger para rastrear requisições completas (ex: do upload ao playback)

7. Segurança e Governança da Arquitetura

Autenticação e autorização: OAuth 2.0 com fluxo Authorization Code + PKCE. Tokens JWT com claims de role (student, instructor, admin). RBAC por serviço: apenas instrutores podem fazer upload; apenas admins podem deletar cursos.

Proteção de conteúdo:
- DRM (Widevine/PlayReady) para vídeos premium
- URLs assinadas no CDN com expiração de 1 hora
- Rate limiting no API Gateway: 100 req/min por usuário para endpoints de busca

Governança:
- ADRs (Architecture Decision Records) documentados no repositório de arquitetura
- Revisões trimestrais de arquitetura com testes de caos (Netflix Chaos Monkey) para validar resiliência
- Política de versionamento de APIs (semver) e depreciação com 6 meses de aviso

8. Exemplo Prático: Fluxo Completo de uma Requisição

Cenário 1: Upload de vídeo por um instrutor

  1. Instrutor envia vídeo via SPA → API Gateway valida JWT e roteia para serviço de upload
  2. Serviço de upload salva metadados no PostgreSQL (status: "processing") e faz upload do arquivo para S3 (bucket raw)
  3. Publica evento video.uploaded no RabbitMQ e retorna 202 Accepted para o cliente
  4. Serviço de transcodificação consome o evento, baixa o vídeo do S3, processa em 3 resoluções e salva no bucket transcoded
  5. Atualiza status no PostgreSQL para "ready" e publica evento video.ready
  6. Serviço de notificação (via WebSocket) avisa o instrutor que o vídeo está disponível

Cenário 2: Usuário assiste a um vídeo

  1. Usuário clica em "Assistir" → SPA requisita URL assinada ao serviço de playback via API Gateway
  2. Serviço de playback verifica se o usuário tem acesso ao curso (consulta PostgreSQL) e se o vídeo está em cache no CDN
  3. Se cache hit: retorna URL do CDN diretamente (latência < 100ms)
  4. Se cache miss: gera URL assinada para o S3 e retorna; CDN fará cache para próximas requisições
  5. Paralelamente, o serviço de playback publica evento video.watched no Kafka
  6. Serviço de recomendação consome o evento e atualiza o perfil do usuário no Redis

Decisões de design justificadas:
- Fila (RabbitMQ) em vez de chamada síncrona no upload: transcodificação leva de 30 segundos a 5 minutos. Se fosse síncrona, o usuário ficaria esperando. Com fila, o upload termina em segundos e o processamento ocorre em background.
- Cache em duas camadas (CDN + Redis): CDN reduz latência de entrega de vídeo para usuários globais; Redis acelera recomendações e sessões sem sobrecarregar o banco relacional.
- Cassandra para logs: logs de visualização geram milhões de escritas por minuto. PostgreSQL não suportaria essa carga sem custo proibitivo. Cassandra oferece escrita rápida e escalabilidade linear.

Referências