> 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/quickstart/modernizacao-de-legados/como-modernizar-seu-sistema-legado-sem-reescrever-tudo-com-skail.md).

# Como modernizar seu sistema legado sem reescrever tudo (com skail)

Se você tem um sistema legado em produção, provavelmente já viveu isso:

* fluxos quebrando no meio
* retries manuais
* status inconsistentes
* dificuldade de entender o que aconteceu, etc.

Modernizar um sistema  não significa refazer tudo.

Na maioria dos casos, o caminho mais rápido é identificar onde estão os gargalos — geralmente fluxos longos, com esperas, retries manuais e integrações frágeis — e evoluir só essa parte.

O restante continua no legado, fazendo bem o que já faz: cadastros, relatórios, regras fiscais e integrações já consolidadas.

Com o skail, você separa responsabilidades de forma simples:

* o legado continua executando operações
* o skail passa a coordenar o fluxo (orquestrando)

E isso pode começar pequeno — um único fluxo — e evoluir aos poucos.

#### Como o skail entra no seu sistema legado

Imagine um ERP legado que cria pedidos, controla estoque e emite nota.\
Tudo isso funciona razoavelmente bem.

O problema está no fluxo:

* reservar estoque
* esperar pagamento
* gerar etiqueta
* aguardar confirmação da transportadora
* atualizar status

Cada etapa depende da anterior — e é aí que começam os problemas.

Isso normalmente fica espalhado entre jobs agendados, filas, tabelas de controle e emails de atendimento chutando o processo quando algo trava.

Esse mesmo fluxo, no skail, vira uma simples função linear:

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

public sealed class FulfillmentService
{
    private readonly ErpDbContext _db;

    public FulfillmentService(ErpDbContext db)
    {
        _db = db;
    }

    [SkailFunction]
    public async SkailTask ProcessarPedido(Guid pedidoId)
    {
        await ReservarEstoqueNoERP(pedidoId);

        var pagamento = await SkailTask.WaitForEvent<PagamentoConfirmado>(
            "PAGAMENTO_CONFIRMADO",
            pedidoId.ToString()
        );

        var etiqueta = await GerarEtiquetaDeEnvio(
            pedidoId,
            pagamento.FormaPagamento
        );

        var entrega = await SkailTask.WaitForEvent<ConfirmacaoEntrega>(
            "PEDIDO_ENTREGUE",
            etiqueta.CodigoRastreio
        );

        await AtualizarStatusNoERP(pedidoId, entrega);
    }

    [SkailCommand]
    private async SkailTask ReservarEstoqueNoERP(Guid pedidoId)
    {
        var itens = await _db.PedidoItens
            .Where(item => item.PedidoId == pedidoId)
            .ToListAsync();

        foreach (var item in itens)
        {
            var estoque = await _db.Estoques
                .SingleAsync(e => e.ProdutoId == item.ProdutoId);
            
            estoque.QuantidadeDisponivel -= item.Quantidade;
            estoque.QuantidadeReservada += item.Quantidade;
        }

        var pedido = await _db.Pedidos.SingleAsync(p => p.Id == pedidoId);
        pedido.Status = "estoque_reservado";

        await _db.SaveChangesAsync();
    }

    [SkailCommand]
    private async SkailTask<EtiquetaGerada> GerarEtiquetaDeEnvio(
        Guid pedidoId,
        string formaPagamento
    )
    {
        var codigoRastreio = $"BR{DateTime.UtcNow:yyyyMMddHHmmss}";

        var etiqueta = new EtiquetaEnvioEntity
        {
            Id = Guid.NewGuid(),
            PedidoId = pedidoId,
            CodigoRastreio = codigoRastreio,
            FormaPagamento = formaPagamento,
            CriadaEm = DateTime.UtcNow
        };

        _db.EtiquetasEnvio.Add(etiqueta);

        var pedido = await _db.Pedidos.SingleAsync(p => p.Id == pedidoId);
        pedido.Status = "etiqueta_gerada";

        await _db.SaveChangesAsync();

        return new EtiquetaGerada
        {
            CodigoRastreio = codigoRastreio
        };
    }

    [SkailCommand]
    private async SkailTask AtualizarStatusNoERP(
        Guid pedidoId,
        ConfirmacaoEntrega entrega
    )
    {
        var pedido = await _db.Pedidos.SingleAsync(p => p.Id == pedidoId);

        pedido.Status = "entregue";
        pedido.EntregueEm = entrega.DataHora;
        pedido.Recebedor = entrega.Recebedor;

        await _db.SaveChangesAsync();
    }
}
```

O que antes estava espalhado, agora está explícito em um único lugar.

Dois pontos de espera — pagamento e entrega — orquestrados como um método linear.

Cada passo que mexe no ERP vira um `[SkailCommand]`.\
Na prática, é a mesma operação que seu legado já conhece: consultar tabelas, atualizar status, salvar registros e confirmar mudanças no banco.

```csharp
[SkailCommand]
private async SkailTask ReservarEstoqueNoERP(Guid pedidoId)
{
    var pedido = await _db.Pedidos.SingleAsync(p => p.Id == pedidoId);

    pedido.Status = "estoque_reservado";

    await _db.SaveChangesAsync();
}
```

O legado não precisa ser reescrito para isso.\
O skail apenas coordena quando cada etapa deve acontecer.

#### Estruturas usadas no exemplo

```csharp
public sealed record PagamentoConfirmado
{
    public string TransacaoId { get; init; } = "";
    public string FormaPagamento { get; init; } = "";
}

public sealed record EtiquetaGerada
{
    public string CodigoRastreio { get; init; } = "";
}

public sealed record ConfirmacaoEntrega
{
    public DateTime DataHora { get; init; }
    public string Recebedor { get; init; } = "";
}
```

```csharp
using Microsoft.EntityFrameworkCore;

public sealed class ErpDbContext : DbContext
{
    public ErpDbContext(DbContextOptions<ErpDbContext> options)
        : base(options)
    {
    }

    public DbSet<PedidoEntity> Pedidos => Set<PedidoEntity>();
    public DbSet<PedidoItemEntity> PedidoItens => Set<PedidoItemEntity>();
    public DbSet<EstoqueEntity> Estoques => Set<EstoqueEntity>();
    public DbSet<EtiquetaEnvioEntity> EtiquetasEnvio => Set<EtiquetaEnvioEntity>();
}

public sealed class PedidoEntity
{
    public Guid Id { get; set; }
    public string Status { get; set; } = "";
    public DateTime? EntregueEm { get; set; }
    public string? Recebedor { get; set; }
}

public sealed class PedidoItemEntity
{
    public Guid Id { get; set; }
    public Guid PedidoId { get; set; }
    public Guid ProdutoId { get; set; }
    public decimal Quantidade { get; set; }
}

public sealed class EstoqueEntity
{
    public Guid ProdutoId { get; set; }
    public decimal QuantidadeDisponivel { get; set; }
    public decimal QuantidadeReservada { get; set; }
}

public sealed class EtiquetaEnvioEntity
{
    public Guid Id { get; set; }
    public Guid PedidoId { get; set; }
    public string CodigoRastreio { get; set; } = "";
    public string FormaPagamento { get; set; } = "";
    public DateTime CriadaEm { get; set; }
}
```

#### Configuração do runtime

```csharp
using Microsoft.EntityFrameworkCore;
using Skail.Platform.Runtime;

[assembly: VisibleToSkailPlatform]

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddDbContext<ErpDbContext>(options =>
{
    options.UseSqlServer(
        builder.Configuration.GetConnectionString("ErpDatabase")
    );
});

builder.UseSkail();

await builder.Build().RunAsync();
```

#### Como o legado inicia um fluxo no skail

Do ponto de vista do legado, nada muda.\
Ele só chama um endpoint HTTP normal.

Para iniciar uma `[SkailFunction]`, use o endpoint de trigger:

```bash
curl -X POST \
  "https://seu-ambiente.skail.app/trigger/seu_workload/versao_workload/ProcessarPedido/00000000-0000-0000-0000-000000000000" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "skail-key: SUA_SKAIL_KEY" \
  -H "Skail-namespace: SEU_NAMESPACE" \
  -d '[
    "00000000-0000-0000-0000-000000000000"
  ]'
```

Quando o ERP conclui a criação do pedido, ele dispara esse trigger.

O skail recebe, inicia a execução durável e o ERP segue sua vida.

A execução pode durar dias; o ERP não precisa saber disso.

#### Como o skail reage ao que acontece no seu legado

Quando algo acontece no seu sistema, como pagamento aprovado, você só precisa avisar o skail.

Ele já dispara coisas quando algo acontece — como uma chamada HTTP, uma mensagem em fila ou até um registro no banco.

A ideia aqui é simples: pegar esse sinal e transformar em algo que o skail entende.

Na prática, isso significa publicar um evento externo usando a API HTTP de fire.

Um webhook de pagamento vindo do gateway que o ERP já integra, por exemplo, vira:

```csharp
using System.Net.Http.Json;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("webhooks/gateway-pagamento")]
public sealed class PagamentoWebhookController : ControllerBase
{
    private readonly HttpClient _httpClient;
    private readonly IConfiguration _configuration;

    public PagamentoWebhookController(
        HttpClient httpClient,
        IConfiguration configuration
    )
    {
        _httpClient = httpClient;
        _configuration = configuration;
    }

    [HttpPost]
    public async Task<IActionResult> Receber(
        [FromBody] NotificacaoGateway notificacao
    )
    {
        if (notificacao.Evento != "payment.approved")
        {
            return Ok();
        }

        var ambiente = _configuration["Skail:BaseUrl"];
        var skailKey = _configuration["Skail:Key"];
        var skailNamespace = _configuration["Skail:Namespace"];

        var url =
            $"{ambiente}/api/v1/fire/PAGAMENTO_CONFIRMADO/{notificacao.PedidoId}";

        using var request = new HttpRequestMessage(HttpMethod.Post, url)
        {
            Content = JsonContent.Create(new PagamentoConfirmado
            {
                TransacaoId = notificacao.TransactionId,
                FormaPagamento = notificacao.Method
            })
        };

        request.Headers.Accept.ParseAdd("application/json");
        request.Headers.Add("skail-key", skailKey);
        request.Headers.Add("Skail-namespace", skailNamespace);

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        return Ok();
    }
}

public sealed record NotificacaoGateway
{
    public string Evento { get; init; } = "";
    public Guid PedidoId { get; init; }
    public string TransactionId { get; init; } = "";
    public string Method { get; init; } = "";
}
```

Ou, diretamente por `curl`:

```bash
curl -X POST \
  "https://seu-ambiente.skail.app/api/v1/fire/PAGAMENTO_CONFIRMADO/00000000-0000-0000-0000-000000000000" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "skail-key: SUA_SKAIL_KEY" \
  -H "Skail-namespace: SEU_NAMESPACE" \
  -d '{
    "transacaoId": "txn_123",
    "formaPagamento": "cartao_credito"
  }'
```

Esse código não decide nada — ele só transforma o evento do formato do legado para o formato que o skail entende.

A função `ProcessarPedido`, que estava hibernada há minutos ou horas esperando o `PAGAMENTO_CONFIRMADO` daquele pedido, retoma automaticamente.

O mesmo padrão serve para confirmação de entrega da transportadora, aprovação de crédito que vem do banco, qualquer coisa que antes precisaria de um job consumindo uma fila e atualizando uma tabela de "próximo passo".

```bash
curl -X POST \
  "https://seu-ambiente.skail.app/api/v1/fire/PEDIDO_ENTREGUE/BR123456789" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "skail-key: SUA_SKAIL_KEY" \
  -H "Skail-namespace: SEU_NAMESPACE" \
  -d '{
    "dataHora": "2026-04-27T18:30:00Z",
    "recebedor": "Maria Silva"
  }'
```

{% hint style="info" %}
[Agende uma conversa grátis com um arquiteto de solução](https://skailhq.com/) para discutir seus objetivos
{% endhint %}

#### Avançando aos poucos

O primeiro fluxo modernizado geralmente convive com o fluxo antigo por um tempo — alguns pedidos vão pelo skail, outros pelo caminho de sempre, separados por uma flag.

À medida que a confiança aumenta, o percentual de tráfego no skail cresce, e eventualmente o caminho legado é desligado.

Depois disso, o segundo fluxo. E o terceiro.

O legado vai perdendo responsabilidade em pontos específicos, mas não precisa ser reescrito — ele continua sendo o dono dos cadastros, das regras fiscais, dos relatórios.

O skail assume a camada que o legado nunca soube fazer direito: a vida de um processo que dura mais do que uma requisição HTTP.

#### Observabilidade

No monitor do skail, cada pedido em fulfillment aparece com seu estágio atual — aguardando pagamento, aguardando entrega, atualizando ERP.

As operações no banco do ERP aparecem como `SkailCommand`, uma a uma, com latência e status.

Isso resolve um problema comum em sistemas legados: entender em que etapa um pedido está sem precisar correlacionar logs de vários sistemas diferentes.

No monitor, você consegue ver exatamente:

* em que etapa o processo está
* onde ele parou, se falhou
* qual foi o motivo
* e retomar dali, se necessário

E como cada `SkailCommand` fica registrado dentro da execução, você também consegue ver o que foi executado em cada etapa do legado.

Antes: logs espalhados.\
Agora: fluxo completo visível.

Além disso, mesmo que a execução seja retomada várias vezes ao longo dos dias, as etapas já concluídas não são repetidas.

Ou seja: o legado não corre o risco de receber duas reservas de estoque ou dois updates de status para o mesmo pedido por causa de uma retomada.

***

No fim, modernizar seu sistema não é sobre trocar tudo.

É sobre tirar a complexidade de onde ela não deveria estar.

E começar pequeno.

{% hint style="info" %}
[Agende agora uma consultoria grátis](https://skailhq.com/) com um arquiteto especializado skail para falar sobre seu projeto
{% endhint %}
