# ADR-013 — DDD-lite de 4 camadas com fronteira física

| Campo | Valor |
|---|---|
| **Status** | Aceito (2026-10-16) |
| **Data** | 2026-10-14 |
| **Plano de origem** | `wiki/plan/01-analise-e-decisoes.md` |
| **Relacionadas** | ADR-003, ADR-015, ADR-023, ADR-016, ADR-033, ADR-034 |

## Contexto

O `cms/` usa DDD na raiz com resultados bons; o `sucesu-cms` acumulou **828 arquivos sem
fronteira rígida**, gerando código órfão (ex.: `ExcelImporter`) e exceções não documentadas.
Convenção sem verificação erode. A comunicação entre módulos precisa ser mediada.

## Decisão

Quatro camadas com papéis fixos:

- `src/core`: invisível ao usuário — auditoria, observabilidade, bus, permissões (ADR-033),
  ativação (ADR-034), descoberta e contratos universais/portas. É o **fundamento**.
- `src/services`: headless, sem UI, **sempre disponíveis** (ADR-015): contrato no próprio service +
  `ServiceProvider.php` absorvido pelo Core (Media, SEO, Security, RichText, Audit…).
- `src/layout`: estrutura de front-end — slots, presets, tema e componentes.
- `src/modules`: caso de uso autocontido, sustentado pelo Core e consumindo services.

**Fundamento:** o Core é o fundamento; os services flutuam sobre a implementação; os módulos são os
casos de uso (ADR-015).

**Regra dura:** `modules/*` **não** importa `modules/*` (comunica-se por eventos/ports do Core);
`core` **não** importa `modules` nem `services` (a descoberta de service é por convenção/string);
`services` **não** importa `modules`. Violação = build vermelho (`make check`).
Vale para **widgets/banners de outros módulos**: o módulo de origem registra um provider no
`WidgetRegistry` do Core e o perfil de destino só referencia o `id` — nunca import.

## Justificativa (alternativas descartadas)

- **`app/` único com convenções** — descartada: foi o que erodiu no legado.
- **Somente eventos (sem ports)** — descartada: há fluxos síncronos (ex.: render de widget)
  que precisam de contrato direto.
- **Acoplamento direto entre módulos** — descartada: impede remoção/evolução independente.

## Consequências

**Positivas**
- Fronteira testável por AST (`tools/arch-check`), não por disciplina.
- Módulos plugáveis: criar/remover sem tocar no Core.

**Custos / negativas**
- Exige declarar portas/eventos (um pouco mais de cerimônia pontual).
- Widgets cross-módulo exigem o registry do Core.

## Exemplo mínimo (comparativo)

```php
// ❌ Legislação importando Notícias (proibido)
use Modules\Noticias\Database\Models\Noticia;
$ultimas = Noticia::latest()->take(5)->get();

// ✅ Porta do Core: Notícias registra, Legislação consome o contrato
// Core\Contracts\Ports\UltimasNoticias
interface UltimasNoticias { public function recentes(int $limite): array; }
// Modules\Publicacoes\Noticias\ModuleProvider: Core::port(UltimasNoticias::class, NoticiaUltimas::class);
// Modules\...\Legislacao\...: fn (UltimasNoticias $port) => $port->recentes(5)
```

## Verificação

- `make check` falha ao importar `Modules\Noticias` dentro de `Modules\Legislacao`.
- Teste de contrato por porta registrada.

## Referências

- `prefeituras_v5/.agents/rules/domain_isolation.md`; `wiki/plan/07-core-contratos-discovery.md`.
