Appearance
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 loteEnvio 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 lotesWARNING
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 /falhouO 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 recebido | Ação recomendada |
|---|---|
202 Accepted | Lote aceito — próximo lote ou confirmar comando |
204 No Content | Operação concluída — confirmar comando |
400 Bad Request | Dados inválidos — reportar falha com o corpo do erro como motivo |
403 Forbidden | Token inválido — verificar configuração, não tentar novamente automaticamente |
404 Not Found | Comando não encontrado — ignorar (pode ter expirado) |
5xx | Falha temporária — aguardar próximo ciclo de polling de comandos, não incrementar falhas |
Adicionando um novo tipo de comando
- Crie a classe herdando
ComandoSemArgumentos,ComandoComIntervaloDeDatasouComandodiretamente. - Adicione
[JsonDerivedType(typeof(NovaClasse), "NOVO_TIPO")]emComando.cs. - Implemente
ExecutarAsynccom 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 { ... }