> For the complete documentation index, see [llms.txt](https://docs.skail.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.skail.dev/aprender/fundamentos/execucao-duravel.md).

# Execução durável

O que é execução durável, qual problema ela resolve, o que o skail garante quando você escreve um fluxo desse jeito, e por que a comparação mais útil é com uma transação de banco de dados (com uma diferença que importa).

## O problema

Um fluxo de negócio raramente cabe em uma requisição. Emitir uma fatura é buscar o cliente, gerar o documento em um serviço externo e notificar a conclusão. Cobrar uma assinatura é reservar, esperar 24 horas, cobrar, e tentar de novo se o gateway estiver fora. Aprovar um reembolso é esperar uma pessoa clicar, o que pode levar três dias.

Escrito como um método `async` comum, esse fluxo vive na memória de um processo. Se o processo cai entre o segundo e o terceiro passo, o progresso se perde e ninguém sabe se o documento foi gerado. Se o gateway está fora, alguém precisa escrever o retry. Se é preciso esperar três dias, alguém precisa criar uma fila, um job agendado e uma tabela de estado para lembrar onde cada reembolso parou. A lógica de negócio, que cabia em quinze linhas, se espalha por filas, handlers, cron e colunas de status.

## A ideia central

Execução durável é separar decidir de fazer, e gravar o progresso da execução fora do processo, passo a passo. Cada passo concluído fica registrado com seu resultado. Se o processo morre, reinicia ou passa por um deploy, a execução continua do último passo registrado, com os mesmos dados, em vez de recomeçar do zero ou ficar perdida no meio.

O ponto que faz diferença para quem escreve o código: o fluxo continua sendo um método. Não há máquina de estados para desenhar, nem mensagens para definir entre os passos. Você escreve `await BuscarCliente(id)`, depois `await GerarDocumento(cliente)`, e o skail cuida de registrar e retomar.

## Como o skail implementa

Dois atributos dividem o código em dois papéis. `[SkailFunction]` marca o método que coordena o fluxo: ele decide a ordem, avalia resultados, escolhe caminhos. `[SkailCommand]` marca cada método que fala com o mundo de fora: banco, HTTP, fila, arquivo, relógio, geração de identificadores. Os dois retornam `SkailTask` em vez de `Task`.

Cada `await` dentro de uma function é um checkpoint. Quando um command termina, o runtime grava no histórico da execução um passo com os argumentos e o resultado. Quando a function chega a uma espera (`SkailTask.Delay`, `SkailTask.WaitForEvent`), ela hiberna: nenhuma thread, nenhum processo fica ocupado. Quando é hora de continuar, o runtime reexecuta a function desde o início e, a cada `await` que já tem passo gravado, devolve o resultado sem executar de novo. Esse mecanismo é o replay, descrito em [Determinismo e replay](/aprender/fundamentos/determinismo-e-replay.md).

```csharp
using Skail.Platform.Runtime.Standard;
using Skail.Platform.Runtime.Standard.Threading;

public class EmissaoService
{
    private readonly IClienteRepositorio _repositorio;
    private readonly IIntegracaoDocumentos _integracao;
    private readonly IMensageria _mensageria;

    public EmissaoService(IClienteRepositorio repositorio, IIntegracaoDocumentos integracao, IMensageria mensageria)
    {
        _repositorio = repositorio;
        _integracao = integracao;
        _mensageria = mensageria;
    }

    [SkailFunction]
    public async SkailTask Emitir(Guid id)
    {
        var cliente = await BuscarCliente(id);   // passo 0: argumentos e resultado gravados
        await GerarDocumento(cliente);           // passo 1
        await NotificarConclusao(id);            // passo 2
    }

    [SkailCommand]
    public async SkailTask<Cliente> BuscarCliente(Guid id)
        => await _repositorio.ObterAsync(id);

    [SkailCommand]
    public async SkailTask GerarDocumento(Cliente cliente)
        => await _integracao.EnviarAsync(cliente);

    [SkailCommand]
    public async SkailTask NotificarConclusao(Guid id)
        => await _mensageria.PublicarAsync(id);
}
```

Se o processo cair depois de `GerarDocumento` gravar o resultado, a retomada reexecuta `Emitir`, devolve o cliente e o documento a partir do histórico, e executa só `NotificarConclusao`. Se `GerarDocumento` lançar exceção, o runtime tenta de novo até o `retryCount` do command antes de a exceção chegar à function.

## A garantia

O que o skail promete, dado que o trigger foi aceito: a execução continua até terminar ou até esgotar as tentativas, atravessando queda de processo, reinício, deploy que não muda o fluxo e esperas de minutos a semanas. Um command que gravou seu resultado não roda de novo naquela execução. Um evento disparado para uma espera é entregue, mesmo que chegue antes de a espera começar.

O que o skail não promete: que o efeito externo de um command aconteceu exatamente uma vez. Se o command chamou o gateway, o gateway cobrou, e o processo caiu antes de o runtime gravar o resultado, a próxima tentativa chama o gateway de novo. O efeito é no mínimo uma vez; fechar essa janela é idempotência no seu código, com uma chave de negócio na chamada externa. [Falhas, retries e idempotência](/aprender/fundamentos/falhas-retries-e-idempotencia.md) detalha o mecanismo; [Garantias de execução](/aprender/garantias/garantias-de-execucao.md) lista tudo em uma tabela.

## A analogia com transação

Uma transação de banco lhe dá o seguinte contrato: você declara uma sequência de operações e o banco garante que, aconteça o que acontecer no meio, o resultado final é consistente. Você não escreve código de recuperação; o banco tem um log e sabe reconstruir o estado a partir dele.

Execução durável oferece um contrato parecido para fluxos que atravessam vários sistemas e muito tempo: você declara a sequência de passos em um método e o skail garante que ela vai até o fim, sem repetir passo concluído, sem que você escreva recuperação. O histórico da execução cumpre o papel do log do banco: a retomada é uma reconstrução a partir dele.

A diferença é o que "consistente" significa. A transação é atômica dentro de um banco e desfaz sozinha o que fez se falhar. A execução durável não é atômica entre sistemas e não desfaz nada sozinha: se a cobrança passou e a emissão falhou de vez, é o seu código que decide estornar. Esse código é uma sequência de commands de compensação, e a function que os coordena é o padrão [Saga](/construir/padroes-e-antipadroes/saga.md). A outra diferença é a escala de tempo. A transação segura locks e dura milissegundos. A execução durável pode durar semanas, e enquanto espera não segura nada.

## O que isso muda para quem escreve código

A regra prática cabe em uma frase: se o método coordena, é function; se fala com o mundo ou pode devolver algo diferente na próxima execução, é command. A function fica fina e determinística; o command fica gordo e idempotente. Argumentos e retornos são gravados no histórico, então são DTOs pequenos e serializáveis, não entidades de ORM. Toda espera por evento tem um prazo. E quem chama o trigger faz retry se a chamada HTTP falhar, porque esse é o único passo que ainda não está sob a durabilidade do skail.

O que não fazer: I/O, relógio ou aleatoriedade dentro de uma function, e confiar em "exatamente uma vez" no efeito de um command. A lista completa está em [Antipadrões](/construir/padroes-e-antipadroes/antipadroes-o-que-nao-fazer-e-por-que.md).

## Próximos passos

[Modelo de programação](/aprender/fundamentos/modelo-de-programacao.md) mostra as três peças do SDK e a linha do tempo de uma execução com os passos gravados. [Determinismo e replay](/aprender/fundamentos/determinismo-e-replay.md) explica por que a function é reexecutada e o que isso proíbe dentro dela. [Quando usar e quando não usar](/quando-usar-e-quando-nao-usar.md) ajuda a decidir se o seu fluxo se beneficia disso.
