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:
Estrutura de diretórios por módulo:
textpackages/ ├── contracts/ # Interfaces, DTOs, eventos ├── invoices/ # Módulo de faturas ├── people/ # Módulo de pessoas └── communications/ # Módulo de comunicaçõesComposer Workspaces:
- Cada módulo é um pacote Composer independente
composer.jsonraiz define workspaces- Autoload e dependências gerenciadas centralmente
Namespace isolado por módulo:
FilamentCore\Contracts\*FilamentCore\Invoices\*FilamentCore\People\*FilamentCore\Communications\*
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
- Módulos dependem apenas de
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 cloneecomposer 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/contractspode 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
doneVersionamento
- 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