Como Criamos Uma Plataforma Multi-App com Um Código Único
5 min de leituraMohammad Shaker

Como Criamos Uma Plataforma Multi-App com Um Código Único

Alphazed opera mais de 7 apps educacionais com um único backend e Flutter, cada app tem tabelas, configuração e conteúdo próprios.

Engineering

Resposta rápida

Alphazed opera mais de 7 apps educacionais com um único backend e Flutter, cada app tem tabelas, configuração e conteúdo próprios.

A Alphazed opera mais de 7 aplicativos educacionais (Amal, Thurayya, Qais, KidElite, Alphazed School, Alphazed Montessori e outros) a partir de um único código backend e uma estrutura móvel Flutter compartilhada. Cada aplicativo possui suas próprias tabelas de banco de dados (com prefixo), configuração, notificações push, modelos de email e conteúdo — mas compartilham autenticação (AWS Cognito), infraestrutura de análise e algoritmos centrais de aprendizagem.

Backend: Seleção do App em Tempo de Execução

Como Funciona

No momento do deploy, uma variável de ambiente seleciona o app:

# Deploy Amal
export APP_NAME=amal
serverless deploy

Deploy Thurayya

export APP_NAME=thurayya serverless deploy

Deploy Qais

export APP_NAME=qais serverless deploy

Cada implantação cria funções Lambda independentes, rotas API Gateway e monitoramento — mas todas se conectam ao mesmo código backend.

Prioridade de Configuração em Três Camadas

# src/config.py
import os

app_name = os.getenv('APP_NAME', 'amal')

Camada 1: Configuração exata do app

config = load_json(f'config/{app_name}.json')

Camada 2: Configuração da família do app (se não existir configuração exata)

if not config: family = app_name.split('_')[0] # 'amal_beta' → 'amal' config = load_json(f'config/{family}.json')

Camada 3: Configuração padrão

if not config: config = load_json('config/default.json')

Configuração por App (config/amal.json)

{
  "app_name": "amal",
  "app_id": "com.alphazed.amal",
  "database": {
    "table_prefix": "amal_"
  },
  "email": {
    "template_dir": "templates/amal",
    "from_address": "amal@alphazed.com",
    "from_name": "Equipe Amal"
  },
  "push_notifications": {
    "firebase_project": "alphazed-amal",
    "apns_certificate": "certs/amal.pem"
  },
  "content": {
    "s3_bucket": "amal-content-production",
    "curriculum_id": "amal_v3_arabic"
  },
  "feature_flags": {
    "enable_tajweed_feedback": false,
    "enable_noorani_qaida": false,
    "enable_juz_amma": false,
    "enable_creature_building": true,
    "enable_physics_games": true
  },
  "pricing": {
    "monthly_usd": 6.99,
    "yearly_usd": 67.99,
    "trial_days": 14
  }
}

Recursos Compartilhados vs Por App

RecursoCompartilhadoPor AppRazão
Código Lambda✓ Sim✗ Não Código único, ramificação via APP_NAME
Instância RDS✓ Sim✗ Não (tabelas com prefixo) Redução de custo, backup único
Conteúdo S3✗ Não✓ Sim Cada app tem seu próprio currículo
AWS Cognito✓ Sim✗ Não (via app_id) Autenticação central, app_id diferencia
Lago de Analytics✓ Sim✗ Não (particionado por app) Pipeline unificado, dados segregados
Mensagens Firebase✗ Não✓ Sim APNs + FCM específicos do app

Frontend: Arquitetura Multi-App Flutter

Core Compartilhado (packages/alphazed_common)

  • Gerenciamento de estado (Riverpod)
  • Manipulação de áudio
  • Biblioteca de animações (integração Rive)
  • Sistema de design (cores, tipografia, widgets)
  • Fluxos de autenticação
  • Cliente de analytics

Camadas Específicas de Apps (apps/amal, apps/thurayya, etc.)

apps/
  ├── amal/
  │   ├── lib/
  │   │   ├── main.dart (ponto de entrada do app)
  │   │   ├── config/
  │   │   │   ├── curriculum.json (estrutura das aulas do Amal)
  │   │   │   ├── colors.dart (tema do Amal)
  │   │   │   └── characters.json (customização de avatar)
  │   │   └── screens/ (telas específicas do Amal)
  │   └── pubspec.yaml (dependências do Amal)
  │
  ├── thurayya/
  │   ├── lib/
  │   │   ├── main.dart (entrada diferente)
  │   │   ├── config/
  │   │   │   ├── curriculum.json (estrutura do Juz Amma)
  │   │   │   ├── colors.dart (tema do Thurayya)
  │   │   │   └── characters.json (customização de avatar)
  │   │   └── screens/ (telas específicas do Thurayya)
  │   └── pubspec.yaml (dependências do Thurayya)
  │
  └── packages/
      └── alphazed_common/ (código compartilhado)

Configuração na Hora da Build

# Build Amal
flutter build apk --dart-define=APP_NAME=amal --dart-define=CURRICULUM=amal_v3

Build Thurayya

flutter build apk --dart-define=APP_NAME=thurayya --dart-define=CURRICULUM=thurayya_juzamma

Na build, cada app recebe seus dados de currículo, cores e configuração incorporados. Um único repositório de código, múltiplos binários compilados.

Implantação: Stacks Independentes

Configuração do Serverless Framework

# serverless.yml
service: alphazed-backend

custom: pythonRequirements: dockerizePip: true app_name: ${env:APP_NAME}

functions:

Funções implantadas para cada valor APP_NAME

get_user: handler: src/handlers/user.get_user events: - http: path: app/{app_name}/user/{user_id} method: get

content_duo: handler: src/handlers/content.generate_content_duo timeout: 20 events: - http: path: app/{app_name}/content_duo method: post

resources: Resources: # Cada app tem seu próprio grupo de logs CloudWatch ${self:custom.app_name}LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: /aws/lambda/alphazed-${self:custom.app_name} RetentionInDays: 30

Ao implantar com APP_NAME=amal, o CloudFormation cria:

  • Funções Lambda prefixadas alphazed-amal-*
  • Rotas API Gateway em /amal/*
  • Logs CloudWatch em /aws/lambda/alphazed-amal
  • Escalabilidade e monitoramento independentes

Por Que Isso É Importante

Para Times de Produto

  • Lançar novo app em semanas, não meses (infrastrutura compartilhada)
  • Paridade de recursos: correções e melhorias aplicadas em todos os apps instantaneamente
  • Teste A/B fácil em um app antes de liberar para os demais

Para Engenharia

  • Código único: um conjunto de testes, uma pipeline CI/CD
  • Algoritmos de aprendizagem compartilhados: repetição espaçada e mistura de conteúdo beneficiam todos os apps
  • Eficiência operacional: um time para toda infraestrutura

Para Custo

  • RDS compartilhado: fração do custo de 7 bancos de dados independentes
  • Cognito compartilhado: um provedor de autenticação para todos apps
  • Analytics unificado para os 7 apps
  • Economia estimada: US$ 40.000-60.000 por ano comparado a backends separados

Desafios e Soluções

Desafio 1: Conflito no esquema do banco de dados
- Amal precisa da tabela amal_user_memory
- Thurayya precisa da tabela thurayya_user_memory (com campos diferentes para memorização do Alcorão)
- Solução: migrações conscientes do app. migrations/001_amal_user_memory.sql roda apenas se APP_NAME=amal

Desafio 2: Diferentes conjuntos de recursos
- Thurayya possui feedback de tajweed; Amal não
- Amal tem jogos de física; Thurayya não
- Solução: flags de recursos na configuração (enable_tajweed_feedback, enable_physics_games)

Desafio 3: Escalabilidade independente
- Amal tem pico de tráfego; Thurayya está calma
- Lambda compartilhado compete pelo limite de concorrência
- Solução: políticas de escalonamento CloudWatch por app. Se Lambdas amal-* atingem limite, escalam automaticamente

Perguntas Frequentes

P: Compartilhar um código só não cria dependência entre apps?
R: Não. O código de cada app fica em diretórios separados (apps/amal, apps/thurayya). O código compartilhado está em packages/alphazed_common, com versionamento explícito. Dependência excessiva seria um problema detectado nas revisões.

P: E se um app precisar de uma mudança pesada na API?
R: Versionamos APIs por app: /amal/v1/*, /thurayya/v1/*. Apps atualizam independentemente. Versões antigas rodam por 12 meses para dar tempo de adaptação.

P: Os apps podem compartilhar usuários?
R: Por padrão não. Cada app tem sua própria tabela de usuários (com prefixo). Se um responsável quiser assinar Amal e Thurayya, cria contas separadas (ou podemos adicionar um recurso de “família” para vincular depois).

Artigos Relacionados