Skip to content

Repository (Store)

The Repository pattern abstracts data access behind a collection-like interface, isolating business logic from persistence details. In Granit, repositories are named Stores and provide interchangeable implementations (InMemory, EF Core, Redis).

classDiagram
    class IBlobDescriptorStoreReader {
        <<interface>>
        +FindAsync(blobId) BlobDescriptor?
    }

    class IBlobDescriptorStoreWriter {
        <<interface>>
        +SaveAsync(descriptor)
        +UpdateAsync(descriptor)
    }

    class IFeatureStoreReader {
        <<interface>>
        +GetOrNullAsync(tenantId, name) string?
    }

    class IFeatureStoreWriter {
        <<interface>>
        +SetAsync(tenantId, name, value)
        +DeleteAsync(tenantId, name)
    }

    class ISettingStoreReader {
        <<interface>>
        +GetOrNullAsync(name, scope) string?
        +GetListAsync(scope) List
    }

    class ISettingStoreWriter {
        <<interface>>
        +SetAsync(name, value, scope)
        +DeleteAsync(name, scope)
    }

    class EfBlobDescriptorStore
    class InMemoryFeatureStore
    class EfCoreFeatureStore
    class EfCoreSettingStore

    IBlobDescriptorStoreReader <|.. EfBlobDescriptorStore
    IBlobDescriptorStoreWriter <|.. EfBlobDescriptorStore
    IFeatureStoreReader <|.. InMemoryFeatureStore
    IFeatureStoreWriter <|.. InMemoryFeatureStore
    IFeatureStoreReader <|.. EfCoreFeatureStore
    IFeatureStoreWriter <|.. EfCoreFeatureStore
    ISettingStoreReader <|.. EfCoreSettingStore
    ISettingStoreWriter <|.. EfCoreSettingStore
Store (port)LocationImplementations
IBlobDescriptorStoreReader / IBlobDescriptorStoreWritersrc/Granit.BlobStorage/EfBlobDescriptorStore
IFeatureStoreReader / IFeatureStoreWritersrc/Granit.Features/Store/InMemoryFeatureStore, EfCoreFeatureStore
IBackgroundJobStoreReader / IBackgroundJobStoreWritersrc/Granit.BackgroundJobs/Internal/InMemoryBackgroundJobStore, EfBackgroundJobStore
ISettingStoreReader / ISettingStoreWritersrc/Granit.Settings/Values/EfCoreSettingStore
IWebhookSubscriptionStoreReader / IWebhookSubscriptionStoreWritersrc/Granit.Webhooks/Abstractions/EfWebhookSubscriptionStore

Each EF Core store uses an isolated DbContext (not the application DbContext) via IDbContextFactory<T>.

The decoupling allows using InMemoryFeatureStore in development and EfCoreFeatureStore in production without changing application code. The Reader/Writer separation (CQRS) allows injecting only the required interface: read handlers only access the Reader, write handlers access the Writer. Unit tests use InMemory stores to avoid database dependencies.

// Read -- inject the Reader
IFeatureStoreReader reader = serviceProvider.GetRequiredService<IFeatureStoreReader>();
string? value = await reader.GetOrNullAsync(tenantId, "MaxPatients", ct);
// Write -- inject the Writer
IFeatureStoreWriter writer = serviceProvider.GetRequiredService<IFeatureStoreWriter>();
await writer.SetAsync(tenantId, "MaxPatients", "500", ct);