FractalDataWorks.Abstractions
0.9.0-alpha.1011.ged0a6c6e98
dotnet add package FractalDataWorks.Abstractions --version 0.9.0-alpha.1011.ged0a6c6e98
NuGet\Install-Package FractalDataWorks.Abstractions -Version 0.9.0-alpha.1011.ged0a6c6e98
<PackageReference Include="FractalDataWorks.Abstractions" Version="0.9.0-alpha.1011.ged0a6c6e98" />
<PackageVersion Include="FractalDataWorks.Abstractions" Version="0.9.0-alpha.1011.ged0a6c6e98" />
<PackageReference Include="FractalDataWorks.Abstractions" />
paket add FractalDataWorks.Abstractions --version 0.9.0-alpha.1011.ged0a6c6e98
#r "nuget: FractalDataWorks.Abstractions, 0.9.0-alpha.1011.ged0a6c6e98"
#:package FractalDataWorks.Abstractions@0.9.0-alpha.1011.ged0a6c6e98
#addin nuget:?package=FractalDataWorks.Abstractions&version=0.9.0-alpha.1011.ged0a6c6e98&prerelease
#tool nuget:?package=FractalDataWorks.Abstractions&version=0.9.0-alpha.1011.ged0a6c6e98&prerelease
FractalDataWorks.Abstractions
Core abstractions and base interfaces for the FractalDataWorks framework. This package provides the fundamental contracts that all other framework components build upon, ensuring consistent patterns across services, commands, results, and configuration.
Overview
FractalDataWorks.Abstractions serves as the foundation layer of the FractalDataWorks ecosystem, providing:
- Core Service Contracts -
IGenericServiceandIServiceFactoryinterfaces - Command Infrastructure -
IGenericCommandbase interface for all commands - Result Contracts -
IGenericResultandIGenericResult<T>for Railway-Oriented Programming - Message Contracts -
IGenericMessagefor structured operation feedback - Configuration Contracts -
IGenericConfigurationfor type-safe settings
This package is designed for maximum compatibility and is placed in the abstractions layer to avoid circular dependencies with source generators and other framework components.
Target Frameworks: .NET Standard 2.0, .NET 10.0 Dependencies: FractalDataWorks.Collections
Why This Package Exists
The framework follows a strict dependency hierarchy to prevent circular references:
FractalDataWorks.Abstractions (this package)
↓
FractalDataWorks.Results
↓
FractalDataWorks.Services.Abstractions
↓
FractalDataWorks.Services (concrete implementations)
By placing foundational interfaces in this package, we ensure:
- Source generators can reference these interfaces without circular dependencies
- All framework components share consistent base contracts
- Maximum compatibility through .NET Standard 2.0 support
- Clean separation of concerns
Core Interfaces
IGenericService
Base interface for all services in the framework. Services represent operational components that execute commands and return results.
public interface IGenericService
{
/// <summary>
/// Gets the unique identifier for this service instance.
/// </summary>
string Id { get; }
/// <summary>
/// Gets the display name of the service.
/// </summary>
string ServiceType { get; }
/// <summary>
/// Gets a value indicating whether the service is currently available for use.
/// </summary>
bool IsAvailable { get; }
/// <summary>
/// Executes a data command and returns a typed result.
/// </summary>
Task<IGenericResult<T>> Execute<T>(IGenericCommand command, CancellationToken cancellationToken);
/// <summary>
/// Executes a data command and returns a non-typed result.
/// </summary>
Task<IGenericResult> Execute(IGenericCommand command, CancellationToken cancellationToken);
}
Key Characteristics:
- All services have a unique identifier and type name
- Services report availability status
- Command execution uses the Railway-Oriented Programming pattern via
IGenericResult - Supports both typed and non-typed command execution
- Full cancellation token support for async operations
Usage Example:
public class DatabaseService : IGenericService
{
public string Id => "db-service-001";
public string ServiceType => "Database";
public bool IsAvailable => _connection?.State == ConnectionState.Open;
public async Task<IGenericResult<T>> Execute<T>(
IGenericCommand command,
CancellationToken cancellationToken)
{
if (!IsAvailable)
return GenericResult<T>.Failure("Service unavailable");
// Execute command logic
var result = await ExecuteCommandAsync<T>(command, cancellationToken);
return GenericResult<T>.Success(result);
}
public async Task<IGenericResult> Execute(
IGenericCommand command,
CancellationToken cancellationToken)
{
if (!IsAvailable)
return GenericResult.Failure("Service unavailable");
await ExecuteCommandAsync(command, cancellationToken);
return GenericResult.Success();
}
}
IGenericCommand
Base interface for all commands in the framework. Commands represent operations to be executed by services.
public interface IGenericCommand
{
/// <summary>
/// Gets the unique identifier for this command instance.
/// </summary>
Guid CommandId { get; }
/// <summary>
/// Gets the timestamp when this command was created.
/// </summary>
DateTime CreatedAt { get; }
/// <summary>
/// Gets the command type identifier.
/// </summary>
string CommandType { get; }
}
Key Characteristics:
- Every command has a unique GUID for tracking and correlation
- Creation timestamp enables performance measurement and timeout handling
- Command type identifier enables routing and logging
- Designed for immutability - properties should be set at creation
Usage Example:
public class QueryCommand : IGenericCommand
{
public Guid CommandId { get; } = Guid.NewGuid();
public DateTime CreatedAt { get; } = DateTime.UtcNow;
public string CommandType => "Query";
public string Query { get; }
public object[] Parameters { get; }
public QueryCommand(string query, params object[] parameters)
{
Query = query;
Parameters = parameters;
}
}
// Usage
var command = new QueryCommand("SELECT * FROM Users WHERE Id = @id", userId);
var result = await service.Execute<User>(command, cancellationToken);
IGenericResult and IGenericResult<T>
Result interfaces that implement the Railway-Oriented Programming pattern for handling operation outcomes.
public interface IGenericResult
{
/// <summary>
/// Gets a value indicating whether the operation succeeded.
/// </summary>
bool IsSuccess { get; }
/// <summary>
/// Gets a value indicating whether the operation failed.
/// </summary>
bool IsFailure { get; }
/// <summary>
/// Gets a value indicating whether this represents an empty result.
/// </summary>
bool IsEmpty { get; }
/// <summary>
/// Gets a value indicating whether this result represents an error.
/// </summary>
bool Error { get; }
/// <summary>
/// Gets the most recent message (LIFO - Last In, First Out).
/// </summary>
string? CurrentMessage { get; }
/// <summary>
/// Gets the collection of messages associated with this result.
/// </summary>
IReadOnlyList<IGenericMessage> Messages { get; }
}
public interface IGenericResult<out T> : IGenericResult
{
/// <summary>
/// Gets the result value if successful.
/// </summary>
T? Value { get; }
}
Key Characteristics:
- Replaces exceptions for expected failure scenarios
IsSuccessandIsFailureare mutually exclusiveErroris an alias forIsFailureIsEmptyindicates no messages (implementation-specific)CurrentMessageprovides the most recent message using LIFO ordering- Generic variant carries typed values on success
- Covariant (
out T) for safe variance
Usage Patterns:
// Check result and handle accordingly
IGenericResult<User> result = await service.Execute<User>(command, cancellationToken);
if (result.IsSuccess)
{
User user = result.Value;
Console.WriteLine($"Found user: {user.Name}");
}
else
{
Console.WriteLine($"Error: {result.CurrentMessage}");
foreach (var msg in result.Messages)
{
Console.WriteLine($" - {msg.Message}");
}
}
// Railway-oriented composition
IGenericResult<int> ageResult = result
.Map(user => user.Age)
.Match(
success: age => age > 18 ? GenericResult<int>.Success(age) : GenericResult<int>.Failure("Under age"),
failure: error => GenericResult<int>.Failure(error)
);
IGenericMessage
Interface for structured messages that provide context about operations.
public interface IGenericMessage
{
/// <summary>
/// Gets the message text.
/// </summary>
string Message { get; }
/// <summary>
/// Gets the message code or identifier.
/// </summary>
string? Code { get; }
/// <summary>
/// Gets the source component or operation that generated the message.
/// </summary>
string? Source { get; }
}
public interface IGenericMessage<TSeverity> : IGenericMessage
where TSeverity : Enum
{
/// <summary>
/// Gets the severity level of the message.
/// </summary>
TSeverity Severity { get; }
}
Key Characteristics:
- Provides structured feedback about operations
- Optional
Codefor programmatic message handling - Optional
Sourcefor debugging and tracing - Generic variant supports strongly-typed severity levels
- Integrates with
IGenericResultfor detailed operation feedback
Usage Example:
public enum MessageSeverity
{
Information,
Warning,
Error,
Critical
}
public class GenericMessage : IGenericMessage<MessageSeverity>
{
public string Message { get; }
public string? Code { get; }
public string? Source { get; }
public MessageSeverity Severity { get; }
public GenericMessage(
string message,
MessageSeverity severity = MessageSeverity.Information,
string? code = null,
string? source = null)
{
Message = message;
Severity = severity;
Code = code;
Source = source;
}
}
// Usage in results
var messages = new List<IGenericMessage>
{
new GenericMessage("Connection established", MessageSeverity.Information, source: "DatabaseService"),
new GenericMessage("Using cached credentials", MessageSeverity.Warning, code: "CACHE_001")
};
return GenericResult.Success(messages);
IGenericConfiguration
Base interface for all configuration objects in the framework.
public interface IGenericConfiguration
{
/// <summary>
/// Gets the unique identifier for this configuration instance.
/// </summary>
int Id { get; }
/// <summary>
/// Gets the name of this configuration for lookup and display.
/// </summary>
string Name { get; }
/// <summary>
/// Gets the section name for this configuration in appsettings.
/// </summary>
string SectionName { get; }
}
public interface IGenericConfiguration<T> : IGenericConfiguration
where T : IGenericConfiguration<T>
{
/// <summary>
/// Gets the service type identifier for this configuration.
/// </summary>
string ServiceType { get; }
}
Key Characteristics:
- Every configuration has a unique integer identifier
Nameprovides human-readable identificationSectionNamemaps to configuration file sections (e.g., appsettings.json)- Generic variant uses CRTP (Curiously Recurring Template Pattern) for type safety
ServiceTypelinks configuration to specific service implementations
Usage Example:
// appsettings.json
{
"Database": {
"ConnectionString": "Server=localhost;Database=MyDb",
"CommandTimeout": 30
}
}
// Configuration class
public class DatabaseConfiguration : IGenericConfiguration<DatabaseConfiguration>
{
public int Id => 1;
public string Name => "Primary Database";
public string SectionName => "Database";
public string ServiceType => "SqlServer";
public string ConnectionString { get; set; } = string.Empty;
public int CommandTimeout { get; set; } = 30;
}
// Loading configuration
var config = configuration
.GetSection("Database")
.Get<DatabaseConfiguration>();
IServiceFactory
Generic factory interface for creating service instances with configuration.
public interface IServiceFactory
{
/// <summary>
/// Creates a service instance of the specified type.
/// </summary>
IGenericResult<T> Create<T>(IGenericConfiguration configuration) where T : IGenericService;
/// <summary>
/// Creates a service instance.
/// </summary>
IGenericResult<IGenericService> Create(IGenericConfiguration configuration);
}
public interface IServiceFactory<TService> : IServiceFactory
{
/// <summary>
/// Creates a service instance of the specified type.
/// </summary>
new IGenericResult<TService> Create(IGenericConfiguration configuration);
}
public interface IServiceFactory<TService, in TConfiguration> : IServiceFactory<TService>
where TConfiguration : IGenericConfiguration
{
/// <summary>
/// Creates a service instance using the strongly-typed configuration.
/// </summary>
IGenericResult<TService> Create(TConfiguration configuration);
}
Key Characteristics:
- Progressive constraint refinement from base to specific implementations
- Returns
IGenericResultfor consistent error handling - Supports both generic and non-generic service creation
- Strongly-typed configuration binding in most specific variant
- Enables factory pattern for service instantiation
Usage Example:
public class DatabaseServiceFactory : IServiceFactory<IDatabaseService, DatabaseConfiguration>
{
private readonly ILogger<DatabaseServiceFactory> _logger;
public DatabaseServiceFactory(ILogger<DatabaseServiceFactory> logger)
{
_logger = logger;
}
public IGenericResult<IDatabaseService> Create(DatabaseConfiguration configuration)
{
try
{
var service = new DatabaseService(
configuration.ConnectionString,
configuration.CommandTimeout,
_logger);
return GenericResult<IDatabaseService>.Success(service);
}
catch (Exception ex)
{
return GenericResult<IDatabaseService>.Failure($"Failed to create service: {ex.Message}");
}
}
public IGenericResult<IDatabaseService> Create(IGenericConfiguration configuration)
{
if (configuration is not DatabaseConfiguration dbConfig)
return GenericResult<IDatabaseService>.Failure("Invalid configuration type");
return Create(dbConfig);
}
public IGenericResult<IGenericService> Create(IGenericConfiguration configuration)
{
return Create(configuration as DatabaseConfiguration
?? throw new ArgumentException("Invalid configuration"));
}
}
Framework Integration Patterns
Service Registration
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddDatabaseService(
this IServiceCollection services,
IConfiguration configuration)
{
// Register configuration
services.Configure<DatabaseConfiguration>(
configuration.GetSection("Database"));
// Register factory
services.AddSingleton<IServiceFactory<IDatabaseService, DatabaseConfiguration>,
DatabaseServiceFactory>();
// Register service
services.AddScoped<IDatabaseService>(sp =>
{
var config = sp.GetRequiredService<IOptions<DatabaseConfiguration>>().Value;
var factory = sp.GetRequiredService<IServiceFactory<IDatabaseService, DatabaseConfiguration>>();
var result = factory.Create(config);
return result.IsSuccess
? result.Value
: throw new InvalidOperationException(result.CurrentMessage);
});
return services;
}
}
Command Execution Flow
public class DataOperationHandler
{
private readonly IGenericService _service;
public DataOperationHandler(IGenericService service)
{
_service = service;
}
public async Task<IGenericResult<TResult>> ExecuteAsync<TResult>(
IGenericCommand command,
CancellationToken cancellationToken = default)
{
// Check service availability
if (!_service.IsAvailable)
{
return GenericResult<TResult>.Failure(
$"Service '{_service.ServiceType}' is not available");
}
// Execute command
var result = await _service.Execute<TResult>(command, cancellationToken);
// Log execution
if (result.IsSuccess)
{
Console.WriteLine($"Command {command.CommandId} executed successfully");
}
else
{
Console.WriteLine($"Command {command.CommandId} failed: {result.CurrentMessage}");
foreach (var msg in result.Messages)
{
Console.WriteLine($" [{msg.Source}] {msg.Message}");
}
}
return result;
}
}
Railway-Oriented Error Handling
public async Task<IGenericResult<Order>> ProcessOrderAsync(
int orderId,
CancellationToken cancellationToken)
{
// Chain of operations using railway-oriented programming
var getOrderCommand = new GetOrderCommand(orderId);
var orderResult = await _service.Execute<Order>(getOrderCommand, cancellationToken);
if (orderResult.IsFailure)
return orderResult; // Propagate failure
var order = orderResult.Value;
// Validate order
if (order.Status != OrderStatus.Pending)
return GenericResult<Order>.Failure("Order is not in pending status");
// Process payment
var paymentCommand = new ProcessPaymentCommand(order.Id, order.TotalAmount);
var paymentResult = await _service.Execute(paymentCommand, cancellationToken);
if (paymentResult.IsFailure)
return GenericResult<Order>.Failure($"Payment failed: {paymentResult.CurrentMessage}");
// Update order status
var updateCommand = new UpdateOrderStatusCommand(order.Id, OrderStatus.Completed);
var updateResult = await _service.Execute<Order>(updateCommand, cancellationToken);
return updateResult; // Return final result
}
Architecture Guidelines
When to Implement These Interfaces
- IGenericService: Implement for all operational components (database services, API clients, file handlers)
- IGenericCommand: Implement for all operations that services can execute
- IGenericConfiguration: Implement for all configuration classes that bind to appsettings
- IServiceFactory: Implement when services require complex initialization or dependency injection
Interface Hierarchy Best Practices
- Keep This Package Minimal: Only add interfaces that are truly foundational
- Avoid Implementation Details: Keep interfaces abstract and implementation-agnostic
- Use Concrete Implementations Elsewhere: Implement these interfaces in higher-level packages
- Maintain .NET Standard 2.0 Compatibility: Ensures maximum reach
- Document Contract Expectations: Clearly specify expected behavior in XML comments
Relationship to Other Packages
FractalDataWorks.Abstractions (foundational contracts)
├── Extended by: FractalDataWorks.Results (IGenericResult implementations)
├── Extended by: FractalDataWorks.Messages (IGenericMessage implementations)
├── Extended by: FractalDataWorks.Commands.Abstractions (command types)
├── Extended by: FractalDataWorks.Services.Abstractions (service types)
└── Used by: All framework packages (universal dependency)
Best Practices
- Always Return Results, Not Exceptions: Use
IGenericResultfor expected failures - Use Cancellation Tokens: Support graceful cancellation in all async operations
- Command Immutability: Design commands as immutable data structures
- Service Availability Checks: Always check
IsAvailablebefore executing commands - Structured Messages: Use
IGenericMessagefor rich feedback instead of plain strings - Factory Pattern: Use
IServiceFactoryfor complex service initialization - Configuration Binding: Map
SectionNameto configuration file sections - Unique Identifiers: Ensure
CommandIdand serviceIdare unique for tracking
Dependencies
- FractalDataWorks.Collections: Provides TypeCollections infrastructure for extensible type systems
Multi-Targeting
This package multi-targets:
- .NET Standard 2.0: Maximum compatibility with older frameworks and Blazor WASM
- .NET 10.0: Access to latest runtime features and performance improvements
Project Configuration
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net10.0</TargetFrameworks>
<RootNamespace>FractalDataWorks</RootNamespace>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Notes
- This package is intentionally minimal to avoid circular dependencies
- Extended versions of these interfaces (with additional functionality) exist in higher-level packages
- All interfaces follow generic naming conventions (
IGeneric*) for consistency - The framework uses Railway-Oriented Programming extensively through
IGenericResult
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- FractalDataWorks.Collections (>= 0.9.0-alpha.1011.ged0a6c6e98)
-
net10.0
- FractalDataWorks.Collections (>= 0.9.0-alpha.1011.ged0a6c6e98)
NuGet packages (10)
Showing the top 5 NuGet packages that depend on FractalDataWorks.Abstractions:
| Package | Downloads |
|---|---|
|
FractalDataWorks.Configuration
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
|
|
FractalDataWorks.Messages
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
|
|
FractalDataWorks.ServiceTypes
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
|
|
FractalDataWorks.Services.Abstractions
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
|
|
FractalDataWorks.Data.DataStores
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.9.0-alpha.1011.ged0a6c6e98 | 0 | 11/18/2025 |
| 0.9.0-alpha.1010.gecd88aac50 | 0 | 11/18/2025 |
| 0.9.0-alpha.1009.g7f6817e985 | 0 | 11/18/2025 |
| 0.9.0-alpha.1006.gf287016c0c | 0 | 11/18/2025 |
| 0.8.0-alpha.1011 | 0 | 11/18/2025 |
| 0.7.0-alpha.1022 | 172 | 11/3/2025 |
| 0.7.0-alpha.1021 | 194 | 11/3/2025 |
| 0.7.0-alpha.1008 | 121 | 11/2/2025 |
| 0.7.0-alpha.1006 | 143 | 10/30/2025 |
| 0.7.0-alpha.1005 | 142 | 10/30/2025 |
| 0.7.0-alpha.1004 | 154 | 10/30/2025 |
| 0.7.0-alpha.1001 | 139 | 10/29/2025 |
| 0.6.0-alpha.1006 | 133 | 10/29/2025 |
| 0.6.0-alpha.1005 | 197 | 10/28/2025 |
| 0.6.0-alpha.1004 | 146 | 10/28/2025 |
| 0.6.0-alpha.1002 | 125 | 10/28/2025 |