ADR-002: Estratégia Contracts-First
Status: Aceito
Data: 2024-11-02
Decisores: Aron Cardoso
Contexto
Com arquitetura monorepo, módulos precisam interagir de forma desacoplada mesmo estando no mesmo repositório. Precisávamos definir como garantir que interfaces permaneçam estáveis enquanto implementações evoluem.
Forças em jogo:
- Estabilidade de APIs: Evitar breaking changes frequentes
- Evolução independente: Módulos devem poder mudar internamente
- Testabilidade: Contratos facilitam mocks e testes
- Documentação viva: Interfaces são documentação executável
Alternativas Consideradas
Opção 1: Implementation-First
- Prós:
- Desenvolvimento mais rápido inicialmente
- Menos overhead de planejamento
- Flexibilidade para experimentar
- Contras:
- Interfaces instáveis (mudanças frequentes)
- Dificulta testes unitários
- Acoplamento entre módulos
- Breaking changes inesperados
Opção 2: Contracts-First (Escolhida)
- Prós:
- Interfaces estáveis e versionadas
- Facilita TDD (Test-Driven Development)
- Documentação clara de dependências
- Previne acoplamento acidental
- Contras:
- Planejamento inicial maior
- Overhead de manter contratos e implementações
- Pode retardar prototipagem rápida
Decisão
Adotamos Contracts-First com as seguintes práticas:
Definição prévia de interfaces:
- Toda nova feature começa com definição de contratos
- Contratos revisados antes de implementação
- Versionamento semântico rigoroso
Pacote
packages/contractscontém:- Interfaces PHP (
*Interface) - DTOs imutáveis
- Eventos globais
- Enums compartilhados
- Interfaces PHP (
Evolução controlada:
- Breaking changes apenas em major versions
@deprecatedcom pelo menos uma minor version de antecedência- Changelog detalhado de contratos
Implementações desacopladas:
- Módulos dependem apenas de contratos
- Nenhuma dependência direta entre módulos de domínio
- Comunicação via eventos do contrato
Consequências
Positivas
- Previsibilidade: Módulos sabem exatamente o que esperar de dependências
- Facilita testes: Mocks baseados em interfaces são simples
- Evolução segura: Breaking changes são explícitos e versionados
- Documentação clara: Interfaces documentam comportamento esperado
- Permite TDD: Testes podem ser escritos antes da implementação
Negativas
- Planejamento prévio: Requer pensar na interface antes de implementar
- Rigidez inicial: Mudanças em contratos têm alto custo
- Overhead de manutenção: Duas bases de código (contratos + implementação)
- Pode ser excessivo: Para features muito simples, pode ser overkill
Neutras
- Necessita disciplina: Time precisa seguir processo religiosamente
- Requer versionamento cuidadoso: SemVer estrito é essencial
Notas de Implementação
Estrutura de Contratos
text
packages/contracts/src/
├─ Payments/
│ ├─ PaymentGatewayInterface.php
│ ├─ TaxCalculatorInterface.php
│ └─ DTOs/
│ └─ PaymentDTO.php
├─ People/
│ ├─ PersonRepositoryInterface.php
│ └─ Events/
│ ├─ PersonCreated.php
│ └─ PersonUpdated.php
└─ Communications/
└─ NotificationChannelInterface.phpPadrões de Naming
- Interfaces:
*Interfacesuffix - DTOs:
*DTOsuffix ou nome descritivo - Eventos: Past tense (
PersonCreated, nãoPersonCreate)
Processo de Mudança
- Propor mudança via issue ou discussão
- Discutir impacto em módulos dependentes
- Implementar com
@deprecatedse breaking - Atualizar documentação de compatibilidade
- Módulos atualizam no mesmo commit (vantagem do monorepo)