# Clean Architecture

> Aplica os princípios de Clean Architecture (livro de Robert Martin, o Uncle Bob) ao desenvolvimento ADVPL/TLPP — regra de dependência, separação em camadas (domínio, caso de uso, adaptador, framework Protheus), SOLID em TLPP OO e organização de fontes por domínio. Use quando o dev pedir para "organizar em camadas", "desacoplar regra de negócio", "aplicar clean architecture", "aplicar SOLID", "onde colocar essa regra", "separar SQL da regra", "estruturar um desenvolvimento grande" ou quando o brainstorm/plan/reviewer precisar de critério estrutural de design.

- Skill: `tbc-servicos/clean-architecture-2` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add tbc-servicos/clean-architecture-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tbc-servicos/clean-architecture-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: tbc-servicos (https://skillmd.com/u/tbc-servicos)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tbc-servicos/clean-architecture-2

---


# Clean Architecture aplicada a ADVPL/TLPP

Destilação prática dos princípios do livro *Clean Architecture* para o ecossistema Protheus.
O objetivo não é academicismo: é que a **regra de negócio do cliente sobreviva** a troca de
tela, de banco, de release e de dev — e seja **testável por unidade, sem subir tela nem
depender de massa de dados**.

> Skills irmãs: `/protheus:ddd` (modelagem do domínio — *o que* construir),
> esta skill (estrutura — *como organizar*), `/protheus:migrate` (procedural → TLPP OO) e
> `/protheus:refactor-method-complexity-reduce` (extração de métodos em função complexa).

## A Regra de Dependência (o coração do livro)

**Dependências de código apontam sempre para dentro — da infraestrutura para o domínio,
nunca o contrário.** A regra de negócio não conhece tela, endpoint, tabela nem framework.

```
[ REST @Get/@Post · ModelDef/ViewDef · Ponto de Entrada · Schedule ]   ← entrada/saída
        ↓ chama
[ Caso de uso: orquestra, valida entrada, controla transação ]        ← aplicação
        ↓ chama                     ↓ chama
[ Regra de negócio pura ]    [ Repositório / ExecAuto wrapper ]        ← domínio · persistência
   (sem SQL, sem tela)          (todo SQL/RecLock vive AQUI)
```

Teste rápido de violação: **se a função tem `BeginSQL`/`RecLock` E um cálculo de negócio
E monta JSON/tela, ela está em três camadas ao mesmo tempo** — dividir.

## Mapa de camadas → artefatos Protheus

| Camada (livro) | No Protheus | Regra |
|---|---|---|
| Entidades / regras de negócio | Classes TLPP ou Static Functions **puras**: cálculo, validação, decisão | Zero SQL, zero tela, zero `RecLock`. Recebe dados, devolve resultado. Filial/parâmetros (`SuperGetMV`) entram **por argumento**, não são lidos dentro |
| Casos de uso | Uma classe/função de aplicação por operação (`IncluirPedido`, `AprovarDesconto`) | Orquestra: valida entrada → chama regra → chama repositório/ExecAuto → devolve resultado tipado. É a **única** camada que controla transação (`BeginTran`) |
| Adaptadores de interface | Endpoint REST TLPP, `ModelDef`/`ViewDef`, Ponto de Entrada, job/Schedule | Só traduz: parseia entrada, chama o caso de uso, formata a resposta. **PE nunca contém a regra** — delega para função externa (regra que o plugin já exige) |
| Frameworks & drivers | Dicionário SX*, FWFormModel, ExecAuto, DBAccess, BeginSQL | Acessados somente via repositórios/wrappers. `ExecAuto` é a "porta oficial" de escrita em tabela padrão — nunca `RecLock` direto em SA1/SC5/etc. |

## SOLID em TLPP — resumo operacional

- **S**RP — um fonte/classe/função = um motivo para mudar. Função > ~60 linhas ou que mistura
  camadas → extrair (a skill `/protheus:refactor-method-complexity-reduce` automatiza a
  extração). Detalhes e exemplos: `references/solid-tlpp.md`.
- **O**CP — variações de comportamento por **classe/bloco injetado**, não por `Do Case` de tipo
  espalhado em N funções.
- **L**SP — subclasse TLPP honra o contrato da base (mesmos pré/pós-requisitos; não "lança erro
  se for do tipo X").
- **I**SP — interfaces TLPP pequenas por papel (`ICalculaFrete`), não uma `IUtils` gorda.
- **D**IP — caso de uso depende de **interface** de repositório; a implementação concreta
  (BeginSQL/ExecAuto) é injetada no construtor. É isso que permite testar a regra com dublê.

## Organização de fontes (screaming architecture)

O diretório grita **o domínio**, não a tecnologia: agrupe por assunto de negócio
(`faturamento/`, `estoque/`), não por tipo (`apis/`, `queries/`, `telas/`). Dentro de cada
assunto, os sufixos de camada dizem o papel: `*Service` (caso de uso), `*Repo` (persistência),
regra pura sem sufixo. A nomenclatura de arquivo segue a convenção do projeto; a organização
é por pasta e namespace TLPP (`namespace cliente.faturamento`).

## Quando aplicar (pragmatismo)

| Tamanho da mudança | Estrutura mínima | Orçamento de fontes (fora os de teste) |
|---|---|---|
| PE trivial, ajuste de 1 função | Só a regra de sempre: PE delega para User Function | 1 |
| Rotina nova média | Separar ao menos **regra pura** × **acesso a dados** (2 funções/classes) — o suficiente para testar a regra por unidade, sem banco | até 3 |
| Desenvolvimento grande (módulo, integração) | Camadas completas: adaptador → caso de uso → domínio + repositórios, 1 namespace por contexto | 1 fonte por camada por contexto; acima disso, justifique fonte a fonte no plano |

Sobre-engenharia também é dívida: **não** crie interface + repositório + service para
encapsular um `Posicione()`. O critério é: a regra de negócio merece viver isolada e testada.

### Camada ≠ arquivo (o SOLID não é para virar enxurrada de fontes)

Um fonte gigante é problema; **vinte fontes minúsculos também são** — cada `.tlpp` entra no
patch, precisa compilar, versionar e ser conferido na documentação de customizações. Separe
por **motivo de mudança**, não por contagem de classes.

- **TLPP aceita mais de uma classe no mesmo fonte** (verificado em código compilado em
  produção: adapter + service convivendo no mesmo arquivo, sob o mesmo `namespace`). Classes
  coesas — mesmo papel, mesmo contexto, que mudam pelo mesmo motivo — ficam juntas.
- **Piso:** classe pequena que existe só "para ficar SOLID" volta para o fonte do seu papel.
  Interface com uma implementação só: só crie se o teste precisa do dublê.
- **Teto:** fonte passando de ~800 linhas, ou classe de ~500, é sinal de divisão real.
- **A lista de fontes é fechada no `/protheus:plan`.** Fonte que não está no plano não é
  criado na implementação — se apareceu a necessidade, ela é decisão de design e volta ao
  `/protheus:brainstorm`.

> Os números são ponto de partida calibrado em desenvolvimento real. Ajuste no seu projeto se
> o padrão local for outro — mas mantenha um teto **declarado**, não "o quanto sair".

## Fluxo de uso

1. **No design** (`/protheus:brainstorm` / `/protheus:plan` / `/protheus:advpl-tlpp-sdd`):
   para cada caso de uso da especificação, defina os artefatos por camada (tabela acima)
   antes de codar. Liste que regra é pura (testável por unidade) e que acesso a dados vira
   repositório/ExecAuto.
2. **Na implementação** (`/protheus:writer` / `/protheus:implement`): siga
   `references/regra-dependencia-camadas.md` (esqueleto de cada camada com código) e valide
   contra os sinais de violação.
3. **No review** (`/protheus:reviewer` / `/protheus:code-review`): aplique o checklist
   estrutural de `references/refatoracao-exemplo.md` — o exemplo before/after mostra o
   monólito típico (endpoint que faz tudo) e a versão em camadas.
4. **Testes**: a recompensa da separação — regra pura testada por unidade (ex.: PROBAT, o
   framework de testes do tlppCore) sem fixture de banco; o E2E de tela fica com
   `/protheus:tir-test-generator`.

## Regras inegociáveis

- Regra de negócio **nunca** contém `BeginSQL`, `RecLock`, `MsMessage` ou montagem de JSON.
- Endpoint REST / PE / ViewDef **nunca** acessa tabela diretamente — sempre via caso de uso.
- Escrita em tabela padrão TOTVS = **ExecAuto** (encapsulado em repositório), nunca `RecLock`
  direto (pula validações do dicionário).
- Transação (`BeginTran`/`EndTran`) pertence ao caso de uso — nem à regra, nem ao repositório.
- Todo caso de uso novo nasce com teste unitário da(s) regra(s) pura(s) que orquestra.

