Skip to main content

Clean Architecture Principles

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Dependency Analysis
  7. Performance Considerations
  8. Troubleshooting Guide
  9. Conclusion
  10. 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 structure contains access token, refresh token, authorization scope and expiration time, convenient for adapter layer to return to client