Common questions and answers about the DKNet Framework.
- General Questions
- Architecture & Design
- Entity Framework Core
- CQRS & Messaging
- Performance
- Testing
- Deployment
- Troubleshooting
DKNet is a comprehensive .NET framework designed to enhance enterprise application development using Domain-Driven Design (DDD) principles and Onion Architecture patterns. It provides a collection of libraries that simplify building scalable, maintainable applications.
DKNet Framework requires .NET 10.0 or later. All packages are built and tested against .NET 10.0.
Yes! DKNet Framework is released under the MIT License, making it free for both commercial and non-commercial use.
DKNet Framework follows semantic versioning and maintains high code coverage (99% for core libraries). It includes comprehensive testing, CI/CD pipelines, and code quality checks.
- Documentation: Complete Documentation
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Examples: SlimBus Template
DDD helps manage complexity in large applications by:
- Focusing on business domain: Core business logic is central
- Ubiquitous language: Shared terminology between developers and domain experts
- Bounded contexts: Clear boundaries between different parts of the system
- Rich domain models: Business rules are expressed in code
Onion Architecture is a architectural pattern that:
- Inverts dependencies: Inner layers don't depend on outer layers
- Separates concerns: Each layer has a specific responsibility
- Enables testing: Business logic can be tested in isolation
- Supports flexibility: Infrastructure can be easily replaced
Consider CQRS (Command Query Responsibility Segregation) when:
- Complex business logic: Commands and queries have different requirements
- Scalability needs: Read and write operations need different scaling strategies
- Event sourcing: You want to capture all changes as events
- Audit requirements: Full traceability of operations is needed
While DKNet is designed with DDD in mind, you can use individual components:
- Core Extensions:
DKNet.Fw.Extensionsworks standalone - EF Core Extensions: Repository patterns can be used without full DDD
- Blob Storage: Service abstractions work independently
No, you can pick and choose based on your needs:
- Minimum:
DKNet.EfCore.Abstractions+DKNet.EfCore.Extensions - Repository Pattern: Add
DKNet.EfCore.Repos - Domain Events: Add
DKNet.EfCore.Events - Data Authorization: Add
DKNet.EfCore.DataAuthorization
# Create migration
dotnet ef migrations add YourMigrationName
# Update database
dotnet ef database update
# For production
dotnet ef script --idempotent > migration.sqlYes! Each context can be configured independently:
services.AddDbContext<CatalogContext>(options =>
options.UseSqlServer(connectionString));
services.AddDbContext<IdentityContext>(options =>
options.UseSqlServer(identityConnectionString));
services.AddDKNetRepositories<CatalogContext>();
services.AddDKNetRepositories<IdentityContext>();DKNet provides built-in multi-tenancy support:
- Implement ITenantEntity on your entities
- Configure tenant provider in DI
- Repositories automatically filter by tenant
See Multi-tenant Example for details.
DKNet includes several performance optimizations:
- Specification pattern for complex queries
- Lazy loading support where appropriate
- Efficient change tracking in repositories
- Bulk operations for large datasets
The SlimBus template uses a lightweight message bus, but you can use MediatR:
services.AddMediatR(typeof(CreateProductHandler));
services.AddDKNetMediatRIntegration();Use FluentValidation with the command pipeline:
public class CreateProductValidator : AbstractValidator<CreateProductCommand>
{
public CreateProductValidator()
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
RuleFor(x => x.Price).GreaterThan(0);
}
}Domain events are dispatched when SaveChangesAsync() is called on the DbContext. This ensures events are only sent after successful database commits.
Yes, all event handlers support async operations:
public class ProductCreatedHandler : IDomainEventHandler<ProductCreatedEvent>
{
public async Task Handle(ProductCreatedEvent domainEvent, CancellationToken cancellationToken)
{
await _emailService.SendNotificationAsync(...);
}
}DKNet is designed for performance:
- Minimal allocations in hot paths
- Efficient repository patterns with proper caching
- Optimized queries using specifications
- Async patterns throughout
Benchmark results show minimal overhead compared to raw EF Core.
Domain events are designed to be lightweight:
- In-memory dispatch by default
- Batched processing during SaveChanges
- Async handlers don't block the main thread
- Optional external messaging for scalability
- Use specifications for complex queries
- Project to DTOs for read operations
- Enable query splitting for large includes
- Use raw SQL for performance-critical scenarios
// Projection example
var results = await repository.Gets()
.Select(p => new ProductDto { Id = p.Id, Name = p.Name })
.ToListAsync();DKNet promotes testability:
- TestContainers for integration tests
- In-memory providers for unit tests
- Mocking repositories where needed
- Domain event testing utilities
TestContainers for integration tests (recommended):
- Real database behavior
- SQL Server features work correctly
- Catches database-specific issues
In-memory for unit tests:
- Faster execution
- No external dependencies
- Focus on business logic
[Test]
public async Task CreateProduct_ShouldRaiseEvent()
{
// Arrange
var product = new Product("Test", 10.0m, "user");
// Act
product.UpdatePrice(15.0m, "user");
// Assert
var events = product.GetUncommittedEvents();
events.Should().ContainSingle<ProductPriceChangedEvent>();
}[Test]
public async Task Repository_ShouldFilterByTenant()
{
// Arrange
var tenantProvider = new Mock<ITenantProvider>();
tenantProvider.Setup(x => x.GetCurrentTenant()).Returns("tenant1");
var repository = new TenantProductRepository(context, tenantProvider.Object);
// Act & Assert
var products = await repository.GetAllAsync();
products.Should().OnlyContain(p => p.TenantId == "tenant1");
}DKNet applications can be deployed like any .NET application:
- Docker containers (recommended)
- Azure App Service
- AWS ECS/EKS
- On-premises IIS
The SlimBus template includes Docker support and deployment configurations.
Recommended approach:
- Generate idempotent scripts:
dotnet ef script --idempotent - Run scripts during deployment pipeline
- Never run migrations from application startup in production
Alternative: Use migration bundles for zero-downtime deployments.
Use standard .NET configuration providers:
- Azure Key Vault for Azure deployments
- AWS Secrets Manager for AWS deployments
- Environment variables for containerized deployments
- User secrets for development
DKNet is designed for scale:
- Stateless services enable horizontal scaling
- Event-driven architecture supports distributed systems
- Repository pattern works with caching layers
- CQRS enables read/write scaling separation
This usually indicates missing service registration:
// Ensure you've registered all required services
services.AddDKNetRepositories<AppDbContext>();
services.AddDKNetSlimBusIntegration();
services.AddDKNetBlobStorage(builder => /* configuration */);Check these common issues:
- SaveChangesAsync called: Events dispatch during save
- Event handlers registered: Use
AddDKNetEventHandlers() - Async handlers: Ensure proper async/await usage
Common issues:
- DbContext registration: Ensure context is registered in DI
- Repository registration: Call
AddDKNetRepositories<TContext>() - Specifications: Check expression syntax for specifications
Performance optimization steps:
- Enable query logging: See generated SQL queries
- Use specifications: Instead of complex LINQ expressions
- Project to DTOs: Avoid loading full entities for display
- Check indexes: Ensure proper database indexing
Migration troubleshooting:
- Check connection string: Ensure database connectivity
- Permissions: Verify database permissions
- Concurrent migrations: Avoid running multiple migrations simultaneously
- Backup first: Always backup before major migrations
- Follow Onion Architecture: Keep dependencies pointing inward
- Use feature folders: Organize by business capability
- Separate concerns: Commands, queries, events in separate files
- Consistent naming: Follow established conventions
- Rich domain models: Put business logic in entities
- Value objects: Use for concepts without identity
- Aggregate boundaries: Keep aggregates small and focused
- Domain events: Use for side effects and integration
- Project early: Don't load full entities for display
- Lazy loading carefully: Be aware of N+1 query problems
- Async all the way: Use async/await consistently
- Monitor queries: Use logging to identify problematic queries
- Test business logic: Focus on domain entities and services
- Use real databases: TestContainers for integration tests
- Mock external dependencies: Keep tests focused and fast
- Test edge cases: Null values, empty collections, boundaries
- Getting Started Guide: Step-by-step setup
- Configuration Guide: Detailed setup options
- Examples & Recipes: Practical implementations
- Migration Guide: Upgrade instructions
- API Reference: Detailed API documentation
π‘ Still have questions? Don't hesitate to open an issue or start a discussion. The community is here to help!