DTDE Development Plan - Sharding Strategies¶
← Back to Revised Architecture | Next: Core Domain Model →
1. Sharding Strategy Overview¶
DTDE provides multiple sharding strategies that can be applied to any entity property. All strategies are property-agnostic - you choose which property to shard by.
2. Property-Based Sharding¶
2.1 Single Property Sharding¶
Shard by any property value:
// Shard by Region (string)
entity.ShardBy(c => c.Region);
// Results in: Customers_EU, Customers_US, Customers_APAC
// Shard by Status (enum)
entity.ShardBy(o => o.Status);
// Results in: Orders_Pending, Orders_Completed, Orders_Cancelled
// Shard by Year (int)
entity.ShardBy(o => o.Year);
// Results in: Orders_2023, Orders_2024, Orders_2025
2.2 Composite Property Sharding¶
Shard by multiple properties:
// Shard by Year + Region
entity.ShardBy(o => new { o.Year, o.Region });
// Results in: Orders_2024_EU, Orders_2024_US, Orders_2025_EU
// Shard by Category + SubCategory
entity.ShardBy(p => new { p.Category, p.SubCategory });
// Results in: Products_Electronics_Phones, Products_Electronics_Laptops
3. Hash-Based Sharding¶
3.1 Even Distribution¶
Distribute records evenly across a fixed number of shards:
// Distribute by ID hash
entity.ShardByHash(c => c.Id, shardCount: 8);
// Results in: Customers_0, Customers_1, ... Customers_7
// Distribute by composite key
entity.ShardByHash(o => new { o.CustomerId, o.OrderId }, shardCount: 16);
3.2 Custom Hash Function¶
// Custom hash function
entity.ShardByHash(c => c.Id, shardCount: 4,
hashFunction: id => Math.Abs(id.GetHashCode()) % 4);
// Consistent hashing (for dynamic shard count)
entity.ShardByConsistentHash(c => c.Id, virtualNodes: 150);
4. Range-Based Sharding¶
4.1 Numeric Ranges¶
// Shard by ID ranges
entity.ShardByRange(c => c.Id, new[]
{
new ShardRange("low", 0, 999_999),
new ShardRange("mid", 1_000_000, 9_999_999),
new ShardRange("high", 10_000_000, int.MaxValue)
});
// Results in: Customers_low, Customers_mid, Customers_high
// Shard by amount ranges
entity.ShardByRange(o => o.Amount, new[]
{
new ShardRange("small", 0m, 999.99m),
new ShardRange("medium", 1000m, 9999.99m),
new ShardRange("large", 10000m, decimal.MaxValue)
});
4.2 Date Ranges¶
// Shard by explicit date ranges
entity.ShardByRange(o => o.OrderDate, new[]
{
new ShardRange("archive", DateTime.MinValue, new DateTime(2022, 12, 31)),
new ShardRange("2023", new DateTime(2023, 1, 1), new DateTime(2023, 12, 31)),
new ShardRange("2024", new DateTime(2024, 1, 1), new DateTime(2024, 12, 31)),
new ShardRange("current", new DateTime(2025, 1, 1), DateTime.MaxValue)
});
5. Date-Based Sharding¶
5.1 Automatic Date Intervals¶
// Yearly sharding
entity.ShardByDate(o => o.OrderDate, DateShardInterval.Year);
// Results in: Orders_2023, Orders_2024, Orders_2025
// Monthly sharding
entity.ShardByDate(o => o.OrderDate, DateShardInterval.Month);
// Results in: Orders_2024_01, Orders_2024_02, ... Orders_2024_12
// Quarterly sharding
entity.ShardByDate(o => o.OrderDate, DateShardInterval.Quarter);
// Results in: Orders_2024_Q1, Orders_2024_Q2, Orders_2024_Q3, Orders_2024_Q4
// Weekly sharding
entity.ShardByDate(o => o.OrderDate, DateShardInterval.Week);
// Results in: Orders_2024_W01, Orders_2024_W02, ... Orders_2024_W52
5.2 Custom Date Formatting¶
// Custom date pattern
entity.ShardByDate(o => o.OrderDate, DateShardInterval.Month,
formatPattern: "yyyyMM");
// Results in: Orders_202401, Orders_202402
// Custom shard name generator
entity.ShardByDate(o => o.OrderDate,
shardNameGenerator: date => $"Orders_{date:yyyy}_{(date.Month <= 6 ? "H1" : "H2")}");
// Results in: Orders_2024_H1, Orders_2024_H2
6. Alphabetic Sharding¶
6.1 First Letter Sharding¶
// Shard by first letter
entity.ShardByAlphabet(c => c.LastName);
// Results in: Customers_A, Customers_B, ... Customers_Z
// Shard by letter groups
entity.ShardByAlphabet(c => c.LastName, new[] { "A-F", "G-L", "M-R", "S-Z" });
// Results in: Customers_A-F, Customers_G-L, Customers_M-R, Customers_S-Z
6.2 Custom Character Ranges¶
// Custom ranges with special handling
entity.ShardByAlphabet(c => c.Name, new AlphabetShardConfig
{
Ranges = new[] { "0-9", "A-M", "N-Z" },
DefaultShard = "Other", // For special characters
CaseSensitive = false
});
7. Row Count Sharding¶
7.1 Automatic Shard Rotation¶
// Create new shard when current reaches limit
entity.ShardByRowCount(maxRowsPerShard: 1_000_000);
// Automatically creates: Orders_1, Orders_2, Orders_3...
// With custom naming
entity.ShardByRowCount(maxRowsPerShard: 500_000,
shardNameGenerator: index => $"Orders_Partition_{index:D3}");
// Results in: Orders_Partition_001, Orders_Partition_002
7.2 Row Count with Date Fallback¶
// Combine row count with date for deterministic routing
entity.ShardByRowCount(maxRowsPerShard: 1_000_000,
fallbackStrategy: ShardByDate(o => o.CreatedAt, DateShardInterval.Month));
8. Expression-Based Sharding¶
8.1 Custom LINQ Expressions¶
// Custom shard key calculation
entity.ShardBy(o => o.Amount > 10000 ? "HighValue" : "Standard");
// Results in: Orders_HighValue, Orders_Standard
// Complex logic
entity.ShardBy(c =>
c.Country == "US" ? (c.State.StartsWith("C") ? "US_West" : "US_East")
: c.Country == "UK" ? "EU_UK"
: "EU_Other");
8.2 Custom Shard Resolver¶
// Full control with custom resolver
entity.ShardBy(new CustomShardResolver<Order>(order =>
{
if (order.Priority == Priority.Urgent)
return "urgent_orders";
if (order.Amount > 100000)
return "large_orders";
return $"orders_{order.CreatedAt.Year}";
}));
9. Manual Sharding (sqlproj Support)¶
9.1 Explicit Table Mapping¶
For pre-created tables (sqlproj, DacPac, manual SQL):
entity.UseManualSharding(config =>
{
// Explicit table definitions
config.AddTable("dbo.Orders_2023", o => o.OrderDate.Year == 2023);
config.AddTable("dbo.Orders_2024", o => o.OrderDate.Year == 2024);
config.AddTable("dbo.Orders_Current", o => o.OrderDate.Year >= 2025)
.AsWritable(); // Only writable shard
config.AddTable("archive.Orders_Historical", o => o.OrderDate.Year < 2023)
.AsReadOnly();
});
9.2 Table Discovery¶
// Auto-discover tables at runtime
entity.UseManualSharding(config =>
{
config.DiscoverTables("Orders_%"); // SQL LIKE pattern
config.ShardKeyExtractor = tableName =>
{
// Extract shard key from table name
var year = int.Parse(tableName.Split('_').Last());
return new ShardKeyInfo { Year = year };
};
});
9.3 Configuration File¶
// shards.json
{
"entities": {
"Order": {
"storageMode": "Tables",
"migrationsEnabled": false,
"shards": [
{
"name": "orders_2023",
"table": "dbo.Orders_2023",
"predicate": "OrderDate.Year == 2023",
"isReadOnly": true
},
{
"name": "orders_2024",
"table": "dbo.Orders_2024",
"predicate": "OrderDate.Year == 2024",
"isWritable": true
}
]
}
}
}
10. Combining Strategies¶
10.1 Hierarchical Sharding¶
// First by region (database), then by year (table)
entity.ShardBy(o => o.Region, ShardStorageMode.Databases)
.ThenShardBy(o => o.Year, ShardStorageMode.Tables);
// Results in: DB_EU.Orders_2024, DB_US.Orders_2024, etc.
10.2 Strategy with Overflow¶
// Primary strategy with overflow handling
entity.ShardByDate(o => o.OrderDate, DateShardInterval.Year)
.WithOverflowStrategy(ShardByRowCount(1_000_000));
// If 2024 exceeds 1M rows, creates Orders_2024_1, Orders_2024_2
11. Strategy Interface¶
public interface IShardingStrategy<TEntity> where TEntity : class
{
/// <summary>
/// Determines the shard key for an entity.
/// </summary>
string GetShardKey(TEntity entity);
/// <summary>
/// Determines which shards to query based on predicates.
/// </summary>
IEnumerable<string> ResolveShards(
IReadOnlyDictionary<string, object?> predicates,
ShardRegistry registry);
/// <summary>
/// Determines which shard to write to for new entities.
/// </summary>
string ResolveWriteShard(TEntity entity, ShardRegistry registry);
}
12. Configuration Summary¶
| Strategy | Best For | Example |
|---|---|---|
| Property | Natural partitions (region, status) | ShardBy(c => c.Region) |
| Hash | Even distribution, no hotspots | ShardByHash(c => c.Id, 8) |
| Range | Numeric/date ranges | ShardByRange(c => c.Id, ranges) |
| Date | Time-series data | ShardByDate(o => o.Date, Year) |
| Alphabetic | Name-based lookups | ShardByAlphabet(c => c.Name) |
| Row Count | Auto-scaling | ShardByRowCount(1_000_000) |
| Expression | Complex logic | ShardBy(o => CalcShard(o)) |
| Manual | Pre-created tables | UseManualSharding(...) |