Architecture Overview

Architecture Overview

Application created with Flexbase - Architecture & Best Practices

๐Ÿ—๏ธ Solution Overview

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.

๐Ÿ“‹ Table of Contents


๐Ÿ›๏ธ Architecture Overview

High-Level Architecture

graph TB
    subgraph "Presentation Layer"
        WebAPI[Web API Endpoints]
        Controllers[Controllers]
        CronJobs[Background Jobs]
    end
    
    subgraph "Application Layer"
        Handlers[Command Handlers]
        Queries[Query Handlers]
        Services[Application Services]
        DTOs[Data Transfer Objects]
    end
    
    subgraph "Domain Layer"
        Models[Domain Models]
        Events[Domain Events]
        Rules[Business Rules]
    end
    
    subgraph "Infrastructure Layer"
        Database[(Database)]
        MessageBus[Message Bus]
        ExternalAPIs[External APIs]
    end
    
    subgraph "Framework Layer"
        FlexCore[Sumeru.Flex Core]
        Bridges[Framework Bridges]
    end
    
    WebAPI --> Controllers
    Controllers --> Handlers
    Controllers --> Queries
    Handlers --> Models
    Queries --> Models
    Handlers --> MessageBus
    MessageBus --> Database
    FlexCore --> Bridges
    Bridges --> Handlers
    Bridges --> Queries

Key Architectural Principles

  • Separation of Concerns: Clear boundaries between layers

  • Dependency Inversion: High-level modules don't depend on low-level modules

  • Single Responsibility: Each component has one reason to change

  • Open/Closed Principle: Open for extension, closed for modification

  • Interface Segregation: Clients depend only on interfaces they use


๐Ÿ“ Solution Structure

Project Organization

YourApplication_blank_sol/
โ”œโ”€โ”€ YourApplication.Application/           # Application Layer
โ”‚   โ”œโ”€โ”€ ControlContracts/           # API Contracts & DTOs
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.DTOs/        # Data Transfer Objects
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.Messages/    # Message Contracts
โ”‚   โ”‚   โ””โ”€โ”€ YourApplication.SharedEvents/ # Shared Domain Events
โ”‚   โ”œโ”€โ”€ ControlHub/                 # API Controllers & Jobs
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.WebControllers/
โ”‚   โ”‚   โ””โ”€โ”€ YourApplication.CronJobs/
โ”‚   โ”œโ”€โ”€ Domain/                     # Domain Layer
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.CommonLib/   # Common Utilities
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.DomainModels/ # Domain Entities
โ”‚   โ”‚   โ””โ”€โ”€ YourApplication.Mappers/     # Object Mapping
โ”‚   โ”œโ”€โ”€ DomainHandler/              # CQRS Implementation
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.Handlers/    # Command Handlers
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.Queries/     # Query Handlers
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.PreBus/      # Message Preprocessing
โ”‚   โ”‚   โ””โ”€โ”€ YourApplication.RESTClients/ # External API Clients
โ”‚   โ”œโ”€โ”€ EndPoints/                  # Application Hosts
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.EndPoint.WebAPI/
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.EndPoint.Handlers.Default/
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.EndPoint.Subscribers.Default/
โ”‚   โ”‚   โ”œโ”€โ”€ YourApplication.EndPoint.CronJob/
โ”‚   โ”‚   โ””โ”€โ”€ DbMigrations/          # Database Migrations
โ”‚   โ””โ”€โ”€ Infrastructure/             # Infrastructure Layer
โ”‚       โ”œโ”€โ”€ Bus/                   # Message Bus (NServiceBus)
โ”‚       โ””โ”€โ”€ Db/                    # Database Implementations
โ””โ”€โ”€ YourApplication.Framework/            # Framework Layer
    โ”œโ”€โ”€ Bridge/                    # Framework Bridges
    โ””โ”€โ”€ PostBus/                   # Message Bus Interfaces

๐ŸŽฏ Core Architectural Patterns

1. Clean Architecture

The solution follows Clean Architecture principles with clear layer separation:

  • Domain Layer: Contains business logic and entities

  • Application Layer: Contains use cases and application services

  • Infrastructure Layer: Contains external concerns (database, messaging)

  • Presentation Layer: Contains controllers and API endpoints

2. CQRS (Command Query Responsibility Segregation)

// Command Pattern
public abstract class FlexCommandBridge : FlexCommand, IFlexCommandBridge
{
    // Command implementation
}

// Query Pattern  
public abstract class FlexQueryBridge : FlexQuery, IFlexQueryBridge
{
    // Query implementation
}

Benefits:

  • Clear separation between read and write operations

  • Independent scaling of read/write workloads

  • Optimized data models for each operation type

3. Domain-Driven Design (DDD)

  • Domain Models: Rich business entities with behavior

  • Value Objects: Immutable objects representing concepts

  • Domain Events: Business events that occur in the domain

  • Aggregates: Consistency boundaries for domain objects

4. Event-Driven Architecture

public abstract class FlexEventBridge : FlexEvent, IFlexEventBridge
{
    // Event implementation
}

Features:

  • Loose coupling between components

  • Asynchronous processing

  • Event sourcing capabilities

  • Eventual consistency

5. Vertical Slice Architecture (Partial Implementation)

The solution demonstrates elements of Vertical Slice Architecture by organizing functionality around business capabilities rather than technical layers:

Identified Vertical Slices

graph TB
    subgraph "Business Capabilities (Vertical Slices)"
        subgraph "Web API Slice"
            WebAPI[Web API Endpoint]
            WebControllers[Web Controllers]
            WebDTOs[API DTOs]
        end
        
        subgraph "Command Processing Slice"
            Handlers[Command Handlers]
            HandlerEndPoint[Handler Endpoint]
            HandlerMessages[Command Messages]
        end
        
        subgraph "Query Processing Slice"
            Queries[Query Handlers]
            QueryDTOs[Query DTOs]
            QueryMappers[Query Mappers]
        end
        
        subgraph "Event Processing Slice"
            Subscribers[Event Subscribers]
            SubscriberEndPoint[Subscriber Endpoint]
            SharedEvents[Shared Events]
        end
        
        subgraph "Background Processing Slice"
            CronJobs[Background Jobs]
            CronJobEndPoint[Cron Job Endpoint]
            JobSchedules[Job Schedules]
        end
        
        subgraph "External Integration Slice"
            RESTClients[External API Clients]
            RetryPolicies[Retry Policies]
            ExternalDTOs[External DTOs]
        end
    end
    
    WebAPI --> WebControllers
    Handlers --> HandlerEndPoint
    Queries --> QueryDTOs
    Subscribers --> SubscriberEndPoint
    CronJobs --> CronJobEndPoint
    RESTClients --> ExternalDTOs

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

// Example: Web API Slice
YourApplication.WebControllers/          # Presentation
โ”œโ”€โ”€ Controllers/                   # API Controllers
โ””โ”€โ”€ DTOs/                         # API Contracts

// Example: Command Processing Slice  
YourApplication.Handlers/               # Application Logic
โ”œโ”€โ”€ CommandHandlers/              # Business Logic
โ””โ”€โ”€ Messages/                     # Command Contracts

// Example: External Integration Slice
YourApplication.RESTClients/            # Infrastructure
โ”œโ”€โ”€ ExternalAPIs/                 # External Services
โ””โ”€โ”€ RetryPolicies/                # Resilience Patterns

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

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());
        }
    }
}

Configuration Priority (Highest to Lowest):

  1. Command Line Arguments

  2. Azure Key Vault

  3. User Secrets (Development only)

  4. Environment Variables

  5. appsettings.{Environment}.json

  6. appsettings.json

2. Dependency Injection

Service Registration Pattern

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);
    }
}

3. Error Handling

Custom Exception Types

public class BadRequestException : Exception
{
    public BadRequestException(string message) : base(message) { }
}

public class InternalServerErrorException : Exception
{
    public InternalServerErrorException(string message) : base(message) { }
}

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

// Single Tenant
services.AddTransient<IWriteDbConnectionProviderBridge, AppSettingsWriteDbConnectionProvider>();
services.AddTransient<IReadDbConnectionProviderBridge, AppSettingsReadDbConnectionProvider>();

// Multi Tenant Support
ConfigureFlexDb.ForMultitenantMasterDbForSqlServer(services);
ConfigureFlexDb.ForRepositoryForSqlServer(services);

6. Object Mapping

AutoMapper Configuration

public class CoreMapperConfiguration : FlexMapperProfile
{
    public CoreMapperConfiguration() : base()
    {
        #region Input
        //CreateMap<YourAPIModel, YourDomainModel>();
        #endregion

        #region Output
        //CreateMap<YourDomainModel, YourOutputAPIModel>();
        #endregion
    }
}

โš™๏ธ 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

  • appsettings.EndPoint.json - Endpoint-specific configuration

Security Best Practices

  1. Secrets Management

    • User Secrets for development

    • Azure Key Vault for production

    • Environment variables for containers

  2. Configuration Validation

    • Strongly-typed configuration classes

    • Runtime validation of required settings

    • Fail-fast on missing critical configuration


๐Ÿ—„๏ธ Database Strategy

Multi-Database Support

The solution supports multiple database providers:

Single Tenant

private static void EnableSingleTenant(IServiceCollection services)
{
    ConfigureSingleTenantDb(services);
    ConfigureFlexDb.ForRepositoryForSqlServer(services);
}

Multi Tenant

private static void EnableMultitenant(IServiceCollection services)
{
    ConfigureMultiTenantMasterDb(services);
    ConfigureFlexDb.ForMultitenantMasterDbForSqlServer(services);
    ConfigureFlexDb.ForRepositoryForSqlServer(services);
}

Migration Strategy

Separate migration projects for each database type:

  • YourApplication.Migrations.SqlServer

  • YourApplication.Migrations.MySql

  • YourApplication.Migrations.PostgreSql

  • YourApplication.Migrations.Tenant.* (for multi-tenant)

Database Factory Pattern

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>();
    }
}

๐Ÿ“จ Messaging & Communication

NServiceBus Integration

The solution uses NServiceBus for reliable messaging with multiple transport options:

RabbitMQ Configuration

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);
        }
    }
}

Azure Service Bus Configuration

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);
        }
    }
}

Message Types

  • Commands: Request for action

  • Events: Notification of something that happened

  • Queries: Request for data


๐Ÿข Multi-Tenancy Support

Tenant Isolation Strategies

  1. Database per Tenant: Complete isolation

  2. Schema per Tenant: Shared database, separate schemas

  3. Row-level Security: Shared database with tenant filtering

Implementation

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));
    }
}

๐Ÿš€ Deployment & Scalability

Application Endpoints

The solution provides multiple deployment options:

  1. Web API Endpoint - REST API services

  2. Handler Endpoint - Command processing

  3. Subscriber Endpoint - Event processing

  4. Cron Job Endpoint - Scheduled tasks

Scalability Features

  • Horizontal Scaling: Multiple instances of each endpoint

  • Load Balancing: Built-in support for load balancers

  • Message Queuing: Asynchronous processing

  • Database Sharding: Multi-tenant database support

  • Caching: Ready for implementation (not yet built-in)

Container Support

  • Docker ready configuration

  • Environment variables for configuration

  • Health checks for monitoring

  • Graceful shutdown support


๐ŸŽฏ Key Benefits

For Developers

  1. Rapid Development: Pre-built patterns and infrastructure

  2. Type Safety: Strong typing throughout the application

  3. Testability: Easy to unit test with dependency injection

  4. Maintainability: Clear separation of concerns

  5. Extensibility: Easy to add new features

For Operations

  1. Reliability: Built-in error handling and retry mechanisms

  2. Monitoring: Comprehensive logging and metrics

  3. Scalability: Horizontal scaling capabilities

  4. Security: Built-in security best practices

  5. Deployment: Multiple deployment options

For Business

  1. Time to Market: Faster development cycles

  2. Cost Effective: Reduced infrastructure costs

  3. Flexibility: Easy to adapt to changing requirements

  4. Compliance: Built-in audit trails and security

  5. Integration: Easy integration with external systems


๐Ÿ”ง Getting Started

Prerequisites

  • .NET 8, 9 SDK

  • Visual Studio 2022 or VS Code

  • SQL Server, MySQL, or PostgreSQL

  • RabbitMQ or Azure Service Bus or Azure Storage Queue Or Amazon Sqs (for messaging)

Quick Start

  1. Clone the repository

  2. Configure connection strings in appsettings.json

  3. Run database migrations

  4. Start the Web API endpoint

  5. Start message handlers (if using messaging)

Configuration

  1. Update appsettings.json with your database connection

  2. Configure message bus settings

  3. Set up logging configuration

  4. Configure multi-tenancy (if needed)


๐Ÿ”ฎ Future Enhancements

Caching Strategy (Planned)

While caching is not yet implemented, the architecture is designed to easily accommodate various caching strategies:

Recommended Caching Options

  • Redis: Distributed caching for multi-instance scenarios

  • In-Memory Caching: For single-instance applications

  • Response Caching: HTTP response caching

  • Query Result Caching: Database query result caching

Implementation Approach

// 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;
    }
}

Benefits of Adding Caching

  • Performance: Reduced database load and faster response times

  • Scalability: Better handling of high-traffic scenarios

  • Cost Efficiency: Reduced infrastructure costs

  • User Experience: Improved application responsiveness

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

YourApplication.Features/
โ”œโ”€โ”€ UserManagement/
โ”‚   โ”œโ”€โ”€ Controllers/
โ”‚   โ”œโ”€โ”€ Commands/
โ”‚   โ”œโ”€โ”€ Queries/
โ”‚   โ”œโ”€โ”€ Events/
โ”‚   โ””โ”€โ”€ DTOs/
โ”œโ”€โ”€ OrderProcessing/
โ”‚   โ”œโ”€โ”€ Controllers/
โ”‚   โ”œโ”€โ”€ Commands/
โ”‚   โ”œโ”€โ”€ Queries/
โ”‚   โ”œโ”€โ”€ Events/
โ”‚   โ””โ”€โ”€ DTOs/
โ””โ”€โ”€ PaymentProcessing/
    โ”œโ”€โ”€ Controllers/
    โ”œโ”€โ”€ Commands/
    โ”œโ”€โ”€ Queries/
    โ”œโ”€โ”€ Events/
    โ””โ”€โ”€ DTOs/

2. Slice-Specific Infrastructure

// 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>();
    }
}

3. Cross-Slice Communication

// 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
    }
}

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.

Last updated