The Application created with Flexbase is a modern, enterprise-grade .NET 9 application built using Clean Architecture principles with Domain-Driven Design (DDD) patterns. It leverages the Sumeru.Flex framework to provide a robust, scalable foundation for business applications.
The solution demonstrates elements of Vertical Slice Architecture by organizing functionality around business capabilities rather than technical layers:
Identified Vertical Slices
Vertical Slice Characteristics
1. Feature-Based Organization
Each slice represents a complete business capability
Contains all layers needed for that capability
Minimal coupling between slices
2. Self-Contained Slices
3. Independent Deployment
Each endpoint can be deployed independently
Separate scaling for different capabilities
Technology-agnostic slice boundaries
4. Shared Infrastructure
Common framework bridges
Shared domain models
Centralized configuration
Benefits of Vertical Slice Architecture
Business Alignment: Code organization matches business capabilities
Team Autonomy: Teams can work on slices independently
Reduced Coupling: Changes in one slice don't affect others
Easier Testing: Each slice can be tested in isolation
Independent Scaling: Scale slices based on business needs
🛠️ Technology Stack
Core Technologies
Component
Technology
Version
Purpose
Runtime
.NET
9.0
Application runtime
Framework
Sumeru.Flex
9.0.0
Core framework
ORM
Entity Framework Core
Latest
Data access
Messaging
NServiceBus
Latest
Message bus
Mapping
AutoMapper
13.0.1
Object mapping
Logging
Serilog
Latest
Structured logging
Validation
FluentValidation
Latest
Input validation
Database Support
SQL Server - Primary database
MySQL - Alternative database
PostgreSQL - Alternative database
Multi-tenant - Separate database per tenant
Message Bus Options
RabbitMQ - Open-source message broker
Azure Service Bus - Cloud messaging service
SQL Server - Database-based messaging
Learning Transport - Development/testing
✅ Best Practices Implemented
1. Configuration Management
Multi-Source Configuration Strategy
Configuration Priority (Highest to Lowest):
Command Line Arguments
Azure Key Vault
User Secrets (Development only)
Environment Variables
appsettings.{Environment}.json
appsettings.json
2. Dependency Injection
Service Registration Pattern
3. Error Handling
Custom Exception Types
4. Logging Strategy
Structured Logging with Serilog
Correlation IDs for request tracing
Environment-specific log levels
Centralized logging configuration
5. Database Patterns
Repository Pattern with Unit of Work
6. Object Mapping
AutoMapper Configuration
⚙️ Configuration Management
Environment-Specific Configuration
The solution supports multiple environments with specific configuration files:
appsettings.json - Base configuration
appsettings.Development.json - Development overrides
appsettings.Staging.json - Staging overrides
appsettings.Production.json - Production overrides
The current solution has a foundation for vertical slice architecture but can be enhanced to be more feature-centric:
Current State
Technical Slices: Organized by technical concerns (Handlers, Queries, Controllers)
Shared Infrastructure: Common framework and domain models
Independent Endpoints: Each capability can be deployed separately
Recommended Enhancements
1. Feature-Based Slices
2. Slice-Specific Infrastructure
3. Cross-Slice Communication
Benefits of Enhanced Vertical Slices
Business Focus: Code organization matches business domains
Team Ownership: Clear ownership boundaries
Independent Evolution: Features can evolve independently
Easier Onboarding: New developers can focus on specific business areas
Reduced Complexity: Smaller, focused codebases
This architecture documentation provides a comprehensive overview of the Flexbase solution's design patterns, best practices, and implementation strategies. The solution demonstrates enterprise-grade .NET development practices with a focus on maintainability, scalability, and developer productivity.
public class AutoConfigurationProvider : IHostConfigurationProvider
{
public void Configure(IConfigurationBuilder configBuilder, HostBuilderContext context, string? userSecretName = null)
{
configBuilder
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
if (!string.IsNullOrEmpty(userSecretName) && context.HostingEnvironment.IsDevelopment())
{
configBuilder.AddUserSecrets(userSecretName);
}
// Azure Key Vault integration
var keyVaultName = tempConfig["Azure:KeyVaultName"];
if (!string.IsNullOrEmpty(keyVaultName))
{
var keyVaultUri = new Uri($"https://{keyVaultName}.vault.azure.net/");
configBuilder.AddAzureKeyVault(keyVaultUri, new DefaultAzureCredential());
}
}
}
public static class DbEndPointConfig
{
public static void AddFlexBaseDbServices(this IServiceCollection services)
{
// Single Tenant Configuration
EnableSingleTenant(services);
// Multi Tenant Configuration (commented)
//EnableMultitenant(services);
}
private static void EnableSingleTenant(IServiceCollection services)
{
ConfigureSingleTenantDb(services);
ConfigureFlexDb.ForRepositoryForSqlServer(services);
}
}
public class BadRequestException : Exception
{
public BadRequestException(string message) : base(message) { }
}
public class InternalServerErrorException : Exception
{
public InternalServerErrorException(string message) : base(message) { }
}
// Single Tenant
services.AddTransient<IWriteDbConnectionProviderBridge, AppSettingsWriteDbConnectionProvider>();
services.AddTransient<IReadDbConnectionProviderBridge, AppSettingsReadDbConnectionProvider>();
// Multi Tenant Support
ConfigureFlexDb.ForMultitenantMasterDbForSqlServer(services);
ConfigureFlexDb.ForRepositoryForSqlServer(services);
public class CoreMapperConfiguration : FlexMapperProfile
{
public CoreMapperConfiguration() : base()
{
#region Input
//CreateMap<YourAPIModel, YourDomainModel>();
#endregion
#region Output
//CreateMap<YourDomainModel, YourOutputAPIModel>();
#endregion
}
}
public class ApplicationEFDbContextFactory : IDesignTimeDbContextFactory<ApplicationEFDbMigrationContext>
{
public ApplicationEFDbMigrationContext CreateDbContext(string[] args)
{
// Configuration and service setup
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables().Build();
// Service collection setup
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(configuration);
// FlexBase configuration
services.AddFlexBase(new List<AssembliesToLoad> {
new AssembliesToLoad("YourApplication*.dll")
});
return serviceProvider.GetRequiredService<ApplicationEFDbMigrationContext>();
}
}
public class RabbitMqNsbConfiguration : NsbBaseConfiguration
{
public RabbitMqNsbConfiguration(string endpointName, IEnumerable<BusRouteConfig> routes,
IConfiguration configuration, IHostEnvironment env, string errorQueueName = "error")
: base(endpointName, configuration, env, errorQueueName)
{
// Persistence configuration
var persistence = this.UsePersistence<SqlPersistence>();
persistence.SqlDialect<SqlDialect.MsSqlServer>();
persistence.ConnectionBuilder(() => new SqlConnection(sqlPersistenceConnectionString));
// Transport configuration
var transport = this.UseTransport<RabbitMQTransport>();
transport.ConnectionString(rabbitMqConnectionString);
transport.UseConventionalRoutingTopology(QueueType.Classic);
// Routing configuration
var routing = transport.Routing();
foreach (var route in routes)
{
routing.RouteToEndpoint(route.Assembly, route.Destination);
}
}
}
public class AzureServiceNsbConfiguration : NsbBaseConfiguration
{
public AzureServiceNsbConfiguration(string endpointName, IEnumerable<BusRouteConfig> routes,
IConfiguration configuration, IHostEnvironment env, string errorQueueName = "error")
: base(endpointName, configuration, env, errorQueueName)
{
// Azure Service Bus transport
var transport = new AzureServiceBusTransport(azureServiceBusConnectionString);
this.UseTransport(transport);
// Routing configuration
var routing = this.UseTransport(transport);
foreach (var route in routes)
{
routing.RouteToEndpoint(route.Assembly, route.Destination);
}
}
}
public class TenantMigrationContext : ApplicationTenantEFDbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
var connectionString = _configuration.GetSection("FlexBase")["AppDbTenantConnection"];
optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
}
}
// Example future caching implementation
public class CachedQueryHandler<TQuery, TResult> : IQueryHandler<TQuery, TResult>
{
private readonly IQueryHandler<TQuery, TResult> _decorated;
private readonly IMemoryCache _cache;
public async Task<TResult> HandleAsync(TQuery query)
{
var cacheKey = GenerateCacheKey(query);
if (_cache.TryGetValue(cacheKey, out TResult cachedResult))
{
return cachedResult;
}
var result = await _decorated.HandleAsync(query);
_cache.Set(cacheKey, result, TimeSpan.FromMinutes(5));
return result;
}
}
// Each slice can have its own infrastructure
public class UserManagementSlice
{
public void ConfigureServices(IServiceCollection services)
{
// Slice-specific services
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IUserNotificationService, UserNotificationService>();
}
}
// Events for cross-slice communication
public class UserCreatedEvent : IFlexEvent
{
public Guid UserId { get; set; }
public string Email { get; set; }
public DateTime CreatedAt { get; set; }
}
// Other slices can subscribe to events
public class OrderProcessingSlice
{
public void Handle(UserCreatedEvent @event)
{
// Create welcome order for new user
}
}