Skip to main content

Why I built the Orion family

· 5 min read

Most .NET backends end up needing the same dozen things: validation, a distributed lock, an audit trail, an outbox, encrypted columns, typed IDs, idempotent endpoints, rate limits, retries. Each team builds them again, a little differently, and each version is wrong in its own small way.

The Orion family is my answer to that: small, focused libraries for those problems, built to one quality bar and published on NuGet.

Today it is 24 libraries in 89 packages, downloaded more than 350,000 times. This post is about why they exist, what holds them together, and what each one is for.

Primitives, not a framework​

Every Orion library solves one problem and stands on its own. You add OrionLock because you need a lock, not because you signed up for a platform. There is no deep dependency web between them; pick only what you need.

That also means saying no. OrionPatch is a transactional outbox, and its documentation says plainly that sagas are out of scope: if you need a process manager, reach for MassTransit or Wolverine instead. A library that knows where it stops is easier to trust than one that wants to be everything.

What holds the family together​

Building the libraries one after another, I noticed five things being implemented again and again, and drifting apart each time:

  • calling observers safely, so an observability outage can never break the code that does the real work;
  • OpenTelemetry naming, so every library's traces and metrics look alike on a dashboard;
  • a clock that tests can control;
  • the shape of options and DI registration;
  • the vocabulary for errors.

They now live once, in Orion.Abstractions. Its surface is frozen at 1.0: every contract stays source- and binary-compatible across the whole 1.x line, so one library can't break under a sibling's upgrade. The rules that go with it are written down and apply to every package in the family.

Two libraries grew out of that spine. OrionClock is a TimeProvider, so it drops into any .NET API that takes one, and it lets a test move time forward instead of waiting. OrionResult gives expected failures a shape: a zero-allocation Result<T>, an Option<T>, and a structured Error with a code, a kind and field errors.

What is in the family​

Input and the domain

  • OrionGuard: validation, guard clauses and DDD primitives, with integrations for ASP.NET Core, MediatR, Blazor, gRPC, SignalR and OpenTelemetry, source generators, and messages in 14 languages. The most used of the family.
  • OrionKey: strongly-typed IDs from one attribute, with equality, EF Core and JSON converters generated for you.

Data

  • OrionAudit: an automatic EF Core audit trail with JSON Patch diffs, and time travel to rebuild an entity as it was at any moment.
  • OrionVault: column-level encryption at rest for EF Core: AES-256-GCM, key rotation, and a blind index for searching encrypted values.
  • OrionPage: keyset pagination that stays fast on page 10,000, because OFFSET is a table scan.

Messaging and consistency

  • OrionPatch and OrionInbox: the two halves of reliable messaging. Enqueue inside the same SaveChanges transaction and dispatch at least once; on the other side, apply each message's effect exactly once.
  • OrionSaga: in-process saga orchestration; when a step fails, the completed steps are compensated.
  • OrionRelay: outbound webhooks, signed with HMAC-SHA256, retried with backoff and jitter.

Coordination

  • OrionLock: distributed locks with lease renewal and fencing tokens, over Redis, Postgres, SQL Server or EF Core.
  • OrionBeacon: leader election, so exactly one instance runs the job that must run once.

HTTP and APIs

  • OrionOnce: idempotency keys, so a retried request gets the stored response instead of running twice.
  • OrionEnvelope: one HTTP contract for the whole API: typed { data, meta } for success, RFC 9457 problem details for failure.
  • OrionRate: token-bucket and sliding-window rate limiting.
  • OrionGrant and OrionLedger: permissions and policies, and the full lifecycle of API keys.
  • OrionStream: Server-Sent Events with bounded buffers and heartbeats.

Cross-cutting

  • OrionLens carries a correlation id through async calls and across HTTP; OrionShade masks secrets and personal data before they reach a log; OrionCache does cache-aside without a stampede; OrionResilience retries with jitter over OrionClock, so a test of a retry takes no time.

Seeing them together​

Package pages show one library at a time. OrionShowcase shows how they work together: a production-shaped banking sample in which one money transfer passes through OrionGuard's validation, OrionLock's distributed locks, OrionAudit's change capture, OrionPatch's outbox, OrionKey's typed IDs and OrionVault's encrypted personal data. Assembling that story is much harder than any single package looks, and that is the point of the sample.

Why share them​

I wrote these because I needed them, and I publish them so that nobody else has to write them again. Every library is MIT-licensed, has its documentation on this site, and takes issues on GitHub. The ones that need no database or broker can be tried right here in the playground.

If you use one of them, or tried one and it didn't fit, I would like to know why. That is the most useful thing anyone can tell me.