Example
A support-ticket system, written once.
Declare storage intent once. Move backends without touching the domain contract.
In code
A manifest that travels across providers.
One declaration; the same indexes, concurrency, and serialization across every supported backend.
SupportTicketStorage.cs
C#1// Declare storage intent once — portable across providers.2public sealed class SupportTicketStorage : StorageManifest<SupportTicket>3{4public SupportTicketStorage() : base("support_tickets")5{6Serialization.UseJson();7Concurrency.UseOptimistic(t => t.Version);89// Declared indexes — portable query semantics10Indexes.Unique(t => t.TicketNumber);11Indexes.On(t => t.CustomerId);12Indexes.On(t => t.Status);13Indexes.On(t => t.AssigneeId);14Indexes.On(t => t.Priority);15}16}
Example
A support-ticket system, written once.
The same manifest carries from local SQLite through production PostgreSQL or MongoDB — without touching the domain.
- 01DeclareDefine ticket storage in one StorageManifest — fields, indexes, concurrency.
- 02Materialize locallyRun against SQLite for development and tests with zero infrastructure.
- 03Promote backendMove to PostgreSQL or MongoDB without changing the domain contract.
- 04Query by indexAll access goes through declared indexes — portable across providers.
- 05Preserve concurrencyOptimistic version tokens travel with the manifest, not the provider.
same call site, any provider
var ticket = await store.GetByIndexAsync(
t => t.TicketNumber,
"TCK-10472"
);
ticket.Status = TicketStatus.Resolved;
await store.UpdateAsync(ticket); // optimistic
Runs unchanged on:
SQLite PG Mongo
Build persistence on declared intent.
Use Groundwork as an MIT-licensed foundation for provider-neutral persistence in .NET applications.