Commands são intenções, não ações
Um Command não faz nada por si mesmo. Ele é um objeto imutável que diz: “quero executar esta operação, com estes dados”. A execução de fato só ocorre quando o Command é passado ao Handler correspondente. Essa separação tem uma consequência importante: você pode construir, inspecionar, serializar e até registrar em log um Command sem que nenhuma operação de rede aconteça.Handlers são executores
O Handler tem uma única responsabilidade: receber um Command, chamar o serviço externo e devolver um Result. Ele não decide se a operação deve ser feita — apenas como fazê-la. Cada Handler é especializado em um tipo de Command. OCteAutorizacaoHandler trata apenas CteAutorizacaoCommand; o ConsultaRntrcHandler trata apenas ConsultaRntrcCommand. Essa especialização garante que o PHPStan possa verificar a compatibilidade entre Command e Handler em tempo de análise estática.
Queries
Nem toda operação muda estado — algumas apenas consultam. Para essas, os SDKs SUOT usam Queries em vez de Commands. A forma é idêntica: Query → Handler → Result. A diferença é semântica: uma Query nunca produz efeitos colaterais no serviço externo.A distinção entre Command e Query é intencional: ao ler o código, qualquer desenvolvedor (ou agente de IA) sabe imediatamente se uma operação é uma leitura ou uma escrita — sem precisar abrir a implementação do Handler.
Results são tipados e imutáveis
Todo Handler devolve um Result concreto e tipado para aquela operação específica. Não existe umResult genérico — existe CteAutorizacaoResult, ConsultaCteResult, ConsultaRntrcResult etc. Cada um expõe exatamente os métodos relevantes para aquela operação.
Testabilidade
Como o Handler é uma dependência injetada, ele pode ser substituído por um mock nos testes unitários. Você testa a lógica da sua aplicação — o que ela faz com um sucesso, o que ela faz com uma rejeição — sem fazer nenhuma chamada real a SEFAZ ou ANTT.Jornada do desenvolvedor
Esta é a jornada típica de um desenvolvedor ao implementar uma nova operação com os SDKs SUOT:1
Identificar a operação
Você precisa autorizar um CT-e. A documentação indica
CteAutorizacaoCommand e CteAutorizacaoHandler.2
Autocomplete e PHPDoc
Ao digitar
new CteAutorizacaoCommand(, a IDE exibe todos os parâmetros com seus tipos e descrições em português. Nenhum argumento ambíguo — cada um é um Value Object com nome significativo.3
Construir o Command com Value Objects
Você constrói os Value Objects (
Cnpj, ChaveCTe, etc.) com os dados validados. Erros de formato são detectados aqui, antes de qualquer I/O.4
Chamar o Handler
O Handler é injetado via construtor no seu serviço. Você chama
handle(command: $command) envolto em try/catch para InfrastructureException.5
Tratar o Result
Você verifica
isSuccess() e isRejected() e age de acordo — persiste o protocolo, registra o motivo da rejeição ou notifica o operador.6
Configurar a Retry Policy
Para operações críticas, você configura uma
RetryPolicy no Handler para lidar automaticamente com falhas de infraestrutura transientes — sem alterar a lógica de negócio.7
Reconstruir o Command se necessário
Como o Command é imutável e não tem estado de execução, você pode reconstruí-lo com os mesmos dados e passar a um novo Handler após um failover ou mudança de ambiente — sem efeitos colaterais.

