Ir para o conteúdo
DedicatedPHP Contato

Como projetar um estado de revisão para dados incertos em uma aplicação PHP

Projete uma revisão humana rastreável em PHP: separe o dado recebido da decisão, defina transições e controle permissões, concorrência e correções.

Diagrama conceitual de um registro recebido, uma proposta e uma decisão humana em um fluxo de revisão em PHP

Quando uma aplicação recebe um dado ambíguo, forçá-lo a uma resposta de «válido» ou «inválido» costuma ocultar o problema em vez de resolvê-lo. Isso pode acontecer com formulários, integrações ou resultados propostos por um sistema automatizado: falta contexto para aceitar ou descartar o registro com segurança. Nesses casos, a revisão humana de dados incertos em PHP deve ser um estado do domínio, com regras explícitas, não um rótulo informal nem uma fila improvisada.

O design precisa responder a quatro perguntas: o que foi recebido, o que está sendo proposto, quem deve decidir e o que acontece depois. Separar essas responsabilidades facilita a operação do fluxo, a correção de erros e a explicação de por que um dado foi processado de determinada maneira.

Classifique o problema antes de criar estados

Classifique o problema antes de criar estados — guía visual de DedicatedPHP

Nem todos os dados que não podem avançar precisam de revisão humana. Diferencie, pelo menos, estas situações:

  • Inválido: não atende a uma regra verificável, como um formato que não pode ser interpretado. Cabe rejeitá-lo ou solicitar uma correção, conforme a origem e o produto.
  • Incompleto: falta um campo necessário. A ação pode ser solicitar o dado, conservar o rascunho ou aguardar uma fonte adicional.
  • Incerto: há várias interpretações plausíveis ou as evidências não permitem decidir automaticamente. Pode ser necessária a avaliação de uma pessoa.

A revisão não deve se tornar o destino genérico de todo erro. Se um valor não atende a uma restrição inequívoca, enviá-lo para uma fila de revisão apenas acrescenta trabalho. Por outro lado, quando a decisão depende de contexto ou critério, um estado revisável permite interromper o processamento sem declarar prematuramente que o dado está correto ou incorreto.

Também convém diferenciar esse fluxo de uma quarentena de importação. A quarentena costuma isolar entradas que não devem ser incorporadas ao sistema, por exemplo, porque falharam em controles técnicos ou de segurança. A revisão resolve uma ambiguidade de negócio. Um mesmo registro pode passar pelos dois mecanismos, mas seus objetivos, permissões e critérios de saída não são intercambiáveis.

Defina um ciclo de vida pequeno e explícito

Comece pelos estados necessários para descrever o processo, não por uma lista de todas as possibilidades futuras. Um ciclo básico poderia ser:

  • pending_review: aguarda uma decisão.
  • confirmed: uma pessoa confirma um valor utilizável.
  • rejected: a pessoa determina que o dado não deve ser processado.
  • reopened ou uma nova entrada em revisão: uma decisão anterior volta a ser questionada.

Defina as transições permitidas. Por exemplo, um caso pendente pode ser confirmado ou rejeitado; um caso já resolvido só pode ser reaberto sob condições autorizadas. Não permita que uma solicitação qualquer altere o estado diretamente para qualquer valor. Uma transição deve especificar o estado de origem, o estado de destino, o ator permitido e os efeitos associados.

Armazene o motivo da decisão e a identidade da pessoa responsável. Se um dado for rejeitado, registre um motivo útil, não apenas um valor booleano. Se uma interpretação for confirmada, preserve qual valor foi aprovado e, quando pertinente, quais evidências fundamentaram a decisão. Defina se o motivo é obrigatório conforme a ação e evite incluir informações pessoais desnecessárias em notas livres.

Separe entrada, proposta e valor confirmado

Um design robusto não sobrescreve o dado recebido com a interpretação de um processo automático nem com a correção de uma pessoa. Mantenha conceitos distintos:

  • Dado recebido: a entrada original e sua procedência, com os controles adequados de acesso e retenção.
  • Interpretação proposta: uma sugestão gerada por regras, uma integração ou um sistema assistido, junto com as informações necessárias para avaliá-la.
  • Valor confirmado: o valor que a aplicação utiliza após a decisão autorizada.

Essa separação evita que uma proposta seja tratada acidentalmente como verdade e permite reconstruir como se chegou ao valor atual. Não é necessário duplicar indiscriminadamente cada campo: conserve os dados necessários para auditoria e suporte, aplicando políticas de retenção e proteção de dados. Se uma proposta incluir uma pontuação ou explicação automatizada, trate-a como contexto, não como decisão.

Registre as alterações como eventos ou entradas de histórico que não podem ser alterados pelas operações habituais da aplicação: identificador do caso, ação, estado anterior e novo, ator, data e motivo. O modelo também pode manter o estado atual para facilitar a consulta da fila. A correção é representada por uma nova decisão ou reabertura vinculada ao histórico; excluir a decisão anterior destrói a rastreabilidade.

Implemente as decisões na camada de domínio

Em PHP, encapsule as transições em um serviço de aplicação ou em uma entidade de domínio, em vez de confiar que cada controlador aplique as mesmas regras. Um exemplo esquemático:

final class ReviewDecisionService
{
    public function confirm(ReviewCase $case, User $actor, string $value, string $reason): void
    {
        if (!$actor->canReview($case)) {
            throw new DomainException('Acesso não permitido');
        }

        if (!$case->isPending()) {
            throw new DomainException('O caso não está pendente');
        }

        $case->confirm($value, $actor->id(), $reason);
    }
}

O trecho ilustra onde posicionar as regras, não uma implementação completa. Valide também o formato e os limites do valor, verifique se o ator tem autorização para aquele caso e registre a decisão. Não confunda autenticação — quem é o usuário — com autorização — o que ele pode fazer naquele registro. Se houver papéis ou separação de responsabilidades, expresse essas restrições como regras do produto.

Quando a atualização do caso e a gravação do histórico precisarem ser coerentes, armazene-as em uma transação de banco de dados. Se a decisão disparar efeitos externos, como uma chamada a outra API, não presuma que a transação local também abrange esse sistema. Use um mecanismo de entrega confiável, como uma outbox transacional quando a arquitetura exigir, e projete os consumidores para tolerar novas tentativas.

Projete uma fila que ajude a decidir

Uma fila útil permite priorizar e encontrar casos sem ocultar seu contexto. Inclua filtros pertinentes — estado, data, tipo ou atribuição — e exiba apenas as informações necessárias para compreender o caso. Se houver uma proposta, deixe claro que se trata de uma proposta. Apresente a procedência, o motivo da revisão e as ações disponíveis, e evite exibir controles que o usuário não esteja autorizado a executar.

A atribuição pode reduzir o trabalho duplicado, mas não substitui a autorização nem a proteção contra decisões simultâneas. Uma pessoa pode abrir um caso e outra resolvê-lo antes que a primeira envie sua resposta. Para evitar que uma tela desatualizada sobrescreva uma alteração recente, use controle de concorrência otimista: compare uma versão ou marca temporal ao salvar e rejeite a operação obsoleta para que o usuário recarregue o caso.

Se o processo precisar de reservas temporárias, defina seu vencimento e o que acontece quando expiram. Não use uma atribuição permanente como bloqueio acidental. Registre ações relevantes para poder explicar quem revisou o caso e detectar registros que permanecem pendentes por mais tempo do que o aceitável para a operação.

Corrija e reabra sem apagar o passado

Uma decisão pode estar errada ou mudar porque surgiram novas evidências. Defina quem pode reabri-la, qual motivo deve apresentar e se o dado confirmado deixa de estar disponível enquanto é revisado novamente. A resposta depende do impacto do valor nos processos posteriores; por exemplo, pode ser necessário interromper novas ações, mas não eliminar automaticamente efeitos já produzidos.

Conserve a decisão anterior no histórico e crie uma nova versão ou evento de revisão. Se a correção do dado exigir o recálculo de informações dependentes, identifique esses efeitos explicitamente e estabeleça um procedimento idempotente para evitar duplicações. Uma correção de estado não deve fingir que uma operação externa já foi desfeita.

Teste regras, permissões e efeitos posteriores

Teste regras, permissões e efeitos posteriores — guía visual de DedicatedPHP

Os testes devem abranger o fluxo completo e seus limites, não apenas verificar se o formulário foi salvo. No mínimo, verifique:

  • Que um caso pendente aceite as transições previstas e rejeite as demais.
  • Que atores sem permissão não possam decidir, reabrir nem consultar dados restritos.
  • Que cada decisão preserve o ator, o motivo e os valores anteriores e novos, conforme a política definida.
  • Que duas alterações concorrentes não permitam que uma decisão obsoleta sobrescreva outra.
  • Que uma falha ao registrar o histórico não deixe o estado atualizado sem rastreabilidade.
  • Que uma nova tentativa de um efeito posterior não duplique operações nem notificações.

Como lista de verificação de design, confirme que a incerteza tem uma causa identificável; que o dado original, a proposta e o valor confirmado estão diferenciados; que cada transição tem permissão e motivo; que a fila fornece contexto suficiente; e que a reabertura preserva o histórico e define o impacto operacional. Se algum desses pontos depender de convenções informais, ainda falta uma regra do sistema.

Deseja aplicar essas ideias ao seu projeto?Vamos discutir sua plataforma PHP.
Veja os serviços relacionados