> 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/modelo-de-programacao.md).

# Modelo de programação

Três elementos formam todo código skail: a `[SkailFunction]` que orquestra, o `[SkailCommand]` que executa as operações de I/O, e o `SkailTask` que carrega o `await` dos dois. Esta página fecha o que cada um é, como eles se combinam e o que a linha do tempo de uma execução guarda.

## Function: o orquestrador

Um método marcado com `[SkailFunction]` descreve a sequência: chame A, depois B e C em paralelo, espere o evento D, decida. O corpo é reexecutado a partir do histórico da execução a cada retomada, então ele precisa ser determinístico: mesma entrada e mesmo histórico, mesma sequência de `await`s. Não faz I/O, não lê o relógio, não gera aleatoriedade. Pode chamar commands, outras functions (que viram orquestrações filhas) e as primitivas `SkailTask.*`.

```csharp
[SkailFunction]
public async SkailTask EmitirFatura(Guid faturaId)
{
    var fatura   = await CarregarFatura(faturaId);                 // command
    var cobranca = await CobrarCartao(fatura);                     // command
    if (cobranca == ResultadoCobranca.Recusado) { await NotificarRecusa(faturaId); return; }

    await SkailTask.Delay(TimeSpan.FromDays(1));                   // primitiva: espera durável
    await EmitirNotaFiscal(faturaId);                              // command
}
```

## Command: a unidade de trabalho

Um método marcado com `[SkailCommand]` faz a operação de I/O: grava no banco, chama a API, publica na fila, lê o relógio. Executa uma vez por resultado; argumentos e retorno são gravados no histórico; no replay o runtime devolve o gravado sem executar. Dentro dele vale tudo que vale em C# comum, inclusive `Task.Delay`, `Task.WhenAll`, `HttpClient`, `DbContext`. Se falhar, é retentado até `retryCount` (5 por padrão).

Um `[SkailCommand]` não chama outro `[SkailCommand]` nem uma `[SkailFunction]`: quem compõe os passos é sempre a function, e commands são chamados só de dentro de functions. Se dois passos precisam ser duráveis separadamente, a function chama os dois em sequência; para reaproveitar lógica dentro de um command, use métodos comuns, sem atributo. Ver [SkailCommand](/construir/sdk-.net/skailcommand.md).

```csharp
[SkailCommand(retryCount: 8)]
public async SkailTask<ResultadoCobranca> CobrarCartao(Fatura fatura)
{
    var resposta = await _gateway.CobrarAsync(fatura.Id, fatura.Valor, idempotencyKey: fatura.Id.ToString());
    return resposta.Aprovada ? ResultadoCobranca.Aprovado : ResultadoCobranca.Recusado;
}
```

A regra que separa os dois: se o resultado pode diferir entre duas execuções com as mesmas entradas, é command. O restante pode ser function.

| Quero...                                          | Uso                                                          |
| ------------------------------------------------- | ------------------------------------------------------------ |
| Encadear passos, decidir, repetir, paralelizar    | `[SkailFunction]`                                            |
| Chamar HTTP, banco, fila, arquivo                 | `[SkailCommand]`                                             |
| Ler `DateTime.UtcNow`, `Guid.NewGuid()`, `Random` | `[SkailCommand]` que devolve o valor                         |
| Esperar um tempo                                  | `await SkailTask.Delay(...)` na function                     |
| Esperar um webhook, uma pessoa, outro sistema     | `await SkailTask.WaitForEvent(...)` na function              |
| Rodar passos em paralelo                          | `await SkailTask.WhenAll(...)` ou `WhenAny(...)` na function |
| Iniciar um sub-fluxo                              | Chamar outra `[SkailFunction]`                               |
| Devolver um valor de um passo                     | `SkailTask<T>` em qualquer dos dois; se faz I/O, command     |

## SkailTask: o tipo de retorno

Todo método decorado retorna `SkailTask` ou `SkailTask<T>`, nunca `Task`. É um awaitable com method builder próprio: é ele que permite ao runtime interceptar cada `await`, consultar o histórico e gravar o passo. Do lado de quem escreve, a única diferença de `Task` é o nome no retorno; `await`, `try/catch`, `using`, `try/finally` e LINQ funcionam normalmente. O que não funciona: `.Result`, `.Wait()`, `.GetAwaiter().GetResult()` (o analyzer bloqueia) e misturar `Task` e `SkailTask` no mesmo caminho durável.

As primitivas são estáticas em `SkailTask`: `Delay(TimeSpan)`, `DelaySeconds(ulong)`, `DelayMinutes(ulong)`, `WhenAll(params SkailTask[])`, `WhenAny(params SkailTask[])`, `WaitForEvent(nome, instanceId)` e `WaitForEvent<T>(nome, instanceId)`. Cada uma grava um passo e suporta replay. Não existe uma primitiva para disparar eventos: isso é feito pela API HTTP, de fora.

## Regras de assinatura

Método de instância (nunca `static`), `async`, retorno `SkailTask`/`SkailTask<T>`, `public`. Argumentos e retornos serializáveis por `System.Text.Json`: primitivos, `Guid`, `DateTime`, records, DTOs, coleções deles. Um `SkailTask` não é serializável e não pode ser argumento de function. O analyzer `Skail.Platform.Analyzer` cobra as regras na compilação: SKAIL001 (assinatura), SKAIL002 (`async`), SKAIL003 (`.GetResult()`). Ver [Regras do analyzer](/construir/sdk-.net/regras-do-analyzer-skail001-skail002-skail003.md).

A classe que contém os métodos é registrada no contêiner de injeção de dependência automaticamente por `builder.UseSkail()`, como transient. Injete `ILogger<T>`, `DbContext`, `IHttpClientFactory` e seus serviços pelo construtor, como em qualquer classe .NET. Mantenha os que fazem I/O em uso dentro de commands.

## A linha do tempo de uma execução

A function acima, com a cobrança aprovada, deixa este histórico da execução:

```
passo 0  início      EmitirFatura(faturaId)
passo 1  command     CarregarFatura(faturaId)        → Fatura {...}         gravado
passo 2  command     CobrarCartao(fatura)            → Aprovado              gravado
passo 3  delay       1 dia                            agendado → concluído
passo 4  command     EmitirNotaFiscal(faturaId)      → void                  gravado
passo 5  fim
```

Quando o delay do passo 3 vence, um dia depois, o runtime chama `EmitirFatura` do início. Em cada `await`, encontra o passo correspondente e devolve o resultado gravado: `CarregarFatura` e `CobrarCartao` não executam. O delay está concluído. `EmitirNotaFiscal` não tem passo: executa, grava o passo 4, e a function termina. É esse histórico que o Monitor desenha como linha do tempo, e é ele que o Time Travel Debug reproduz na sua IDE.

## Próximos passos

[Determinismo e replay](/aprender/fundamentos/determinismo-e-replay.md) detalha o que pode e o que não pode dentro da function. [Falhas, retries e idempotência](/aprender/fundamentos/falhas-retries-e-idempotencia.md) explica o que acontece quando um command falha. A referência de [SkailFunction](/construir/sdk-.net/skailfunction.md), [SkailCommand](/construir/sdk-.net/skailcommand.md) e [SkailTask](/construir/sdk-.net/skailtask-e-skailtask-less-than-t-greater-than.md) tem assinaturas e parâmetros.
