$npx -y skills add managedcode/dotnet-skills --skill orleansDesign Microsoft Orleans systems from each primitive's purpose and failure model. USE FOR: grains, digital twins, state versus databases, transactions, messaging, streams, timers, reminders, Durable Jobs, stateless workers, grain services, startup, and hosting. DO NOT USE FOR: ot
| 1 | # Microsoft Orleans |
| 2 | |
| 3 | ## Start With Purpose |
| 4 | |
| 5 | Do not begin with an Orleans API. First state: |
| 6 | |
| 7 | 1. the business identity that owns the behavior; |
| 8 | 2. the invariant and what must survive activation or cluster failure; |
| 9 | 3. the required consistency, query shape, acknowledgement, durability, replay, fan-out, and timing. |
| 10 | |
| 11 | Then select the smallest Orleans primitive whose guarantees match those requirements. Reject Orleans when the problem is primarily shared-memory computation, a finite batch, relational querying, or global coordination with few independent entities. |
| 12 | |
| 13 | Inspect package versions for version-sensitive work. Orleans `10.2.1` ships `Microsoft.Orleans.DurableJobs*` and `Microsoft.Orleans.Journaling*` as `10.2.1-alpha.1`; treat them as experimental until that status changes. |
| 14 | |
| 15 | ## Mental Model |
| 16 | |
| 17 | - A **grain** is a virtual actor: a logical entity with stable identity, behavior, and optional state. It is not a process, row, DTO, controller, or background job. |
| 18 | - A **grain reference** is a location-transparent address. Getting a reference does not create a durable record or prove that an activation exists. |
| 19 | - An **activation** is an ephemeral in-memory execution instance. Orleans creates, places, moves, deactivates, and recreates it. Never equate activation lifetime with entity lifetime. |
| 20 | - A normal grain has at most one activation in the cluster by default and processes turns one at a time. This makes the grain a natural owner of per-identity invariants. |
| 21 | - A **silo** hosts activations. Silos form a cluster; external clients or a co-hosted `IGrainFactory` call grains. |
| 22 | - Calls are asynchronous messages even when they look like C# method calls. Network failure, timeout, serialization, retries, and duplicate side effects still matter. |
| 23 | - A grain can model a **digital twin** when its identity and behavior correspond to a device, user, order, room, account, or other real/domain entity. Digital twin is a use case, not the definition of every grain. |
| 24 | - Orleans gives logical ownership and turn-based execution. It does not make external side effects transactional, turn arbitrary data into a queryable database, or provide exactly-once execution by default. |
| 25 | |
| 26 | Model a grain as a tiny, always-addressable service per business identity: |
| 27 | |
| 28 | ```text |
| 29 | identity -> serialized decisions -> bounded current state -> messages/events/work |
| 30 | ``` |
| 31 | |
| 32 | ## Workflow |
| 33 | |
| 34 | 1. Inspect the solution, Orleans version, hosting topology, grain interfaces, providers, and tests. |
| 35 | 2. Identify domain identities and invariants. Prefer many independent, bounded entities over global coordinator grains. |
| 36 | 3. Choose state, communication, and time-based work from the tables below. |
| 37 | 4. Keep default non-reentrant scheduling and placement until a measured requirement justifies a change. |
| 38 | 5. Configure providers independently and test the failure model the design depends on. |
| 39 | |
| 40 | ## Current Upstream Notes |
| 41 | |
| 42 | - The July 2026 docs still separate the core virtual-actor model from provider-dependent storage, streaming, clustering, and deployment. Route from the required guarantee to the narrow primitive, then validate its configured provider and topology rather than generalizing from in-memory defaults. |
| 43 | |
| 44 | ## Choose the State Owner |
| 45 | |
| 46 | | Primitive | Purpose | Choose it when | Do not use it as | |
| 47 | |---|---|---|---| |
| 48 | | Activation fields | Fast, temporary state for one activation | The value is derived, cached, disposable, or safe to rebuild after deactivation/failure | Durable truth | |
| 49 | | `IPersistentState<T>` | Durable current state owned by one grain identity | The grain needs a bounded snapshot loaded on activation and explicitly written after commands | A general query database or cross-grain table | |
| 50 | | External database/repository | Queryable, indexed, relational, bulk, shared, or externally owned data | The system needs joins, search, reporting, set-based updates, independent access, or an existing system of record | A replacement for grain ownership when serialized per-entity decisions are still required | |
| 51 | | Grain plus database/read model | Separate command ownership from query/storage concerns | A grain owns invariants and a small control snapshot while a database owns large records, history, projections, or reporting | Two competing sources of truth without an explicit contract | |
| 52 | | `JournaledGrain<TState,TEvent>` | Persist domain events and reconstruct state | Audit history, business-event replay, log consistency, or multi-cluster event-sourced replication is a requirement | A default persistence choice for ordinary CRUD st |