Separate Command And Query
Overview
FlexBase implements Command Query Responsibility Segregation (CQRS) as a core architectural pattern, providing clear separation between data modification operations (Commands) and data retrieval operations (Queries). This separation enables independent scaling, optimization, and maintenance of read and write operations.
Architecture Pattern
Command-Query Separation
βββββββββββββββββββ βββββββββββββββββββ
β COMMANDS β β QUERIES β
β (Write Ops) β β (Read Ops) β
βββββββββββββββββββ€ βββββββββββββββββββ€
β β’ Insert β β β’ GetById β
β β’ Update β β β’ GetSingle β
β β’ Delete β β β’ GetList β
β β’ SoftDelete β β β’ GetPagedList β
βββββββββββββββββββ βββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ
β Write Repositoryβ β Read Repository β
β (Write DB) β β (Read DB) β
βββββββββββββββββββ βββββββββββββββββββ
Command Operations
Purpose
Commands handle data modification operations that change the state of the system.
Characteristics
Write Operations: Create, Update, Delete, SoftDelete
State Changes: Modify data in the system
Event Publishing: Trigger events after successful operations
Transaction Support: Full ACID compliance
Audit Trail: Track who made changes and when
Command Flow
Controller β Service β Command Handler β Domain Model β Write Repository β Write Database β Event Publishing
Command Examples
Insert:
AddOrder
,CreateProduct
,RegisterUser
Update:
UpdateOrder
,EditProduct
,ModifyUser
Delete:
DeleteOrder
,RemoveProduct
,DeactivateUser
SoftDelete:
SoftDeleteOrder
,ArchiveProduct
,DeactivateUser
Command Implementation
// Command Handler
public class AddOrderHandler : IAddOrderHandler
{
public virtual async Task Execute(AddOrderCommand cmd, IFlexServiceBusContext serviceBusContext)
{
_repoFactory.Init(cmd.Dto);
_model = _flexHost.GetDomainModel<Order>().AddOrder(cmd);
_repoFactory.GetRepo().InsertOrUpdate(_model);
int records = await _repoFactory.GetRepo().SaveAsync();
await this.Fire(EventCondition, serviceBusContext);
}
}
Query Operations
Purpose
Queries handle data retrieval operations that read data without modifying the system state.
Characteristics
Read Operations: GetById, GetSingle, GetList, GetPagedList
No State Changes: Read-only operations
No Events: No side effects or event publishing
Optimized for Performance: Tuned for fast data retrieval
Caching Friendly: Can be easily cached
Query Flow
Controller β Service β Query Handler β Read Repository β Read Database β Response
Query Examples
GetById:
GetOrderById
,GetProductById
,GetUserById
GetSingle:
GetOrderByNumber
,GetProductBySku
,GetUserByEmail
GetList:
GetOrders
,GetProducts
,GetUsers
GetPagedList:
GetOrdersPaged
,GetProductsPaged
,GetUsersPaged
Query Implementation
// Query Handler
public class GetOrderById : FlexiQueryBridge<Order, GetOrderByIdDto>
{
public override GetOrderByIdDto Fetch()
{
var result = Build<Order>().SelectTo<GetOrderByIdDto>().FirstOrDefault();
return result;
}
protected override IQueryable<T> Build<T>()
{
_repoFactory.Init(_params);
IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>().Where(t => t.Id == _params.Id);
return query;
}
}
Repository Separation
Write Repository (Commands)
Purpose: Handles all data modification operations
Database: Connected to the primary/write database
Operations: Insert, Update, Delete, SoftDelete
Transaction Support: Full ACID compliance
Audit Trail: Tracks all changes
Read Repository (Queries)
Purpose: Handles all data retrieval operations
Database: Can be connected to read-only replica or separate read database
Operations: Select, Filter, Search, Pagination
Performance Optimized: Tuned for fast queries
Caching Support: Can be easily cached
Repository Configuration
// Write Repository Registration
services.AddTransient<IWriteDbConnectionProviderBridge, AppSettingsWriteDbConnectionProvider>();
// Read Repository Registration
services.AddTransient<IReadDbConnectionProviderBridge, AppSettingsReadDbConnectionProvider>();
Production Scalability Benefits
1. Independent Database Scaling
Write Database Scaling
Vertical Scaling: Increase CPU, memory, and storage for write operations
Optimized for Writes: Tuned for insert, update, delete operations
Transaction Performance: Optimized for ACID compliance
Backup Strategy: Full backup and point-in-time recovery
Read Database Scaling
Horizontal Scaling: Multiple read replicas for high availability
Read-Only Optimizations: Indexes optimized for query patterns
Caching Layer: Redis or in-memory caching for frequently accessed data
Geographic Distribution: Read replicas in different regions
2. Performance Optimization
Write Database Optimizations
-- Write-optimized indexes
CREATE INDEX IX_Orders_CreatedDate ON Orders (CreatedDate);
CREATE INDEX IX_Orders_LastModified ON Orders (LastModifiedDate);
-- Partitioning for large tables
CREATE PARTITION FUNCTION PF_OrdersByDate (datetime)
AS RANGE RIGHT FOR VALUES ('2024-01-01', '2024-02-01', '2024-03-01');
Read Database Optimizations
-- Read-optimized indexes
CREATE INDEX IX_Orders_CustomerId_OrderDate ON Orders (CustomerId, OrderDate);
CREATE INDEX IX_Orders_Status_CreatedDate ON Orders (Status, CreatedDate);
-- Covering indexes for common queries
CREATE INDEX IX_Orders_Covering ON Orders (CustomerId)
INCLUDE (OrderDate, TotalAmount, Status);
3. High Availability Architecture
Write Database
Primary Database: Single source of truth for all writes
Synchronous Replication: Real-time data consistency
Failover Support: Automatic failover to secondary
Data Integrity: ACID compliance and consistency
Read Database
Multiple Replicas: Several read-only replicas
Asynchronous Replication: Near real-time data availability
Load Balancing: Distribute read load across replicas
Geographic Distribution: Replicas in different regions
4. Cost Optimization
Write Database Costs
High-Performance Hardware: SSD storage, high CPU/memory
Backup Storage: Full and incremental backups
Monitoring: Comprehensive monitoring and alerting
Security: Advanced security and compliance features
Read Database Costs
Standard Hardware: Cost-effective hardware for read operations
Reduced Backup Requirements: Less frequent backups
Basic Monitoring: Essential monitoring for read operations
Standard Security: Standard security measures
Configuration Examples
Single Database Configuration
// Both commands and queries use the same database
services.AddTransient<IWriteDbConnectionProviderBridge, AppSettingsWriteDbConnectionProvider>();
services.AddTransient<IReadDbConnectionProviderBridge, AppSettingsReadDbConnectionProvider>();
Separate Database Configuration
// Commands use write database
services.AddTransient<IWriteDbConnectionProviderBridge, AppSettingsWriteDbConnectionProvider>();
// Queries use read database
services.AddTransient<IReadDbConnectionProviderBridge, AppSettingsReadDbConnectionProvider>();
Connection String Configuration
{
"FlexBase": {
"AppDbConnection": "Data Source=WriteServer;Initial Catalog=YourDatabase;...",
"AppReadDbConnection": "Data Source=ReadServer;Initial Catalog=YourReadDatabase;..."
}
}
Real-World Scaling Scenarios
Scenario 1: High-Volume E-Commerce
Write Database: Handles order creation, updates, and inventory changes
Read Database: Serves product catalogs, search results, and analytics
Scaling: Read replicas in multiple regions for global customers
Scenario 2: Financial Services
Write Database: Processes transactions, account updates, and payments
Read Database: Serves account balances, transaction history, and reports
Scaling: Read replicas for compliance reporting and analytics
Scenario 3: Social Media Platform
Write Database: Handles posts, comments, and user interactions
Read Database: Serves feeds, timelines, and search results
Scaling: Multiple read replicas for content delivery
Monitoring and Metrics
Write Database Metrics
Transaction Rate: Commands per second
Response Time: Average command execution time
Error Rate: Failed command percentage
Resource Utilization: CPU, memory, disk usage
Read Database Metrics
Query Rate: Queries per second
Response Time: Average query execution time
Cache Hit Rate: Percentage of cached queries
Replica Lag: Data freshness across replicas
Best Practices
1. Database Design
Write Database: Normalized for consistency and integrity
Read Database: Denormalized for query performance
Indexing Strategy: Different indexes for write vs read patterns
2. Caching Strategy
Write Operations: No caching (always fresh data)
Read Operations: Aggressive caching for frequently accessed data
Cache Invalidation: Smart invalidation on data changes
3. Monitoring
Separate Monitoring: Different monitoring for write and read operations
Performance Metrics: Track performance independently
Alerting: Different alerting thresholds for each database
4. Backup and Recovery
Write Database: Full backup and point-in-time recovery
Read Database: Less frequent backups, can be rebuilt from write database
Key Benefits
Independent Scaling: Scale read and write operations separately
Performance Optimization: Optimize each database for its specific workload
High Availability: Multiple read replicas for better availability
Cost Optimization: Use appropriate hardware for each workload
Maintenance: Perform maintenance on read replicas without affecting writes
Geographic Distribution: Place read replicas closer to users
Disaster Recovery: Better disaster recovery options
Load Distribution: Distribute read load across multiple replicas
This Command-Query separation in FlexBase provides a robust foundation for building scalable, high-performance applications that can handle both high-volume writes and complex read operations efficiently! π
Last updated