Skip to content

Boas Práticas

Resumo das regras operacionais que garantem uma integração confiável e previsível com a Maggu.

Polling de Comandos

Intervalo fixo de 60s — sem backoff

Consulte GET /v3/comandos/pendentes a cada 60s. Em caso de erro HTTP 5xx da Maggu, não aplique backoff exponencial — aguarde o próximo ciclo normal.

Motivo: o backoff pode atrasar a execução de comandos urgentes por minutos. A Maggu é dimensionada para absorver o volume fixo de polling de comandos.

csharp
while (!ct.IsCancellationRequested)
{
    await ExecutarAsync(ct);
    await Task.Delay(60_000, ct); // sempre 60 segundos
}

Processamento de comandos

Ordenar do mais antigo para o mais recente

Processe os comandos em ordem crescente de criadoEm. Isso garante consistência temporal — ex.: enviar vendas antes de enviar contra-provas do mesmo dia.

csharp
foreach (var comando in comandos.OrderBy(c => c.CriadoEm))
    await _orquestrador.ProcessarAsync(comando, ct);

Confirmar ou reportar falha individualmente

Nunca confirme um lote de comandos de uma vez. Cada id deve ter sua própria chamada a /completou ou /falhou.

csharp
// Correto: um por vez
await _notificador.Concluido(comando);

// Incorreto: não existe endpoint de confirmação em lote

Envio em lote

Máximo de 400 registros por requisição

Todos os endpoints /registrar-em-lote aceitam no máximo 400 itens por chamada. Para volumes maiores, quebre com Chunk(400):

csharp
foreach (var lote in registros.Chunk(400))
{
    var requisicao = new RestRequest("api/v3/usuarios/registrar-em-lote", Method.Post);
    requisicao.AddJsonBody(new { comandoId = Id, conteudo = lote });
    await _provedor.Client.PostAsync(requisicao, ct);
}
// Confirmar o comando SOMENTE aqui, após todos os lotes

WARNING

Confirme o comando somente após todos os lotes serem enviados com sucesso. Se um lote falhar, o comando inteiro ainda não foi concluído.


Idempotência

Reenvios são seguros — não duplicam dados

Todos os endpoints de lote da Maggu são idempotentes pela chave codigoExterno (ou ean para produtos, cpfCnpj para clientes). Reprocessar o mesmo comando não cria registros duplicados.

Ainda assim, mantenha controle local para evitar reprocessar comandos desnecessariamente:

csharp
// RepositorioComandos — persiste estado em memória
_repositorio.Salvar(new RegistroComando(comando.Id, comando.Tipo, StatusComando.Pendente));

Retry local

Até 3 tentativas antes de reportar falha

Se o ERP falhar ao acessar o banco local (timeout, lock, etc.), tente novamente até 3 vezes antes de chamar /falhou:

csharp
const int maxTentativas = 3;
Exception? ultimoErro = null;

for (int i = 0; i < maxTentativas; i++)
{
    try
    {
        await ExecutarOperacaoLocalAsync(ct);
        return; // sucesso
    }
    catch (Exception ex) when (ex is not OperationCanceledException)
    {
        ultimoErro = ex;
        await Task.Delay(500 * (i + 1), ct); // espera progressiva só no retry local
    }
}

throw ultimoErro!; // propaga para /falhou

O retry com espera progressiva aplica-se apenas ao acesso local (banco de dados do ERP). Não aplique backoff nas chamadas à API Maggu.


Tratamento de erros da API Maggu

Código recebidoAção recomendada
202 AcceptedLote aceito — próximo lote ou confirmar comando
204 No ContentOperação concluída — confirmar comando
400 Bad RequestDados inválidos — reportar falha com o corpo do erro como motivo
403 ForbiddenToken inválido — verificar configuração, não tentar novamente automaticamente
404 Not FoundComando não encontrado — ignorar (pode ter expirado)
5xxFalha temporária — aguardar próximo ciclo de polling de comandos, não incrementar falhas

Adicionando um novo tipo de comando

  1. Crie a classe herdando ComandoSemArgumentos, ComandoComIntervaloDeDatas ou Comando diretamente.
  2. Adicione [JsonDerivedType(typeof(NovaClasse), "NOVO_TIPO")] em Comando.cs.
  3. Implemente ExecutarAsync com a lógica local + chamada à API Maggu.
csharp
// 1. Classe
public class NovoComandoComando : ComandoSemArgumentos
{
    protected override async Task ExecutarAsync(CancellationToken ct)
    {
        // lógica local...
        // chamada à API Maggu...
    }
}

// 2. Registro em Comando.cs
[JsonDerivedType(typeof(NovoComandoComando), "NOVO_TIPO")]
public abstract class Comando { ... }