Skip to content

ADR-001: Arquitetura Monorepo

Status: Aceito

Data: 2025-11-03

Decisores: Aron Cardoso

Contexto

O Filament Core Suite é composto por múltiplos módulos (Contracts, Invoices, People, Communications) que compartilham interfaces, eventos e DTOs. Precisávamos decidir entre:

  • Manter todos os módulos em um único repositório (monorepo)
  • Separar cada módulo em seu próprio repositório (multi-repo)

Forças em jogo:

  • Refatorações cross-module: Mudanças que afetam múltiplos módulos devem ser atômicas
  • Simplificação de desenvolvimento: Setup local deve ser simples e rápido
  • Versionamento coordenado: Mudanças em contratos devem ser sincronizadas imediatamente
  • Consistência de ferramentas: Configurações de linting, testes e CI/CD devem ser centralizadas

Alternativas Consideradas

Opção 1: Multi-Repo

  • Prós:
    • Versionamento independente (SemVer por módulo)
    • Deploy isolado por módulo
    • CI/CD mais rápido (apenas módulo afetado)
    • Ownership claro por repositório
    • Facilita open source seletivo
  • Contras:
    • Sincronização manual de mudanças cross-module
    • Duplicação potencial de configs
    • Complexidade de desenvolvimento local
    • Quebras inesperadas em dependências de contratos

Opção 2: Monorepo (Escolhida)

  • Prós:
    • Refatorações cross-module atômicas
    • Todas as dependências em um só lugar
    • Commits atômicos afetando múltiplos módulos
    • Ferramentas de monorepo maduras (Composer workspaces)
    • Setup local simplificado (um único clone)
    • Configurações centralizadas (PHPStan, Pint, Pest)
  • Contras:
    • CI/CD precisa detectar mudanças por módulo
    • Requer disciplina para evitar acoplamento entre módulos

Decisão

Optamos por arquitetura monorepo com as seguintes características:

  1. Estrutura de diretórios por módulo:

    text
    packages/
    ├── contracts/          # Interfaces, DTOs, eventos
    ├── invoices/           # Módulo de faturas
    ├── people/             # Módulo de pessoas
    └── communications/     # Módulo de comunicações
  2. Composer Workspaces:

    • Cada módulo é um pacote Composer independente
    • composer.json raiz define workspaces
    • Autoload e dependências gerenciadas centralmente
  3. Namespace isolado por módulo:

    • FilamentCore\Contracts\*
    • FilamentCore\Invoices\*
    • FilamentCore\People\*
    • FilamentCore\Communications\*
  4. Estratégia de comunicação:

    • Módulos dependem apenas de packages/contracts
    • Sem dependências diretas entre módulos de domínio
    • Comunicação via eventos globais definidos em Contracts
  5. CI/CD inteligente:

    • Detecta mudanças por módulo (git diff)
    • Executa testes apenas dos módulos afetados
    • Build e deploy seletivo por módulo

Consequências

Positivas

  • Refatorações atômicas: Mudanças cross-module em um único commit
  • Setup simplificado: Um único git clone e composer install
  • Consistência garantida: Contratos e implementações sempre sincronizados
  • Compartilhamento de código: Helpers e traits facilmente reutilizáveis
  • Ferramentas centralizadas: PHPStan, Pint, Pest configurados uma vez
  • Histórico unificado: Changelog e git log mostram evolução completa

Negativas

  • CI/CD mais complexo: Precisa detectar mudanças por módulo
  • Potencial de acoplamento: Requer disciplina para manter módulos isolados
  • Repositório maior: Clone inicial mais pesado (mitigado com shallow clone)

Neutras

  • Versionamento: Ainda é possível versionar módulos independentemente usando tags
  • Deploy: Pode ser feito por módulo usando estratégias seletivas

Notas de Implementação

Estrutura do Composer Raiz

json
{
  "name": "filament-core/monorepo",
  "type": "project",
  "require": {
    "php": "^8.2",
    "laravel/framework": "^12.0"
  },
  "autoload": {
    "psr-4": {
      "FilamentCore\\Contracts\\": "packages/contracts/src/",
      "FilamentCore\\Invoices\\": "packages/invoices/src/",
      "FilamentCore\\People\\": "packages/people/src/",
      "FilamentCore\\Communications\\": "packages/communications/src/"
    }
  },
  "extra": {
    "laravel": {
      "providers": [
        "FilamentCore\\Invoices\\InvoicesServiceProvider",
        "FilamentCore\\People\\PeopleServiceProvider",
        "FilamentCore\\Communications\\CommunicationsServiceProvider"
      ]
    }
  }
}

Regras de Isolamento

  • Módulos de domínio (invoices, people, communications) NÃO PODEM importar classes diretamente de outros módulos
  • Apenas packages/contracts pode ser importado por todos
  • Comunicação entre módulos APENAS via eventos

CI/CD Seletivo

yaml
# .github/workflows/test.yml
- name: Detect changed packages
  run: |
    CHANGED=$(git diff --name-only HEAD~1 | grep -E '^packages/([^/]+)/' | cut -d/ -f2 | sort -u)
    echo "CHANGED_PACKAGES=$CHANGED" >> $GITHUB_ENV

- name: Test changed packages
  run: |
    for pkg in $CHANGED_PACKAGES; do
      vendor/bin/pest packages/$pkg/tests
    done

Versionamento

  • Tags Git por módulo: invoices/v1.2.3, people/v2.0.0
  • Changelog por módulo em packages/<module>/CHANGELOG.md
  • Releases independentes usando GitHub Releases com tags específicas

Referências

Documentação privada do ecossistema Filament Core.