IFeatureFlagProvider Interface Reference¶
Overview¶
IFeatureFlagProvider is the abstraction layer for feature flag data persistence. It defines the low-level interface that different storage backends implement (MySQL, PostgreSQL, In-Memory, etc.). Most applications use this indirectly through IFeatureFlagService, but it can be accessed directly for advanced use cases.
Namespace: Flaggy.Abstractions
Note: Unless you're implementing a custom provider, use IFeatureFlagService instead, which adds automatic caching and convenience methods.
Core Methods¶
GetFlagAsync¶
Retrieves a single feature flag from the provider.
Task<FeatureFlag?> GetFlagAsync(string key, CancellationToken cancellationToken = default);
Parameters:
- key (string): The unique identifier for the feature flag
- cancellationToken (CancellationToken, optional): Token to cancel the async operation
Returns: Task<FeatureFlag?> - The FeatureFlag object or null if not found
Example:
var provider = serviceProvider.GetRequiredService<IFeatureFlagProvider>();
var flag = await provider.GetFlagAsync("feature-key");
if (flag != null)
{
Console.WriteLine($"Flag: {flag.Key}, Enabled: {flag.is_enabled}");
}
Use Cases: - Direct provider access in custom implementations - Bypassing cache for immediate data checks (when necessary) - Integration with external systems
GetAllFlagsAsync¶
Retrieves all feature flags from the provider.
Task<IEnumerable<FeatureFlag>> GetAllFlagsAsync(CancellationToken cancellationToken = default);
Parameters:
- cancellationToken (CancellationToken, optional): Token to cancel the async operation
Returns: Task<IEnumerable<FeatureFlag>> - Collection of all FeatureFlag objects
Example:
var provider = serviceProvider.GetRequiredService<IFeatureFlagProvider>();
var allFlags = await provider.GetAllFlagsAsync();
foreach (var flag in allFlags)
{
Console.WriteLine($"Key: {flag.Key}, Enabled: {flag.is_enabled}");
}
Use Cases: - Cache initialization - Bulk export operations - Verification of all stored flags
CRUD Operations¶
CreateFlagAsync¶
Creates a new feature flag in the storage backend.
Task<bool> CreateFlagAsync(FeatureFlag flag, CancellationToken cancellationToken = default);
Parameters:
- flag (FeatureFlag): The feature flag to create
- cancellationToken (CancellationToken, optional): Token to cancel the async operation
Returns: Task<bool> - True if created successfully, false otherwise
Example:
var provider = serviceProvider.GetRequiredService<IFeatureFlagProvider>();
var newFlag = new FeatureFlag
{
Key = "beta-feature",
is_enabled = false,
description = "Beta feature for testing",
Value = null
};
bool created = await provider.CreateFlagAsync(newFlag);
Implementation Notes: - Should return false if a flag with the same key already exists - created_at/updated_at timestamps should be set by the provider - Each provider implementation may have different behavior for duplicate keys
UpdateFlagAsync¶
Updates an existing feature flag in the storage backend.
Task<bool> UpdateFlagAsync(FeatureFlag flag, CancellationToken cancellationToken = default);
Parameters:
- flag (FeatureFlag): The feature flag with updated values
- cancellationToken (CancellationToken, optional): Token to cancel the async operation
Returns: Task<bool> - True if updated successfully, false if flag not found
Example:
var provider = serviceProvider.GetRequiredService<IFeatureFlagProvider>();
var flag = await provider.GetFlagAsync("beta-feature");
if (flag != null)
{
flag.is_enabled = true;
flag.Value = "production-ready";
bool updated = await provider.UpdateFlagAsync(flag);
}
Implementation Notes: - updated_at timestamp should be updated by the provider - Should preserve created_at timestamp - Return false if the flag key doesn't exist
DeleteFlagAsync¶
Deletes a feature flag from the storage backend.
Task<bool> DeleteFlagAsync(string key, CancellationToken cancellationToken = default);
Parameters:
- key (string): The unique identifier of the flag to delete
- cancellationToken (CancellationToken, optional): Token to cancel the async operation
Returns: Task<bool> - True if deleted successfully, false if flag not found
Example:
var provider = serviceProvider.GetRequiredService<IFeatureFlagProvider>();
bool deleted = await provider.DeleteFlagAsync("deprecated-feature");
if (deleted)
{
Console.WriteLine("Flag deleted successfully");
}
Use Cases: - Removing deprecated features - Cleanup during migrations - Archive management
Built-in Provider Implementations¶
Flaggy includes several provider implementations:
InMemoryFeatureFlagProvider¶
Stores flags in application memory. Useful for testing and development.
services.AddFlaggy(options =>
{
options.Provider = new InMemoryFeatureFlagProvider();
});
Characteristics: - No persistence across application restarts - Fast in-memory access - Suitable for unit tests and prototyping
MySqlFeatureFlagProvider¶
Stores flags in a MySQL database.
services.AddFlaggy(options =>
{
options.UseMySQL(
connectionString: "Server=localhost;Database=flaggy;User=root;Password=password"
);
});
Characteristics: - Persistent storage - Shared across multiple instances - Requires database setup and migrations
PostgreSQL Provider¶
Stores flags in a PostgreSQL database.
services.AddFlaggy(options =>
{
options.UsePostgreSQL(
connectionString: "Host=localhost;Database=flaggy;username=user;Password=password"
);
});
Characteristics: - Persistent storage - Shared across multiple instances - Enterprise-grade database support
Custom Provider Implementation¶
To implement a custom provider, create a class implementing IFeatureFlagProvider:
using Flaggy.Abstractions;
using Flaggy.Models;
public class CustomFeatureFlagProvider : IFeatureFlagProvider
{
public async Task<FeatureFlag?> GetFlagAsync(string key, CancellationToken cancellationToken = default)
{
// Retrieve flag from custom storage
return await FetchFromCustomStorage(key, cancellationToken);
}
public async Task<IEnumerable<FeatureFlag>> GetAllFlagsAsync(CancellationToken cancellationToken = default)
{
// Retrieve all flags from custom storage
return await FetchAllFromCustomStorage(cancellationToken);
}
public async Task<bool> CreateFlagAsync(FeatureFlag flag, CancellationToken cancellationToken = default)
{
// Save new flag to custom storage
return await SaveToCustomStorage(flag, cancellationToken);
}
public async Task<bool> UpdateFlagAsync(FeatureFlag flag, CancellationToken cancellationToken = default)
{
// Update flag in custom storage
return await UpdateInCustomStorage(flag, cancellationToken);
}
public async Task<bool> DeleteFlagAsync(string key, CancellationToken cancellationToken = default)
{
// Delete flag from custom storage
return await DeleteFromCustomStorage(key, cancellationToken);
}
// Custom implementation methods
private Task<FeatureFlag?> FetchFromCustomStorage(string key, CancellationToken ct) => throw new NotImplementedException();
private Task<IEnumerable<FeatureFlag>> FetchAllFromCustomStorage(CancellationToken ct) => throw new NotImplementedException();
private Task<bool> SaveToCustomStorage(FeatureFlag flag, CancellationToken ct) => throw new NotImplementedException();
private Task<bool> UpdateInCustomStorage(FeatureFlag flag, CancellationToken ct) => throw new NotImplementedException();
private Task<bool> DeleteFromCustomStorage(string key, CancellationToken ct) => throw new NotImplementedException();
}
Register your custom provider:
services.AddFlaggy(options =>
{
options.Provider = new CustomFeatureFlagProvider();
});
Provider vs Service¶
When to Use IFeatureFlagProvider Directly¶
- Implementing a custom provider
- Direct database access for advanced operations
- Bypassing cache intentionally
When to Use IFeatureFlagService (Recommended)¶
- Standard feature flag checks
- CRUD operations in applications
- Leveraging automatic caching and cache invalidation
- Using convenience extension methods
Example Comparison:
// Using Provider (direct access, no caching)
var provider = serviceProvider.GetRequiredService<IFeatureFlagProvider>();
var flag = await provider.GetFlagAsync("feature");
// Using Service (cached, with extensions)
var service = serviceProvider.GetRequiredService<IFeatureFlagService>();
var enabled = await service.is_enabledAsync("feature");
bool toggled = await service.ToggleFlagAsync("feature");
Performance Considerations¶
- GetAllFlagsAsync: Loads all flags. Suitable for initialization but avoid calling repeatedly without caching
- GetFlagAsync: Direct flag lookup. Should be fast for most providers
- Create/Update/Delete: Trigger cache invalidation in the service layer
- Cancellation tokens: Always respect cancellation tokens for long-running operations
Related Documentation¶
- Service Layer: IFeatureFlagService Interface
- Models: FeatureFlag Model
- Extensions: Extension Methods