FractalDataWorks.Commands.Data.Abstractions
0.9.5-alpha.1008
dotnet add package FractalDataWorks.Commands.Data.Abstractions --version 0.9.5-alpha.1008
NuGet\Install-Package FractalDataWorks.Commands.Data.Abstractions -Version 0.9.5-alpha.1008
<PackageReference Include="FractalDataWorks.Commands.Data.Abstractions" Version="0.9.5-alpha.1008" />
<PackageVersion Include="FractalDataWorks.Commands.Data.Abstractions" Version="0.9.5-alpha.1008" />
<PackageReference Include="FractalDataWorks.Commands.Data.Abstractions" />
paket add FractalDataWorks.Commands.Data.Abstractions --version 0.9.5-alpha.1008
#r "nuget: FractalDataWorks.Commands.Data.Abstractions, 0.9.5-alpha.1008"
#:package FractalDataWorks.Commands.Data.Abstractions@0.9.5-alpha.1008
#addin nuget:?package=FractalDataWorks.Commands.Data.Abstractions&version=0.9.5-alpha.1008&prerelease
#tool nuget:?package=FractalDataWorks.Commands.Data.Abstractions&version=0.9.5-alpha.1008&prerelease
FractalDataWorks.Commands.Data.Abstractions
Universal data command abstractions for the FractalDataWorks Developer Kit.
Overview
This project provides the core abstractions for universal data commands that work across any data store type (SQL, REST, GraphQL, File, etc.). The key principle is "write once, run anywhere" - the same IDataCommand can execute against different backend stores simply by changing configuration.
Core Concepts
- IDataCommand - Universal command interface representing data operations
- IDataCommandTranslator - Converts universal commands to domain-specific commands (SQL, REST, etc.)
- DataCommandTranslators - TypeCollection for compile-time and runtime translator registration
- Built-In Translator Pattern - Connections own their translators (injected via DI or Factory)
Architecture (Built-In Translator Pattern)
IDataCommand (universal)
↓
DataGateway (simple routing)
↓
IDataConnection (owns translator)
├── Connection.Execute(IDataCommand)
├── _translator.TranslateAsync(IDataCommand)
└── Execute IConnectionCommand
Flow Example (Built-In Pattern)
// 1. User creates universal command
var command = new QueryDataCommand
{
ConnectionName = "PrimaryDB", // Which connection
ContainerName = "Users", // Which container (table/endpoint/file)
// ... filter, projection, etc.
};
// 2. DataGateway routes to connection (simple lookup, no translator provider)
var connection = await connectionProvider.GetConnection("PrimaryDB");
// 3. Connection owns translator - translates internally
var result = await connection.Execute<User>(command);
// Connection flow:
// → _translator.TranslateAsync(command) → IConnectionCommand (SQL)
// → Execute SQL: SELECT * FROM [Users] WHERE ...
// → Return IGenericResult<User>
Core Interfaces
IDataCommand
Base interface for all data commands. Extends IGenericCommand from the Commands.Abstractions project.
public interface IDataCommand : IGenericCommand
{
/// <summary>
/// Gets the container name (table, collection, endpoint, file path).
/// </summary>
string ContainerName { get; }
/// <summary>
/// Gets metadata for the command (connection hints, caching, etc.).
/// </summary>
IReadOnlyDictionary<string, object> Metadata { get; }
}
Generic Variants:
IDataCommand<TResult>- Command with typed resultIDataCommand<TResult, TInput>- Command with typed input and result
Usage Examples:
// Simple query command
public sealed class GetUserCommand : IDataCommand<User>
{
public string ConnectionName => "PrimaryDB";
public string ContainerName => "Users";
public int UserId { get; init; }
}
// Insert command
public sealed class CreateUserCommand : IDataCommand<int, UserDto>
{
public string ConnectionName => "PrimaryDB";
public string ContainerName => "Users";
public UserDto User { get; init; }
}
IDataCommandTranslator
Interface for translating universal commands to domain-specific commands.
public interface IDataCommandTranslator
{
/// <summary>
/// Gets the domain name this translator targets (Sql, Rest, File, GraphQL, etc.).
/// </summary>
string DomainName { get; }
/// <summary>
/// Translates a data command to a connection-specific command.
/// </summary>
Task<IGenericResult<IConnectionCommand>> Translate(
IDataCommand command,
CancellationToken cancellationToken = default);
}
Translator Examples:
// SQL Server Translator
public sealed class MsSqlDataCommandTranslator : IDataCommandTranslator
{
public string DomainName => "MsSql";
public Task<IGenericResult<IConnectionCommand>> Translate(
IDataCommand command,
CancellationToken cancellationToken = default)
{
// Convert command.ContainerName → SELECT * FROM [TableName]
// Convert filters → WHERE clauses
// Convert projections → SELECT columns
// Return SQL ConnectionCommand
}
}
// REST API Translator
public sealed class RestDataCommandTranslator : IDataCommandTranslator
{
public string DomainName => "Rest";
public Task<IGenericResult<IConnectionCommand>> Translate(
IDataCommand command,
CancellationToken cancellationToken = default)
{
// Convert command.ContainerName → GET /api/endpoint
// Convert filters → OData $filter query params
// Convert projections → $select query params
// Return HTTP ConnectionCommand
}
}
DataCommandTranslators (TypeCollection)
TypeCollection for compile-time and runtime translator registration. Used for metadata and connection discovery.
[TypeCollection(typeof(DataCommandTranslatorBase), typeof(IDataCommandTranslator), typeof(DataCommandTranslators))]
public abstract partial class DataCommandTranslators : TypeCollectionBase<DataCommandTranslatorBase, IDataCommandTranslator>
{
// Compile-time registration (via [TypeOption] attribute)
// Source generator discovers translators and creates static properties
// Runtime registration (called by connection types during registration)
public static void Register(string name, Type translatorType)
{
// Connections register their translators for discovery/metadata
// Example: MsSqlConnection registers MsSqlTranslator
}
// Get translator type by domain name (for metadata only)
public static Type? GetTranslatorType(string domainName)
{
// Checks both compile-time and runtime registered translators
}
}
Registration Examples (Built-In Pattern):
// Compile-time registration (in translator project) - for metadata
[TypeOption(typeof(DataCommandTranslators), "MsSql")]
public sealed class MsSqlTranslatorType : DataCommandTranslatorBase
{
// ❌ WRONG - Do NOT add Instance property (old/incorrect pattern)
// public static MsSqlTranslatorType Instance { get; } = new();
private MsSqlTranslatorType() : base(1, "MsSql", "Sql") { }
}
// Runtime registration (in connection's ServiceTypeOption.Configure())
public override void Configure(IConfiguration configuration)
{
// Register translator type for metadata/discovery
DataCommandTranslators.Register("MsSql", typeof(MsSqlDataCommandTranslator));
}
public override void Register(IServiceCollection services)
{
// Register translator in DI - connection will inject it
services.AddScoped<IDataCommandTranslator, MsSqlDataCommandTranslator>();
// Register connection with translator injected via DI (deterministic pattern)
services.AddScoped<IDataConnection, MsSqlConnection>();
}
Built-In Translator Pattern
No IDataCommandTranslatorProvider - Connections own their translators directly.
Deterministic Pattern (SQL):
public sealed class MsSqlConnection : IDataConnection
{
private readonly IDataCommandTranslator _translator;
private readonly ILogger<MsSqlConnection> _logger;
// Translator injected via constructor DI
public MsSqlConnection(
IDataCommandTranslator translator,
ILogger<MsSqlConnection> logger)
{
_translator = translator;
_logger = logger;
}
public async Task<IGenericResult<T>> Execute<T>(IDataCommand command)
{
// Translate using owned translator
var translatedResult = await _translator.TranslateAsync(command);
if (!translatedResult.IsSuccess) return GenericResult<T>.Failure(translatedResult.Message);
// Execute translated command
return await ExecuteSqlCommand<T>(translatedResult.Value);
}
}
Flexible Pattern (HTTP):
public sealed class HttpConnection : IDataConnection
{
private readonly IDataCommandTranslator _translator;
private readonly ILogger<HttpConnection> _logger;
// Translator selected by factory based on config.TranslatorLanguage
public HttpConnection(
HttpConnectionConfig config,
IDataCommandTranslatorFactory translatorFactory,
ILogger<HttpConnection> logger)
{
// Factory selects: "Rest", "GraphQL", "OData", etc.
_translator = translatorFactory.GetTranslator(config.TranslatorLanguage);
_logger = logger;
}
public async Task<IGenericResult<T>> Execute<T>(IDataCommand command)
{
// Translate using owned translator
var translatedResult = await _translator.TranslateAsync(command);
if (!translatedResult.IsSuccess) return GenericResult<T>.Failure(translatedResult.Message);
// Execute HTTP request
return await ExecuteHttpCommand<T>(translatedResult.Value);
}
}
Project Structure
FractalDataWorks.Commands.Data.Abstractions/
├── Commands/
│ ├── IDataCommand.cs # Base command interface
│ ├── IDataCommand{TResult}.cs # Generic command interface
│ ├── IDataCommand{TResult,TInput}.cs # Generic command with input
│ ├── DataCommandBase.cs # Base command implementation
│ ├── DataCommandBase{TResult}.cs # Generic base implementation
│ ├── DataCommandBase{TResult,TInput}.cs # Generic base with input
│ └── DataCommands.cs # TypeCollection for commands
├── Translators/
│ ├── IDataCommandTranslator.cs # Translator interface
│ ├── DataCommandTranslatorBase.cs # Base translator implementation
│ ├── DataCommandTranslators.cs # TypeCollection for metadata/discovery
│ └── Logging/
│ └── DataCommandTranslatorLog.cs # Source-generated logging
├── Expressions/
│ ├── IFilterExpression.cs # Filter expression interface
│ ├── IProjectionExpression.cs # Projection interface
│ └── ... (other expression types)
├── Operators/
│ ├── FilterOperators.cs # TypeCollection of filter operators
│ ├── LogicalOperator.cs # AND/OR EnhancedEnum
│ └── SortDirection.cs # ASC/DESC EnhancedEnum
└── Messages/
└── DataCommandMessages.cs # Structured messages
Usage Examples
Example 1: Simple Query Command
using FractalDataWorks.Commands.Data.Abstractions;
public sealed class GetUsersCommand : DataCommandBase<IEnumerable<User>>
{
public GetUsersCommand(string connectionName)
: base(connectionName, "Users")
{
}
// Optional: Add filter metadata
public string? NameFilter { get; init; }
}
// Execute via DataGateway
var command = new GetUsersCommand("PrimaryDB")
{
NameFilter = "John"
};
var result = await dataGateway.Execute<IEnumerable<User>>(command);
if (result.IsSuccess)
{
foreach (var user in result.Value)
{
Console.WriteLine($"User: {user.Name}");
}
}
Example 2: Translator Registration (in Connection ServiceTypeOption)
[ServiceTypeOption(typeof(ConnectionTypes), "MsSql")]
public sealed class MsSqlConnectionType : ConnectionTypeBase<...>
{
public override void Configure(IConfiguration configuration)
{
// Register translator at runtime
DataCommandTranslators.Register("MsSql", typeof(MsSqlDataCommandTranslator));
}
public override void Register(IServiceCollection services)
{
// Register translator in DI
services.AddScoped<IDataCommandTranslator, MsSqlDataCommandTranslator>();
// Register connection
services.AddScoped<IDataConnection, MsSqlConnection>();
}
}
Example 3: Custom Translator Implementation
public sealed class CustomApiTranslator : IDataCommandTranslator
{
private readonly ILogger<CustomApiTranslator> _logger;
public CustomApiTranslator(ILogger<CustomApiTranslator> logger)
{
_logger = logger;
}
public string DomainName => "CustomApi";
public Task<IGenericResult<IConnectionCommand>> Translate(
IDataCommand command,
CancellationToken cancellationToken = default)
{
try
{
// Build custom API request from command
var apiUrl = $"/api/{command.ContainerName}";
// Add filters from metadata
if (command.Metadata.TryGetValue("Filter", out var filter))
{
apiUrl += $"?filter={filter}";
}
// Create HTTP connection command
var connectionCommand = new HttpConnectionCommand
{
Method = "GET",
Url = apiUrl
};
return Task.FromResult(GenericResult<IConnectionCommand>.Success(connectionCommand));
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to translate command for {Container}", command.ContainerName);
return Task.FromResult(GenericResult<IConnectionCommand>.Failure(ex.Message));
}
}
}
Best Practices
1. Translator Design
✅ DO: Keep translators focused on one domain (SQL, REST, etc.) ✅ DO: Use source-generated logging for translation operations ✅ DO: Return descriptive failure messages ✅ DO: Validate commands before translation
❌ DON'T: Mix multiple domain translations in one translator ❌ DON'T: Throw exceptions - use Result pattern ❌ DON'T: Use switch statements - use TypeCollections/polymorphism
2. Command Design
✅ DO: Keep commands simple and focused ✅ DO: Use metadata for optional/hint information ✅ DO: Provide typed generic variants when possible
❌ DON'T: Put translation logic in commands ❌ DON'T: Reference domain-specific types (SQL, REST) in commands
3. Registration
✅ DO: Use [TypeOption] for compile-time translator discovery
✅ DO: Use DataCommandTranslators.Register() for connection-provided translators
✅ DO: Register translators in ServiceTypeOption.Configure()
❌ DON'T: Register translators manually in Startup.cs ❌ DON'T: Skip DI registration
Integration with Other Projects
With FractalDataWorks.Services.Data (DataGateway)
// DataGateway with Built-In Translator Pattern (simplified routing)
public class DataGatewayService : IDataGateway
{
private readonly IDataConnectionProvider _connectionProvider;
private readonly ILogger<DataGatewayService> _logger;
public async Task<IGenericResult<T>> Execute<T>(IDataCommand command)
{
// 1. Get connection by name
var connectionResult = await _connectionProvider.GetConnection(command.ConnectionName);
if (!connectionResult.IsSuccess) return GenericResult<T>.Failure(connectionResult.Message);
var connection = connectionResult.Value;
// 2. Connection owns translator - just route command to connection
// Connection handles: Translate → Execute → Return Result
return await connection.Execute<T>(command);
}
}
With FractalDataWorks.Data.Translators
// Translator implementations (referenced by connections)
public class MsSqlDataCommandTranslator : IDataCommandTranslator
{
private readonly ILogger<MsSqlDataCommandTranslator> _logger;
public MsSqlDataCommandTranslator(ILogger<MsSqlDataCommandTranslator> logger)
{
_logger = logger;
}
public string DomainName => "MsSql";
public async Task<IGenericResult<IConnectionCommand>> TranslateAsync(
IDataCommand command,
CancellationToken cancellationToken = default)
{
// SQL-specific translation logic
// Builds SELECT/INSERT/UPDATE/DELETE statements from IDataCommand
}
}
With FractalDataWorks.Services.Connections
// Connections own translators (Built-In Pattern)
public class MsSqlConnectionType : ConnectionTypeBase<...>
{
public override void Configure(IConfiguration configuration)
{
// Register translator type for metadata/discovery
DataCommandTranslators.Register("MsSql", typeof(MsSqlDataCommandTranslator));
}
public override void Register(IServiceCollection services)
{
// Register translator in DI
services.AddScoped<IDataCommandTranslator, MsSqlDataCommandTranslator>();
// Register connection - translator will be injected
services.AddScoped<IDataConnection, MsSqlConnection>();
}
}
Target Frameworks
- .NET Standard 2.0
- .NET 10.0
Dependencies
NuGet Packages:
- Microsoft.Extensions.DependencyInjection.Abstractions
- Microsoft.Extensions.Logging.Abstractions
Project References:
- FractalDataWorks.Commands.Abstractions - Base command interfaces
- FractalDataWorks.Collections - TypeCollection support
- FractalDataWorks.Results - Result pattern
- FractalDataWorks.Services.Connections.Abstractions - IConnectionCommand
Related Projects
- FractalDataWorks.Data.Translators - Concrete translator implementations (MsSql, Rest, GraphQL)
- FractalDataWorks.Services.Data - DataGateway service that orchestrates translation and execution
- FractalDataWorks.Services.Connections - Connection implementations that execute translated commands
- FractalDataWorks.Data.DataStores.Abstractions - DataStore abstractions that identify translator type
Contributing
When adding new translator types (Built-In Pattern):
- Create implementation in connection's project or separate translator project
- Implement
IDataCommandTranslatorinterface - Use
[TypeOption(typeof(DataCommandTranslators), "YourDomain")]for compile-time discovery (optional) - Register at runtime in connection's Configure():
DataCommandTranslators.Register("YourDomain", typeof(YourTranslator)) - Register in DI in connection's Register() method
- Inject translator into connection via constructor (deterministic) or factory (flexible)
- Add source-generated logging
- Write unit tests
FractalDataWorks.Commands.Data.Abstractions - Universal data command abstractions for write-once, run-anywhere data operations.
| 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 was computed. 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
- FluentValidation (>= 11.9.2)
- FractalDataWorks.Collections (>= 0.9.5-alpha.1008)
- FractalDataWorks.Commands.Abstractions (>= 0.9.5-alpha.1008)
- FractalDataWorks.Data.Abstractions (>= 0.9.5-alpha.1008)
- FractalDataWorks.Messages (>= 0.9.5-alpha.1008)
- FractalDataWorks.Results (>= 0.9.5-alpha.1008)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0-rc.2.25502.107)
NuGet packages (13)
Showing the top 5 NuGet packages that depend on FractalDataWorks.Commands.Data.Abstractions:
| Package | Downloads |
|---|---|
|
FractalDataWorks.Commands.Data
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
|
|
FractalDataWorks.Services.Connections.Abstractions
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
|
|
FractalDataWorks.Services.Connections
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
|
|
FractalDataWorks.Services.Data
Development tools and utilities for the FractalDataWorks ecosystem. Build: |
|
|
FractalDataWorks.Services.Connections.MsSql
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.5-alpha.1008 | 15 | 11/25/2025 |
| 0.9.5-alpha.1007 | 21 | 11/25/2025 |
| 0.9.5-alpha.1006 | 71 | 11/25/2025 |
| 0.9.1-alpha.1037 | 12 | 11/25/2025 |
| 0.9.1-alpha.1036 | 11 | 11/25/2025 |
| 0.9.1-alpha.1035 | 26 | 11/25/2025 |
| 0.9.1-alpha.1033 | 32 | 11/25/2025 |
| 0.9.0-alpha.1011.ged0a6c6e98 | 362 | 11/18/2025 |
| 0.9.0-alpha.1010.gecd88aac50 | 350 | 11/18/2025 |
| 0.9.0-alpha.1009.g7f6817e985 | 354 | 11/18/2025 |
| 0.9.0-alpha.1006.gf287016c0c | 366 | 11/18/2025 |
| 0.8.0-alpha.1011 | 348 | 11/18/2025 |
| 0.7.0-alpha.1022 | 144 | 11/3/2025 |
| 0.7.0-alpha.1021 | 149 | 11/3/2025 |
| 0.7.0-alpha.1008 | 112 | 11/2/2025 |
| 0.7.0-alpha.1006 | 143 | 10/30/2025 |
| 0.7.0-alpha.1005 | 139 | 10/30/2025 |
| 0.7.0-alpha.1004 | 149 | 10/30/2025 |
| 0.7.0-alpha.1001 | 137 | 10/29/2025 |
| 0.6.0-alpha.1006 | 135 | 10/29/2025 |
| 0.6.0-alpha.1005 | 140 | 10/28/2025 |
| 0.6.0-alpha.1004 | 131 | 10/28/2025 |