> 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/construa/conceitos/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 fica 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

O SDK divide o código em dois papéis, marcados em cada método. A function é o método que coordena o fluxo: ela decide a ordem, avalia resultados, escolhe caminhos. O command é cada método que faz operação de I/O ou produz um valor que muda a cada execução: banco, HTTP, fila, arquivo, relógio, geração de identificadores.

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 (um delay, uma espera por evento ou o `await` de uma function filha), 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](/construa/conceitos/replay-e-determinismo.md).

<figure><img src="https://1898946878-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsJmS5ILPG5x2x5j0WyiV%2Fuploads%2FJ5JObcf2fEpQRzVhQ76B%2Fap-execucao-duravel.svg?alt=media" alt="Rastro de uma execução da emissão em duas entregas. Na entrega 1, os passos 0 e 1 (buscar o cliente e gerar o documento) executam e gravam o resultado no histórico da execução, que fica fora do processo, e o processo cai antes do passo 2. Na entrega 2, o replay devolve os passos 0 e 1 do histórico e executa só o passo 2, que notifica a conclusão."><figcaption></figcaption></figure>

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```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);
}
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code lineNumbers="true" %}

```typescript
import { SkailCommand, SkailFunction } from "@skail/runtime";
import type { Provider } from "@skail/runtime";
import { ClienteRepositorio, IntegracaoDocumentos, Mensageria } from "./servicos.js";

interface Cliente {
  id: string;
  nome: string;
}

interface IClienteRepositorio {
  obterAsync(id: string): Promise<Cliente>;
}
interface IIntegracaoDocumentos {
  enviarAsync(cliente: Cliente): Promise<void>;
}
interface IMensageria {
  publicarAsync(id: string): Promise<void>;
}

export class EmissaoService {
  private readonly repositorio: IClienteRepositorio;
  private readonly integracao: IIntegracaoDocumentos;
  private readonly mensageria: IMensageria;

  constructor(repositorio: IClienteRepositorio, integracao: IIntegracaoDocumentos, mensageria: IMensageria) {
    this.repositorio = repositorio;
    this.integracao = integracao;
    this.mensageria = mensageria;
  }

  @SkailFunction()
  async emitir(id: string): Promise<void> {
    const cliente = await this.buscarCliente(id); // passo 0: argumentos e resultado gravados
    await this.gerarDocumento(cliente); // passo 1
    await this.notificarConclusao(id); // passo 2
  }

  @SkailCommand()
  async buscarCliente(id: string): Promise<Cliente> {
    return await this.repositorio.obterAsync(id);
  }

  @SkailCommand()
  async gerarDocumento(cliente: Cliente): Promise<void> {
    await this.integracao.enviarAsync(cliente);
  }

  @SkailCommand()
  async notificarConclusao(id: string): Promise<void> {
    await this.mensageria.publicarAsync(id);
  }
}

export const providers: Provider[] = [
  { token: ClienteRepositorio, useClass: ClienteRepositorio },
  { token: IntegracaoDocumentos, useClass: IntegracaoDocumentos },
  { token: Mensageria, useClass: Mensageria },
  {
    token: EmissaoService,
    useFactory: (s) =>
      new EmissaoService(s.resolve(ClienteRepositorio), s.resolve(IntegracaoDocumentos), s.resolve(Mensageria)),
  },
];
```

{% endcode %}
{% endtab %}
{% endtabs %}

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, mesmo com 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 a operação externa 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](/construa/conceitos/retries-e-idempotencia.md) detalha o mecanismo; [Garantias de execução](/referencia/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 envolvem vários sistemas e duram 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](/construa/como-fazer/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 faz operação de I/O ou pode devolver algo diferente na próxima execução, é command. A function fica pequena e determinística; o command concentra as operações de I/O e precisa ser idempotente. Argumentos e retornos são gravados no histórico, então são tipos simples, pequenos e serializáveis em JSON, 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.

{% hint style="warning" %}
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](/construa/como-fazer/padroes-e-antipadroes/antipadroes-o-que-nao-fazer-e-por-que.md).
{% endhint %}

## Próximos passos

[Modelo de programação](/construa/conceitos/functions-e-commands.md) mostra os elementos do SDK e a linha do tempo de uma execução com os passos gravados. [Determinismo e replay](/construa/conceitos/replay-e-determinismo.md) explica por que a function é reexecutada e o que isso proíbe dentro dela. [Quando usar e quando não usar](/comece-aqui/quando-usar-e-quando-nao-usar.md) ajuda a decidir se o seu fluxo se beneficia disso.
