Transaction Script
Definição (Fowler)
Seção intitulada “Definição (Fowler)”“Organizes business logic by procedures where each procedure handles a single request from the presentation.”
A lógica não vive em objetos de domínio, mas em métodos procedurais. Os dados normalmente vêm de estruturas anêmicas (records, DTOs) ou diretamente do banco via Gateway/DAO - Data Access Object.
Problema que resolve
Seção intitulada “Problema que resolve”Boa parte das aplicações corporativas tem lógica rasa: validar, calcular um total, gravar. Introduzir um Domain Model rico com mapeamento O/R para isso é caro e cerimonioso. O Transaction Script entrega valor com o mínimo de estrutura.
Quando usar
Seção intitulada “Quando usar”- CRUDs e telas administrativas.
- Regras de negócio simples e estáveis, com pouca interdependência.
- Times pequenos, prazos curtos, domínio pouco complexo.
- Protótipos e MVPs.
Trade-offs
Seção intitulada “Trade-offs”| Vantagem | Custo |
|---|---|
| Baixíssimo custo inicial | Duplicação de lógica entre scripts |
| Fácil de entender linearmente | Condicionais aninhados crescem |
| Sem ferramental O/R pesado | Testabilidade sofre (lógica acoplada a IO) |
| Ótimo para lógica rasa | Curva de esforço cresce de forma não linear |
Quando NÃO usar
Seção intitulada “Quando NÃO usar”Quando as regras começam a se entrelaçar e a mesma lógica reaparece em vários scripts. Esse é o sinal de que o custo de manutenção do Transaction Script está superando o do Domain Model. Persistir nele nesse ponto leva ao acoplamento e à duplicação crônica.
Contraste explícito com Domain Model
Seção intitulada “Contraste explícito com Domain Model”O Domain Model distribui a lógica entre objetos que combinam dados + comportamento, controlando invariantes localmente. O Transaction Script centraliza a lógica em procedimentos e trata dados como estruturas passivas. A diferença essencial:
graph TD
subgraph "Transaction Script"
TS[transferir - método] --> C1[Conta - dados]
TS --> C2[Conta - dados]
TS --> DB[(DB)]
end
subgraph "Domain Model"
A[Conta - dados+comportamento] -->|debitar| A
B[Conta - dados+comportamento] -->|creditar| B
end
Exemplo Java/Spring
Seção intitulada “Exemplo Java/Spring”public record TransferenciaRequest(Long contaOrigemId, Long contaDestinoId, BigDecimal valor) {}
@Servicepublic class TransferenciaService {
private final JdbcTemplate jdbc;
public TransferenciaService(JdbcTemplate jdbc) { this.jdbc = jdbc; }
@Transactional public void transferir(TransferenciaRequest req) { if (req.valor() == null || req.valor().signum() <= 0) { throw new IllegalArgumentException("Valor deve ser positivo"); }
BigDecimal saldoOrigem; try { saldoOrigem = jdbc.queryForObject( "SELECT saldo FROM conta WHERE id = ?", BigDecimal.class, req.contaOrigemId()); } catch (EmptyResultDataAccessException e) { throw new IllegalStateException("Conta de origem inexistente"); } if (saldoOrigem.compareTo(req.valor()) < 0) { throw new IllegalStateException("Saldo insuficiente"); }
jdbc.update("UPDATE conta SET saldo = saldo - ? WHERE id = ?", req.valor(), req.contaOrigemId()); jdbc.update("UPDATE conta SET saldo = saldo + ? WHERE id = ?", req.valor(), req.contaDestinoId());
jdbc.update( "INSERT INTO movimento (conta_id, tipo, valor) VALUES (?, 'DEBITO', ?)", req.contaOrigemId(), req.valor()); jdbc.update( "INSERT INTO movimento (conta_id, tipo, valor) VALUES (?, 'CREDITO', ?)", req.contaDestinoId(), req.valor()); }}Toda a lógica (validação, checagem de saldo, débito, crédito, registro) está no procedimento transferir. Repare que a regra “saldo não pode ficar negativo” mora aqui — se outra operação também mexer no saldo, a regra terá de ser repetida.
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Duplicação de regra: a mesma validação copiada em vários scripts. Sinal para migrar rumo ao Domain Model.
- Método-monólito: um script de centenas de linhas com muitos
ifaninhados. Fere o SRP - Single Responsibility Principle. - Insistir no Transaction Script quando o domínio já ficou complexo — custo de manutenção explode.
- Lógica de negócio no Controller: o script deve estar num serviço, não na camada web.
- Confundir com Service Layer: um Service Layer sobre um Domain Model delega às entidades; um Transaction Script contém a regra ele mesmo.
Relações
Seção intitulada “Relações”- Contraste central com Domain Model.
- Pode expor casos de uso via Service Layer (opcionalmente).
- Acessa dados via DAO - Data Access Object ou Gateway.
- Relaciona-se a Code Smells quando a duplicação se acumula.
- Índice: PoEAA.
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. Em uma frase, o que caracteriza o Transaction Script?
Resposta
Um procedimento por caso de uso que concentra a lógica de negócio de forma linear, tratando dados como estruturas passivas.
2. Qual é o principal sintoma de que um Transaction Script está “vencido”?
Resposta
Duplicação de lógica entre scripts e crescimento de condicionais aninhados. Isso indica que o domínio ficou complexo o suficiente para justificar um Domain Model.
3. Por que o custo do Transaction Script cresce de forma não linear?
Resposta
Porque, sem objetos que encapsulem invariantes, cada nova regra tende a ser espalhada e repetida entre os scripts existentes, multiplicando os pontos de manutenção e as chances de inconsistência.
4. Quando o Transaction Script é a escolha correta?
Resposta
Em domínios de lógica rasa e estável: CRUDs, telas administrativas, MVPs e protótipos, onde o custo de um Domain Model com mapeamento O/R não se paga.