Clean Architecture Principles
Table of Contents
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendix
Introduction
This document systematically explains the Clean Architecture principles and practices adopted by the Sparrow framework, focusing on separation of concerns, dependency inversion principle (DIP), interface segregation principle (ISP) and other design ideas, combined with entities, use cases, infrastructure and interface adapter layers in the codebase, explaining the responsibility division of application boundaries, internal business rules and external interfaces. It also provides usage of dependency injection container, interface definition best practices, and how to improve system testability, maintainability and extensibility through this architecture.
Project Structure
Sparrow adopts a layered organization approach, typical layers are as follows:
- Entity Layer: Carries core business data and rules, does not depend on external layers
- Use Case Layer: Encapsulates business use cases and process orchestration, defines repository interfaces
- Interface Adapter Layer: HTTP, message subscription and other external interface adapters
- Infrastructure Layer: Database, message middleware, configuration and other external dependencies
Core Components
- Application Entry and Lifecycle Management: App provides global configuration, logging, HTTP engine, container, subprocess management and graceful shutdown
- Dependency Injection Container: Container supports anonymous and named dependency registration and resolution, has singleton caching capability
- Configuration System: Config structure centrally manages application, server, logging, database, message middleware and other configurations
- Entity and Aggregate Root: Entity defines generic entity interface; AggregateRoot extends command handling, event application and version control
- Repository Interface and Base Implementation: Repository generic interface defines CRUD and query capabilities; BaseRepository provides default implementation and error hints
- Session Use Case: SessionService encapsulates session creation, reading, updating, deletion and expiration cleanup business logic
- Message Bus and Stream Processing: StreamHub implements Startable and GracefulClose, unifies subscription and startup/shutdown process
- HTTP Adapter: SessionHandler maps HTTP requests to session use case service
Architecture Overview
Clean Architecture in Sparrow is implemented as:
- Core Radiating Outward: Entities and use cases do not depend on external details (databases, message middleware), only couple through interfaces
- Dependency Direction: External infrastructure (databases, messages, HTTP) provides implementations to inner layers, inner layers only hold abstract interfaces
- Inversion of Control: Centrally manages dependency resolution and lifecycle through container, reduces coupling between modules
- Replaceability: Same interface can bind multiple implementations (such as different databases, different message backends)
Detailed Component Analysis
Dependency Injection Container (IoC)
Design Points:
- Supports anonymous and named provider registration, prioritizes named during resolution, falls back to anonymous
- Automatically injects dependencies by resolving constructor parameters through reflection
- Singleton Caching: Caches instance after first resolution, subsequent resolutions reuse
Key Interfaces:
- Register/RegisterNamed: Register providers
- Resolve/ResolveByName/ResolveInstance: Resolve dependencies
Usage Recommendations:
- Register "external implementations" as concrete types, use "internal abstractions" as resolution targets
- Use named registration for multi-implementation scenarios, combine with ResolveByName for precise resolution
Application Startup and Lifecycle
Initialization Phase:
- Load configuration, create logging, initialize Gin engine, create container
- Register subprocesses (such as message subscribers, task schedulers, etc.), unified under App management
Startup Phase:
- Subprocess exponential backoff retry startup, failed automatically queue for retry
- Start HTTP server, listen for graceful shutdown signal
Cleanup Phase:
- Cancel retry goroutine, gracefully close subprocesses one by one
Entity and Aggregate Root
Entity Interface (Entity): Unifies basic capabilities like ID, timestamps, etc.
Aggregate Root Interface (AggregateRoot): Extends command handling, event application, version control, snapshot and event replay capabilities on Entity basis
Design Significance: Places business invariants and consistency boundaries in inner layer, avoids being affected by external changes
Repository Interface and Base Implementation
Repository Interface (Repository[T]): Defines generic entity CRUD, batch operations, pagination, conditional queries and statistics capabilities
Base Implementation (BaseRepository[T]): Provides default implementation and error hints, concrete database implementations inherit and extend
Design Significance: Use case layer only depends on abstract interface, concrete persistence implementation is replaceable
Session Use Case
Business Responsibilities: Session creation, reading, updating, deletion, data read/write, expiration cleanup
Dependency Injection: Injects Repository[*Session] and logging through constructor
Interaction with Adapters: HTTP Handler delegates requests to SessionService
Message Bus and Stream Processing
- StreamHub implements Startable and GracefulClose, unifies subscriber lifecycle management
- Creates Hub through App.NewHub, App.AddSub registers event handlers
- Integrates with NATS JetStream, supports event publish/subscribe and stream processing
Configuration System
- Config structure centrally declares application, server, logging, database, message middleware and other configuration items
- Sets default values through viper and environment variable overrides, convenient for deployment in different environments
Dependency Analysis
Inter-layer Dependency Direction
- Adapter layer depends on use case layer interfaces, does not depend on concrete implementations
- Use case layer depends on entities and repository interfaces, does not depend on concrete infrastructure
- Infrastructure layer implements use case layer abstractions, provides concrete implementations upward
Container and Inversion of Control
- App uniformly resolves external dependencies (databases, messages, logging, etc.) through Container
- Use case services inject abstract repositories through constructors, convenient for replacement and testing
Possible Circular Dependencies
- Current structure decouples through interfaces, no direct circular dependency signs found
Performance Considerations
Container Resolution
- Singleton caching reduces repeated construction costs; complex dependency chain resolution needs attention to reflection overhead
Data Persistence
- Batch operations (such as SaveBatch) can reduce round-trips and transaction overhead
Event Bus
- JetStream subscription and publishing should combine backpressure strategy and partition design to avoid blocking
HTTP Service
- Reasonably set timeout and concurrency limits to avoid resource exhaustion
Troubleshooting Guide
Container Resolution Failure
- Symptom: Resolve returns "provider not found"
- Troubleshooting: Confirm Register/RegisterNamed are correctly registered; check if names are consistent during resolution
Dependency Injection Abnormal
- Symptom: Constructor parameters cannot be resolved
- Troubleshooting: Ensure parameter types are registered in container; avoid circular dependencies
Session Service Errors
- Symptom: Create/read/update/delete fails
- Troubleshooting: Check if repository implementation is correct; log output to locate specific errors
Message Subscription Abnormal
- Symptom: Events not consumed or repeatedly consumed
- Troubleshooting: Confirm aggregate type and event type registered by AddSub; check NATS connection and stream configuration
Conclusion
Sparrow clearly separates business core from external details through clean architecture, achieves inversion of control and replaceability through dependency injection container, making the system have good testability, maintainability and extensibility. Following DIP and ISP, interface-centric dependency design ensures use case layer stability, entity layer purity, adapter layer flexibility.
Appendix
Token Model (Authentication Related)
- Token structure contains access token, refresh token, authorization scope and expiration time, convenient for adapter layer to return to client