CS Coder
Arquitetura

Tela, serviço e domínio: separe decisões sem transformar tudo em camadas

Use uma política de limite de itens para distinguir entrada visual, regra e coordenação, com um exemplo independente de VCL, FMX e UniGUI.

· 4 min de leitura
Objetivo e formato

Extrair uma decisão de negócio e identificar onde a persistência deve ser coordenada

Tutorial · prática orientada · Intermediário
Antes de experimentar

Units, funções e contratos de entrada e saída

Unit principal compilada e exercitada com DCC32 37.0, build 37.0.59082.6021. Referência editorial: Delphi 11.3 e 13.2; essas duas versões não foram verificadas individualmente.

Documentação, instalação e limites de Fundamentos em seis units

O pedido do usuário é simples: adicionar itens a uma lista. O evento da tela lê um campo, verifica um limite, salva no banco e mostra o resultado. Tudo funciona até a mesma regra precisar atender uma importação ou uma API.

Separar responsabilidades começa por localizar as decisões. Não é preciso criar uma arquitetura extensa para reconhecer o que é apresentação e o que deve continuar igual sem aquela tela.

Defina a política sem nomes de componentes

Neste exercício, uma lista possui uma quantidade atual e um limite máximo. Uma adição é aceita se todas as quantidades forem válidas e houver espaço. Quantidade a adicionar deve ser positiva; quantidade atual e limite não podem ser negativos.

Essa é uma política didática de contagem, não uma regra real de estoque. Uma aplicação comercial pode exigir reserva, disponibilidade e autorização adicionais.

Coloque a decisão em uma unit independente

Crie Lista.Politica.pas:

delphi
unit Lista.Politica;

interface

function PodeAdicionar(const Atual, Quantidade,
  Limite: Integer): Boolean;

implementation

uses
  System.SysUtils;

function PodeAdicionar(const Atual, Quantidade,
  Limite: Integer): Boolean;
begin
  if (Atual < 0) or (Quantidade <= 0) or (Limite < 0) then
    raise EArgumentException.Create('Contagem inválida');
  if Atual > Limite then
    Exit(False);
  Result := Quantidade <= Limite - Atual;
end;

end.

A comparação usa a diferença disponível em vez de somar duas entradas antes de verificar o limite. Isso evita um overflow da soma nesse contrato, já que a subtração acontece após confirmar valores não negativos e Atual <= Limite.

O retorno False significa que não cabe. Uma entrada fora do contrato gera exceção. Esses caminhos precisam ser documentados para que a apresentação não confunda falta de espaço com um defeito na entrada.

A tela traduz; a regra decide

A apresentação converte o texto digitado, aponta o campo inválido e mostra a mensagem. Ela não deve copiar a fórmula para decidir antes de chamar a unit e depois esquecer de chamar a regra no caminho de gravação.

Uma validação visual pode antecipar feedback. A decisão confiável ainda precisa ocorrer no ponto em que a operação é aplicada. Num sistema web, não confie em um valor de quantidade atual informado pelo navegador.

O caso de uso coordena uma operação

Se adicionar itens altera uma base de dados, alguém deve obter a contagem atual, verificar autorização, aplicar a política e persistir de forma consistente. Esse trabalho de coordenação pode pertencer a um serviço de aplicação ou caso de uso.

text
Apresentação → Caso de uso → Política
                    ↓
             Persistência autorizada e consistente

A política não abre uma conexão nem escolhe uma mensagem de botão. O serviço não precisa conhecer TEdit ou o formulário principal. A infraestrutura implementa os detalhes de acesso ao banco.

Se duas operações leem a mesma contagem simultaneamente, ambas podem concluir que há espaço. A regra pura não elimina essa corrida. Use uma transação e um mecanismo de controle de concorrência adequado ao banco e ao contrato da operação.

Teste a decisão por exemplos

Atual Adicionar Limite Expectativa
2 3 5 Aceita
2 4 5 Rejeita por capacidade
5 1 5 Rejeita por capacidade
0 1 0 Rejeita por capacidade
2 0 5 Entrada inválida
-1 1 5 Entrada inválida

Esses casos foram executados no projeto de verificações; confira o relatório incluído no download. Verifique também contagens próximas de High(Integer).

Quando a separação está ajudando

Uma boa fronteira permite alterar a mensagem sem mudar a política e usar a mesma política numa operação sem tela. Uma separação ruim exige atravessar vários arquivos que apenas repassam chamadas sem explicar nenhuma responsabilidade.

As estruturas de units e interfaces usadas aqui seguem os mecanismos da linguagem descritos no guia oficial Delphi. A escolha das fronteiras é uma proposta de desenho, não uma imposição da linguagem.

Como exercício, substitua o limite fixo por uma política recebida pelo caso de uso e descreva quem tem permissão para alterá-la. Continue em Interfaces na prática para decidir quando um contrato de dependência é útil.

Desafio: capacidade, sem trocar a regra

  1. PodeAdicionar(2,3,5): True, capacidade exata.
  2. PodeAdicionar(2,4,5): False, excede limite.
  3. Quantidade zero ou atual negativo: EArgumentException.
  4. Valores próximos de High(Integer): evitar overflow.

Entrada → conversão → caso de uso → política → coordenação da persistência → saída. A função pura não impede duas gravações concorrentes. Autorização, transação e conflito pertencem à coordenação; banco e ensaio de persistência ficam adiados por decisão do responsável. Fonte e testes.

Agora é com você

Sua regra continuaria funcionando se a tela fosse substituída por uma API?

Teste capacidade exata (2,3,5), capacidade excedida (2,4,5), quantidade zero e valores próximos de High(Integer). Separe política de capacidade da mensagem visual.

Ver uma pista para começar

A regra decide capacidade. A interface converte entradas. Concorrência e persistência precisam de coordenação própria.

Não precisa acertar de primeira. Observe o resultado e explique o que mudou.

Leitura relacionada

Outra conexão para investigar

Tire cálculo e persistência dos eventos sem criar camadas vazias. Use uma função independente como primeiro passo para um formulário mais claro.

Formulários UniGUI menores: organize o evento em torno de um caso de uso ↗

4 min de leitura

Abrir projeto de exemplo ↗