Skip to main content

Layered Architecture Details

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