Novidades e futuro do .NET Orleans

O Orleans é o framework do .NET para construir sistemas distribuídos stateful com base no modelo de atores virtuais, e ele ganhou um novo papel com a chegada das aplicações baseadas em agentes de IA. Afinal, o crescimento de aplicações distribuídas, interativas e orientadas a eventos expôs limitações importantes do modelo tradicional de aplicações stateless.

Com a popularização de arquiteturas de agentes, fluxos assíncronos de longa duração e workloads intensivos em IA, fica cada vez mais necessário tratar o estado como um elemento central e durável do sistema. Nesse contexto, o .NET Orleans surge como uma plataforma madura para esse tipo de cenário.

Este artigo apresenta uma visão técnica do papel do Orleans no ecossistema .NET, dos seus fundamentos, dos casos de uso modernos e das evoluções discutidas no ASP.NET Community Standup: What's next for Orleans?.

O que é o Orleans

O Orleans é um framework multiplataforma, criado pela Microsoft, que simplifica a construção de aplicações distribuídas. Segundo a documentação oficial do Orleans, ele estende conceitos conhecidos do C# para ambientes com vários servidores, de modo que o desenvolvedor foque na lógica da aplicação.

Por isso, muita gente chama o Orleans de "Distributed .NET". Ele escala de um único servidor até milhares de instâncias na nuvem e roda onde o .NET roda, como Linux, Windows e macOS.

Além disso, o framework funciona com opções modernas de implantação, como Kubernetes, Azure App Service e Azure Container Apps. A própria Microsoft usa o Orleans no Azure, no Xbox, no Skype, no PlayFab e em jogos como Halo e Gears of War.

O problema do modelo stateless em aplicações modernas

Aplicações web tradicionais, especialmente as construídas com ASP.NET, são predominantemente stateless. Ou seja, qualquer instância pode atender qualquer requisição, normalmente por trás de um load balancer.

Nesse modelo, não existe garantia de afinidade entre requisições consecutivas. Isso simplifica o escalonamento horizontal, mas cria desafios quando a aplicação precisa:

  • Manter estado entre várias interações do usuário
  • Coordenar vários clientes que interagem com o mesmo contexto
  • Executar processos de longa duração
  • Garantir continuidade após falhas, reinícios ou implantações

Para contornar essas limitações, é comum recorrer a bancos de dados, caches distribuídos, filas e mecanismos de coordenação externa. Por exemplo, muitos times usam um cache distribuído com PostgreSQL ou Redis só para guardar o estado de uma sessão.

Embora funcione, essa estratégia aumenta a complexidade da arquitetura, os custos operacionais e a latência. Cada requisição precisa buscar o estado em outro lugar antes de fazer qualquer trabalho útil.

Orleans e o modelo de atores virtuais

O Orleans adota o modelo de atores virtuais, no qual cada unidade de estado e comportamento fica encapsulada em um grain. Um grain representa uma entidade lógica do sistema, como um usuário, um carrinho ou um agente.

Além disso, cada grain tem as seguintes características:

  • Estado associado de forma explícita
  • Execução isolada e single-threaded
  • Comunicação baseada em mensagens
  • Ativação sob demanda
  • Transparência de localização

A principal inovação do Orleans é eliminar a necessidade de o desenvolvedor gerenciar onde o estado está ou como ele se distribui. Em vez disso, o runtime mapeia cada grain para um nó do cluster, chamado silo, e roteia corretamente todas as mensagens destinadas a ele.

O termo "virtual" vem justamente daí. O grain sempre existe do ponto de vista lógico, então você nunca cria nem destrói um ator manualmente.

Como um grain do Orleans se parece no código

Na prática, um grain é uma interface e uma classe. Por exemplo, o código abaixo representa um agente de conversa que guarda o histórico de mensagens em um estado persistente.

public interface IChatAgentGrain : IGrainWithStringKey
{
    Task<IReadOnlyList<string>> GetHistoryAsync();
    Task AddMessageAsync(string message, CancellationToken cancellationToken = default);
}

[GenerateSerializer]
public class ChatAgentState
{
    [Id(0)]
    public List<string> Messages { get; set; } = [];
}

public sealed class ChatAgentGrain(
    [PersistentState("chat", "chatStore")] IPersistentState<ChatAgentState> state)
    : Grain, IChatAgentGrain
{
    public Task<IReadOnlyList<string>> GetHistoryAsync() =>
        Task.FromResult<IReadOnlyList<string>>(state.State.Messages);

    public async Task AddMessageAsync(string message, CancellationToken cancellationToken = default)
    {
        // interrompe o trabalho se o chamador cancelar a operação
        cancellationToken.ThrowIfCancellationRequested();

        state.State.Messages.Add(message);

        // grava o snapshot completo do estado no storage configurado
        await state.WriteStateAsync();
    }
}

Em seguida, para chamar o grain, basta pedir uma referência pela chave. O Orleans ativa o grain se ele ainda não estiver em memória e entrega a mensagem ao silo correto.

var agent = grainFactory.GetGrain<IChatAgentGrain>("user-42");
await agent.AddMessageAsync("Hello, Orleans!");

Repare também que o código não menciona servidor, conexão nem fila. Toda essa complexidade fica com o runtime do Orleans.

Quando usar o Orleans

A documentação recomenda considerar o Orleans ao construir aplicações .NET que podem precisar escalar no futuro. Ou seja, ele não é só para quem já tem milhões de usuários, mas também para quem quer crescer sem reescrever a arquitetura.

Entre os usos mais comuns, a documentação cita jogos, sistemas bancários, aplicativos de chat, rastreamento por GPS, negociação de ações, carrinhos de compra e sistemas de votação. Em todos eles, existe uma entidade com identidade clara e estado que muda o tempo todo.

Por outro lado, uma API CRUD simples, sem estado relevante entre requisições, talvez não precise de atores. Nesse caso, o custo de aprender e operar um cluster pode não compensar.

Recursos do Orleans além dos grains

Além dos grains e da persistência, o Orleans oferece outros recursos prontos. Por exemplo, os reminders funcionam como agendamentos duráveis, que garantem a execução de uma ação no futuro mesmo que o grain não esteja ativo.

Os timers, por sua vez, são a versão não durável dos reminders. Eles servem para eventos de alta frequência que não exigem confiabilidade.

Também é possível configurar o placement, isto é, a decisão sobre em qual silo cada grain será ativado. Dessa forma, você escolhe entre políticas prontas, como aleatória, local ou baseada em carga, ou escreve a sua própria lógica.

Por fim, o Orleans permite versionar as interfaces dos grains. Assim, o cluster consegue passar por atualizações em produção mesmo com silos rodando versões diferentes do código.

Statefulness como vantagem, não como custo

O modelo stateless costuma aparecer como o mais simples, mas na verdade ele transfere a complexidade para a infraestrutura. O Orleans, por outro lado, assume o estado como parte do modelo de programação.

Com isso, ele oferece:

  • Afinidade automática entre mensagens e estado
  • Recuperação transparente após falhas
  • Menos acessos redundantes ao armazenamento externo
  • Uso mais eficiente de recursos

Enquanto um grain está ativo, o Orleans mantém o estado em memória e atende leituras sem ir ao storage. Quando o grain altera o estado, a chamada a WriteStateAsync atualiza o armazenamento para garantir durabilidade e consistência.

Por isso, esse modelo combina bem com jogos online, colaboração em tempo real, sistemas financeiros, workflows complexos e, mais recentemente, aplicações baseadas em agentes de IA.

Orleans e aplicações baseadas em agentes

Agentes de IA são bem diferentes de aplicações CRUD tradicionais. Se você ainda não tem familiaridade com o conceito, vale ler o nosso artigo sobre o que são agents.

De forma geral, agentes são entidades ativas, interativas e persistentes, que:

  • Reagem a eventos e comandos assíncronos
  • Mantêm histórico de conversação
  • Executam tarefas de longa duração
  • Aceitam interrupção, retomada ou reconfiguração

Por essas características, o modelo stateless se torna inadequado. No Orleans, por outro lado, cada agente pode virar um grain, com estado durável e processamento que continua mesmo após falhas ou reinicializações.

Segundo os apresentadores do Community Standup, a Microsoft adota amplamente esse modelo em serviços internos de IA. Isso inclui, por exemplo, infraestrutura de inferência, coordenação de workloads e sistemas baseados em agentes.

Assim, para quem já constrói agentes no ecossistema .NET, o Orleans pode servir como a camada de estado e distribuição por trás de frameworks como o Microsoft Agent Framework.

Persistência, durabilidade e journaling no Orleans

Historicamente, o Orleans usa um modelo de persistência baseado em snapshots completos do estado. Foi o que vimos no exemplo do ChatAgentGrain, em que cada mensagem nova regrava a lista inteira.

Embora funcione, esse modelo se torna ineficiente quando há atualizações frequentes e incrementais. Um caso típico é o stream de tokens gerados por um modelo de linguagem, em que cada pedaço novo provocaria uma escrita completa.

A partir do Orleans 10, chegou um novo mecanismo baseado em journaling, no qual o runtime persiste apenas os deltas do estado. Esse modelo traz diversas vantagens:

  • Redução significativa de I/O
  • Melhor desempenho em cenários com muitas atualizações
  • Alinhamento com técnicas consagradas, como o write-ahead logging

O journaling usa coleções duráveis, como listas, filas e dicionários, que expõem uma API familiar ao desenvolvedor. Enquanto isso, o runtime cuida da persistência incremental e da compactação periódica do log.

Nas versões seguintes da linha 10, o pacote Orleans.Journaling passou a usar JSON Lines como formato padrão de armazenamento, segundo as notas de release do repositório.

Journaling no Orleans versus event sourcing

Vale não confundir o journaling com o JournaledGrain, que já existia no Orleans para event sourcing. No event sourcing, o desenvolvedor modela eventos de domínio e o grain reconstrói o estado a partir deles.

Já no journaling das coleções duráveis, o foco é técnico. Em outras palavras, você continua trabalhando com uma lista ou um dicionário, e o runtime decide como registrar e compactar as mudanças.

Orleans 10: principais evoluções

O Orleans 10 marca uma evolução importante da plataforma. Entre os destaques apresentados no Community Standup, estão:

  • Multi-targeting com suporte ao .NET 8 e ao .NET 10
  • Melhoria significativa na recuperação de clusters após falhas
  • Suporte a CancellationToken em chamadas entre grains
  • Coleções duráveis baseadas em journaling
  • Load shedding baseado em memória
  • Melhorias no shutdown gracioso
  • Troca do cache LRU interno por uma implementação mais eficiente

Segundo a documentação, o Orleans 10 tem como alvo o .NET 8, o .NET 9 e o .NET 10. Além disso, a versão trouxe um dashboard integrado para monitorar cluster, grains e silos em tempo real, e providers de Redis estáveis para clustering, persistência e reminders.

Na prática, essas mudanças fortalecem a confiabilidade do runtime e ampliam o uso em produção em larga escala.

Cancelamento e load shedding no Orleans

O suporte a CancellationToken permite que o chamador interrompa uma operação longa dentro de um grain. Assim, o grain verifica o token e para o trabalho de forma cooperativa, como no método AddMessageAsync do exemplo.

O load shedding baseado em memória, por sua vez, protege o silo sob pressão. Nesse caso, o runtime desativa grains automaticamente quando a memória fica escassa, em vez de deixar o processo cair.

Se você vem de versões antigas, atenção às mudanças de comportamento. Por exemplo, o guia de migração informa que o padrão de CancelRequestOnTimeout passou de true para false.

Execução durável e workflows no Orleans

Outra frente de evolução é a execução durável. A ideia é permitir que operações complexas cheguem até a conclusão, mesmo diante de falhas no meio do caminho.

Conceitualmente, esse modelo se aproxima de soluções como Durable Functions e Temporal. No entanto, o Orleans se diferencia por integrar a execução durável ao modelo de atores, o que preserva a interatividade e a baixa latência.

A documentação do Azure ajuda a entender essa diferença. Por exemplo, as durable entities priorizam durabilidade em vez de latência, enquanto o Orleans aplica timeout às mensagens após um período configurável, de 30 segundos por padrão.

A proposta para o Orleans inclui:

  • Agendamento durável de tarefas
  • Reexecução segura após falhas
  • Garantia de idempotência
  • Composição de operações complexas com semântica parecida com async/await

Por isso, esses recursos são especialmente relevantes para workflows financeiros, orquestrações distribuídas e processos críticos. Para comparar com orquestrações de agentes, veja como orquestrar agentes com workflows no Microsoft Agent Framework.

Orleans 11 e próximos passos

A próxima versão do Orleans tem como foco principal estabilizar as funcionalidades que chegaram no Orleans 10. Entre os itens citados no Community Standup, estão:

  • Consolidação do journaling
  • Evolução do modelo de tarefas duráveis
  • Introdução de leases para grains
  • Refinamento da arquitetura de inbox e outbox duráveis

Em resumo, o objetivo é oferecer uma base ainda mais sólida para aplicações distribuídas stateful, sem perder a simplicidade do modelo de programação. Como se trata de roadmap, os detalhes podem mudar até o lançamento.

Assista ao Community Standup sobre o Orleans

Este artigo tem como base o ASP.NET Community Standup: What's next for Orleans?. Assista à conversa completa abaixo para ver as demonstrações e a discussão sobre o roadmap.

Conclusão

O Orleans representa uma mudança de paradigma no desenvolvimento de aplicações distribuídas, porque trata o estado como cidadão de primeira classe. Em um cenário dominado por sistemas interativos, orientados a eventos e baseados em agentes, essa abordagem se mostra não só relevante, mas essencial.

Com o journaling, o cancelamento entre grains, o load shedding e a execução durável, o Orleans 10 resolve dores reais de quem roda sistemas stateful em produção. Além disso, o roadmap do Orleans 11 aponta para mais estabilidade, e não para mais complexidade.

Por fim, o Orleans se posiciona como uma peça estratégica do ecossistema .NET. Ele combina simplicidade conceitual, robustez operacional e alto desempenho para aplicações distribuídas modernas.

FAQ

O que é um grain no Orleans?

Um grain é um ator virtual, ou seja, uma unidade com identidade, estado e comportamento. O runtime ativa o grain sob demanda e roteia as mensagens até ele.

Qual a diferença entre o Orleans e o Durable Functions?

Os dois trabalham com estado durável, mas o Durable Functions prioriza durabilidade. Já o Orleans prioriza baixa latência e interatividade dentro do modelo de atores.

Quais versões do .NET o Orleans 10 suporta?

Segundo a documentação oficial, o Orleans 10 tem como alvo o .NET 8, o .NET 9 e o .NET 10.

O journaling substitui a persistência tradicional do Orleans?

Não. O modelo de snapshots continua disponível, e o journaling surge como alternativa para estados que mudam com muita frequência.

O Orleans serve para agentes de IA?

Sim. Cada agente pode ser um grain com histórico durável, o que permite retomar o processamento após falhas ou reinícios.