Layered Architecture Details
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 layered architecture design and implementation of the Sparrow framework, focusing on the responsibility boundaries, dependency directions and interaction patterns of the application layer, adapter layer, domain layer, and infrastructure layer. Through key components such as dependency injection container, repository interface, event bus and HTTP adapter, it achieves separation of concerns and loose coupling with high cohesion. The document also provides inter-layer interface design, dependency injection usage, error propagation mechanisms and best practice recommendations, helping developers quickly understand and correctly extend the framework.
Project Structure
Sparrow adopts a "clean architecture" style, organizing business logic around the use case layer, with the domain layer carrying entities and aggregate roots, the infrastructure layer providing persistence, messaging and runtime environment, and the adapter layer responsible for external protocol access (such as HTTP). Core modules are distributed as follows:
- Application Layer (Use Case Layer): usecase/* provides service orchestration and business rules, such as session service, repository interface and base implementation
- Domain Layer: entity/* defines entity and aggregate root interfaces, carries business invariants
- Adapter Layer: adapter/http/* provides HTTP request handling and route registration
- Infrastructure Layer: bootstrap/* (container, configuration, database), persistence/repo/* (multiple storage implementations), messaging/* (event bus and stream processing)
Core Components
Application Layer (Use Case Layer)
- Session Service: Encapsulates session lifecycle and data read/write, depends on repository interface
- Repository Interface and Base Implementation: Unifies CRUD and query capabilities, supports pagination, conditional queries, random sampling, etc.
Domain Layer
- Aggregate Root Interface: Unifies event sourcing, version control and business validation
- Task Entity: Carries task state machine and retry strategy
Adapter Layer
- HTTP Handlers: Maps HTTP requests to use case layer services; route registration is handled by adapter layer
Infrastructure Layer
- Application Main Container: Global configuration, logging, routing engine, service container and subprocess management
- Dependency Injection Container: Supports anonymous and named dependency registration and resolution
- Database Abstraction: Unifies Redis, SQL, Badger and other clients
- Event Bus and Stream Processing: Hub/Bus abstraction based on NATS JetStream
Architecture Overview
Sparrow's layered boundaries and interactions are as follows:
- Dependency Direction: Upper layers depend on lower layer interfaces, lower layers do not depend on upper layers
- Separation of Concerns: Adapter layer only responsible for protocol conversion; use case layer focuses on business orchestration; domain layer maintains invariants; infrastructure layer provides general capabilities
- Loose Coupling High Cohesion: Achieves decoupling through interfaces and dependency injection; each layer internally organized around single responsibility
Detailed Component Analysis
Application Layer (Use Case Layer)
Session Service:
- Responsibilities: Create, get, update, delete sessions; maintain expiration time; cleanup expired sessions
- Dependencies: Repository interface (Repository[*entity.Session]), logging and TTL configuration
- Error Propagation: Internally captures and logs errors, returns unified error types upward
Repository Interface and Base Implementation:
- Interface: Unifies CRUD, batch operations, pagination, conditional queries, random sampling, etc.
- Base Class: Provides empty implementation and general validation (such as ID non-empty, pagination parameter defaults)
Domain Layer
Aggregate Root Interface:
- Responsibilities: Unifies aggregate identifier, version control, event sourcing (command handling, event application, uncommitted event management, rebuilding state from events/snapshot) and business validation
Task Entity:
- Responsibilities: Task state machine (pending/running/completed/failed/cancelled), retry control, execution duration calculation, expiration judgment
Adapter Layer
HTTP Handlers:
- Session Handler: Maps HTTP requests to session service, handles create, get, update data, delete and get specific data
- Task Handler: Reserved interface, demonstrates adapter layer's approach to task use case integration
Route Registration: Adapter layer is responsible for binding handlers to routes
Infrastructure Layer
Application Main Container:
- Responsibilities: Load configuration, create logging, initialize routing engine, register service container, manage subprocesses and graceful shutdown
- Dependency Injection: Resolves named or anonymous dependencies through container, such as session service, event publisher, NATS connection, etc.
Dependency Injection Container:
- Capabilities: Register anonymous/named constructors, resolve dependency chains, cache instances (singleton behavior)
Database Abstraction:
- Unifies Redis, SQL, Badger clients, convenient for replacement and extension
Event Bus and Stream Processing:
- StreamHub: Encapsulates subscriber collection, supports startup and graceful shutdown; Hub/Bus based on NATS JetStream
Dependency Analysis
Inter-layer Dependency Direction
- Adapter Layer → Use Case Layer: HTTP handlers depend on session service
- Use Case Layer → Domain Layer: Session service depends on repository interface; repository interface and base implementation do not depend on upper layers
- Infrastructure Layer → Use Case Layer: Container and database abstraction provide runtime support for use case layer
Dependency Injection and Interface Contracts
- Use case layer programs through interfaces, avoids directly depending on concrete implementations; infrastructure layer registers concrete implementations through container
- Container supports named and anonymous dependencies, convenient for multi-implementation switching and composition
Performance Considerations
Repository Implementation
- SQL repository supports soft delete, transaction batch processing, conditional queries and sorting pagination, suitable for relational scenarios; pay attention to index and query condition optimization
Event Bus
- Hub/Bus based on NATS JetStream, supports multiple consumers and stream processing; reasonably set stream retention policy and consumer groups
Dependency Injection
- Singleton caching reduces repeated construction costs; avoid resolution failure due to circular dependencies
HTTP Adapter
- Gin default middleware and timeout configuration; pay attention to routing hierarchy and handler single responsibility, avoid blocking
Troubleshooting Guide
Dependency Resolution Failure
- Symptom: Error when resolving named/anonymous dependencies
- Troubleshooting: Confirm constructor signature and return type match; check registration order and name consistency
Repository Operation Abnormal
- Symptom: Save/update/delete errors or return unimplemented errors
- Troubleshooting: Confirm concrete repository implementation is registered; check entity ID and field constraints; verify database connection and permissions
Session Service Errors
- Symptom: Create/get/update/delete fails
- Troubleshooting: View log output; confirm TTL configuration and expiration cleanup logic; verify repository implementation
Event Bus/Stream Processing
- Symptom: Subscription fails or cannot start
- Troubleshooting: Check NATS connection and JetStream stream configuration; confirm Hub/Bus startup and shutdown process
Conclusion
Sparrow achieves good separation of concerns and extensibility through clear layered boundaries and interface contracts, combined with dependency injection container and multi-implementation infrastructure layer. Developers should follow the principle of "upper layers only depend on interfaces, lower layers do not depend on upper layers", use containers for dependency assembly, leverage repositories and event bus for business orchestration and cross-service communication, thereby reducing coupling while ensuring cohesion.
Appendix
Code Example Paths (Not Showing Specific Code Content)
- Application startup and container resolution: pkg/bootstrap/app.go, pkg/bootstrap/container.go
- Session service and repository interface: pkg/usecase/session.go, pkg/usecase/repo.go
- SQL repository implementation and transaction processing: pkg/persistence/repo/sqldb.go
- HTTP session handler and routing: pkg/adapter/http/handlers/session.go, pkg/adapter/http/router/tasks.go
- Event bus Hub/Bus: pkg/messaging/bus.go
- Configuration and database abstraction: pkg/config/config.go, pkg/bootstrap/db.go