๐งผ Clean Architecture in Modern Systems¶
Clean Architecture is a software design approach that emphasizes separation of concerns and independence of frameworks, libraries, and external systems. Introduced by Robert C. Martin (Uncle Bob), Clean Architecture ensures that business logic and application logic remain independent from technical details such as databases, UI frameworks, or external services.
Info
At ConnectSoft, Clean Architecture is foundational across our solution templates, framework libraries, and AI-ready platforms. We treat it not just as a theory โ but as a practical execution model for delivering high-quality .NET-based cloud-native systems.
๐งญ Overview¶
Clean Architecture organizes the system into layers, with the innermost layers containing the most critical business logic. Each layer is designed to depend only on the layers closer to the core, creating a highly testable, flexible, and maintainable system.
Key principles:
- Independence: No layer should depend on implementation details of a more external layer.
- Separation of Concerns: Business logic, application workflows, and technical details are clearly separated.
- Testability: By isolating core logic, testing becomes easier and more efficient.
To provide additional clarity, the following diagram illustrates the Clean Architecture key components and their relationships:
๐งฑ Key Layers¶
-
Entities
- Core business objects that encapsulate enterprise-wide rules.
- Corresponds to the Domain Layer in DDD.
-
Use Cases
- Application-specific business rules.
- Implements workflows and orchestrates the behavior of entities.
- Corresponds to the Application Layer in DDD.
-
Interface Adapters
- Contains code for converting data from the use case and entities to the format required by external systems (e.g., UI, API responses).
-
Frameworks and Drivers
- The outermost layer that includes frameworks, databases, and external tools.
- This layer depends on everything else but does not influence the core logic.
graph TD
Frameworks -->|I/O Calls| Adapters
Adapters -->|DTO Mapping| UseCases
UseCases -->|Coordinates| Entities
๐งฉ Clean Architecture and Use Cases¶
Use cases are central to Clean Architecture, representing the application's specific business logic and workflows. They dictate how data flows between layers and ensure that each operation aligns with business goals.
For more details, see: Use Cases.
๐ฏ Importance of Clean Architecture¶
- Independence of Frameworks: The system remains adaptable to changing frameworks and libraries.
- Testability: Core logic is isolated, making it easy to test without dependencies.
- Flexibility: New features can be added with minimal impact on existing code.
- Maintainability: Clear separation of concerns leads to easier debugging and updates.
- Scalability: The layered approach simplifies scaling the system.
๐ Clean Architecture vs. Domain-Driven Design¶
| Aspect | Clean Architecture | Domain-Driven Design (DDD) |
|---|---|---|
| Focus | Emphasizes separation of concerns and independence from frameworks. | Focuses on modeling the domain and aligning design with business needs. |
| Core Concept | Layers: Entities, Use Cases, Interface Adapters, Frameworks. | Domain: Entities, Aggregates, Value Objects, Domain Services, etc. |
| Application Logic | Use cases orchestrate the flow between layers. | Application services manage workflows, but domain logic resides in the domain layer. |
| Interaction with Domain | Centers around the use case layer to interact with domain entities. | Domain entities are at the core, with application services orchestrating workflows. |
| Independence | Independence from frameworks and technical details is a primary goal. | Independence is focused on ensuring the domain layer is free of external concerns. |
๐ง Practical Implementation in ConnectSoft¶
Clean Architecture is embedded in every ConnectSoft solution template. Below are implementation examples and best practices:
๐ Folder Structure Example¶
/src
/Application
/UseCases
CreateOrder
- CreateOrderCommand.cs
- CreateOrderHandler.cs
/Domain
/Entities
- Order.cs
/ValueObjects
- Address.cs
/Infrastructure
/Persistence
- OrderRepository.cs
/Web
/Controllers
- OrderController.cs
๐งฑ Typical Use Case Flow¶
- API Layer (Controller) receives a request.
- Controller calls Application Use Case Handler (e.g.,
CreateOrderHandler). - Use Case invokes business rules from Domain Entity (e.g.,
Order.AddItem()). - Use Case calls infrastructure repository to persist changes.
- Response is mapped and returned.
graph LR
API[API Request] --> UseCase[Use Case Handler]
UseCase --> Domain[Domain Entity]
UseCase --> Repo[Repository]
Repo --> DB[(Database)]
UseCase --> Mapper[Map to DTO]
Mapper --> API
๐งช Testability Pattern¶
- Unit test:
CreateOrderHandlerin isolation with mocked repository. - Integration test:
OrderControllerusing real application + in-memory database.
[Fact]
public async Task CreatesOrderSuccessfully()
{
var handler = new CreateOrderHandler(mockRepo.Object);
var result = await handler.Handle(new CreateOrderCommand(...), default);
Assert.True(result.IsSuccess);
}
- Independence of Frameworks: The system remains adaptable to changing frameworks and libraries.
- Testability: Core logic is isolated, making it easy to test without dependencies.
- Flexibility: New features can be added with minimal impact on existing code.
- Maintainability: Clear separation of concerns leads to easier debugging and updates.
- Scalability: The layered approach simplifies scaling the system.
๐งช Real-World Examples¶
๐ E-Commerce¶
- Use Case: PlaceOrder
- Flow: UI โ Interface Adapter โ Use Case โ Domain Layer (Entities)
graph TD UI -->|Data Mapping| InterfaceAdapters InterfaceAdapters -->|Calls| UseCases UseCases -->|Coordinates| Entities UseCases -->|Accesses| RepositoriesHold "Alt" / "Option" to enable pan & zoom
๐ฅ Healthcare¶
- Use Case: ScheduleAppointment
- Flow: API โ Interface Adapter โ Use Case โ Domain Layer
graph TD API -->|Input DTO| InterfaceAdapters InterfaceAdapters -->|Orchestrates| UseCases UseCases -->|Interacts With| DomainEntitiesHold "Alt" / "Option" to enable pan & zoom
๐ฐ Finance¶
- Use Case: TransferFunds
- Flow: UI โ Use Case โ Domain Layer โ Infrastructure Layer
graph TD UI -->|Data Transfer| UseCases UseCases -->|Coordinates| DomainEntities UseCases -->|Delegates| InfrastructureHold "Alt" / "Option" to enable pan & zoom
โ Best Practices for Clean Architecture¶
-
Dependency Inversion:
- Depend on abstractions, not concrete implementations.
- Use interfaces for repository and service dependencies.
-
Keep Core Logic Isolated:
- Avoid dependencies on frameworks, databases, or external libraries in the core layers (Entities and Use Cases).
-
Stateless Use Cases:
- Ensure use cases are stateless and reusable across the application.
-
Use DTOs for Data Flow:
- Employ Data Transfer Objects (DTOs) for communication between layers.
-
Test Core Logic Independently:
- Focus on unit tests for core logic without dependencies on infrastructure.
-
Design for Evolution:
- Anticipate changes in external layers (e.g., swapping databases, replacing UI frameworks).
-
Favor Composition Over Inheritance:
- Compose behavior using interfaces and domain services rather than creating deep inheritance hierarchies.
๐งฉ Diagrams in Clean Architecture¶
๐ Layered Architecture¶
graph TD
FrameworksDrivers -->|Infrastructure Calls| InterfaceAdapters
InterfaceAdapters -->|Data Conversion| UseCases
UseCases -->|Logic Orchestration| Entities
- Frameworks and Drivers depend on Interface Adapters for data conversion.
- Use Cases orchestrate workflows and depend on core Entities.
โ๏ธ Use Case Interaction¶
sequenceDiagram
participant UI as User Interface
participant UseCase as Use Case Layer
participant Domain as Domain Entities
participant Repo as Repository
UI->>UseCase: Submit Data
UseCase->>Domain: Apply Business Logic
UseCase->>Repo: Save to Database
Repo-->>UseCase: Acknowledge Save
UseCase-->>UI: Return Success
- The Use Case layer processes user actions, applies domain logic, and interacts with repositories.
๐งฌ Combining Clean Architecture with DDD, Microservices, EDA, and Hexagonal Patterns¶
Modern ConnectSoft services combine Clean Architecture with other proven patterns to enhance modularity, decoupling, and scalability.
๐ง Integration Highlights:¶
- Use DDD in the core (Entities, Use Cases)
- Apply Microservices as bounded contexts
- Emit Domain Events (EDA) for async workflows
- Use CQRS for optimized reads vs writes
- Apply Ports & Adapters to external systems (Hexagonal)
๐๏ธ Combined Architecture Example¶
graph TD
subgraph Bounded Contexts
Microservice1["Microservice 1 (Order)"]
Microservice2["Microservice 2 (Inventory)"]
end
subgraph CleanArchitecture
Entities["Entities (Core Business Objects)"]
UseCases["Use Cases (Workflows)"]
InterfaceAdapters["Interface Adapters (APIs, DTOs, ViewModels)"]
FrameworksDrivers["Frameworks and Drivers (Database, UI, External Services)"]
end
Microservice1 -->|Domain Events| Microservice2
FrameworksDrivers --> InterfaceAdapters
InterfaceAdapters --> UseCases
UseCases --> Entities
This unified model aligns domain-driven design with scalable, testable service boundaries.
๐งพ Conclusion¶
Clean Architecture empowers developers to build robust, testable, and independent systems that scale over time. It provides a clear boundary between core business logic and technical concerns, making it easier to evolve systems as needs change.
At ConnectSoft, we embrace Clean Architecture not just as a theory, but as a practical standard across our .NET-based service templates, SaaS platforms, and AI-integrated agents.
When combined with DDD, CQRS, Microservices, and Event-Driven Architecture, Clean Architecture becomes a powerful foundation for building modular, cloud-native applications that deliver long-term business value.