Skip to content

Service Layer Architecture

Referenced Files in This Document - apps/service/main.py - apps/service/app.py - apps/service/deps.py - apps/service/routes/auth.py - apps/service/routes/config.py - apps/service/routes/decisions.py - apps/service/routes/history.py - apps/service/routes/jellyfin.py - apps/service/routes/logs.py - apps/service/routes/push.py - apps/service/routes/status.py - apps/service/routes/tools.py - apps/service/routes/users.py - apps/shared/config.py - apps/shared/db.py - src/database/decisions_db.py - src/database/history_db.py - src/database/logs_db.py - src/database/users_db.py - src/server/jellyfin.py - src/server/video_api.py - src/core/usecases.py - src/core/document_usecases.py - src/core/image_usecases.py

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

Introduction

This document explains the FastAPI-based service layer architecture, focusing on route organization, dependency injection, middleware components, and request/response handling. It clarifies the separation of concerns between routes (HTTP endpoints), services (business logic), and repositories (data access). It also documents authentication middleware, configuration management, error handling strategies, service composition patterns, validation layers, response formatting, relationships with core business logic modules, external API integrations, and common issues such as circular dependencies, performance optimization, and testing strategies.

Project Structure

The service layer is implemented under apps/service and integrates with shared configuration and database utilities, while delegating data access to src/database and external integrations via src/server. Core business logic resides in src/core.

graph TB
subgraph "Service Layer"
A["FastAPI App<br/>apps/service/app.py"]
B["Router: auth<br/>apps/service/routes/auth.py"]
C["Router: config<br/>apps/service/routes/config.py"]
D["Router: decisions<br/>apps/service/routes/decisions.py"]
E["Router: history<br/>apps/service/routes/history.py"]
F["Router: jellyfin<br/>apps/service/routes/jellyfin.py"]
G["Router: logs<br/>apps/service/routes/logs.py"]
H["Router: push<br/>apps/service/routes/push.py"]
I["Router: status<br/>apps/service/routes/status.py"]
J["Router: tools<br/>apps/service/routes/tools.py"]
K["Router: users<br/>apps/service/routes/users.py"]
end
subgraph "Shared"
S1["Config<br/>apps/shared/config.py"]
S2["DB Session Factory<br/>apps/shared/db.py"]
end
subgraph "Data Access (Repositories)"
R1["Decisions DB<br/>src/database/decisions_db.py"]
R2["History DB<br/>src/database/history_db.py"]
R3["Logs DB<br/>src/database/logs_db.py"]
R4["Users DB<br/>src/database/users_db.py"]
end
subgraph "External Integrations"
X1["Jellyfin Client<br/>src/server/jellyfin.py"]
X2["Video API Client<br/>src/server/video_api.py"]
end
subgraph "Core Business Logic"
L1["Use Cases<br/>src/core/usecases.py"]
L2["Document Use Cases<br/>src/core/document_usecases.py"]
L3["Image Use Cases<br/>src/core/image_usecases.py"]
end
A --> B
A --> C
A --> D
A --> E
A --> F
A --> G
A --> H
A --> I
A --> J
A --> K
B --> S1
C --> S1
D --> R1
E --> R2
F --> X1
G --> R3
H --> S1
I --> S1
J --> R1
K --> R4
D --> L1
D --> L2
D --> L3

Diagram sources - apps/service/app.py - apps/service/routes/auth.py - apps/service/routes/config.py - apps/service/routes/decisions.py - apps/service/routes/history.py - apps/service/routes/jellyfin.py - apps/service/routes/logs.py - apps/service/routes/push.py - apps/service/routes/status.py - apps/service/routes/tools.py - apps/service/routes/users.py - apps/shared/config.py - apps/shared/db.py - src/database/decisions_db.py - src/database/history_db.py - src/database/logs_db.py - src/database/users_db.py - src/server/jellyfin.py - src/server/video_api.py - src/core/usecases.py - src/core/document_usecases.py - src/core/image_usecases.py

Section sources - apps/service/main.py - apps/service/app.py

Core Components

  • Application entrypoint and router mounting: The FastAPI application is created and configured in the service app module, with routers mounted for each feature area.
  • Dependency injection: Centralized dependency providers are defined to supply configuration, database sessions, and reusable services to route handlers.
  • Middleware: Global middleware is registered for cross-cutting concerns such as logging, CORS, and authentication.
  • Request/response handling: Pydantic schemas define input/output contracts; responses are standardized through consistent formatting.

Key responsibilities by layer: - Routes: Parse requests, validate inputs, orchestrate calls to services/repositories, format responses. - Services (use cases): Encapsulate business rules and workflows, coordinating multiple repositories and external clients. - Repositories: Implement data access operations against the database or external APIs.

Section sources - apps/service/app.py - apps/service/deps.py

Architecture Overview

The service layer follows a layered architecture: - HTTP layer (routes) depends on services (use cases). - Services depend on repositories and external clients. - Repositories encapsulate persistence and external API calls. - Shared configuration and database session factories are provided via dependency injection.

sequenceDiagram
participant Client as "Client"
participant Router as "Route Handler"
participant Deps as "Deps Provider"
participant Repo as "Repository"
participant Ext as "External Client"
participant Core as "Core Use Case"
Client->>Router : "HTTP Request"
Router->>Deps : "Resolve dependencies"
alt Auth required
Router->>Router : "Validate token/session"
end
Router->>Repo : "Read/Write data"
Repo-->>Router : "Domain objects"
Router->>Core : "Invoke business logic"
Core-->>Router : "Result"
Router->>Ext : "Call external API if needed"
Ext-->>Router : "Response"
Router-->>Client : "Formatted JSON Response"

Diagram sources - apps/service/app.py - apps/service/deps.py - apps/service/routes/auth.py - src/database/decisions_db.py - src/server/jellyfin.py - src/core/usecases.py

Detailed Component Analysis

Authentication Middleware and Protected Routes

  • Authentication flow: Requests to protected endpoints pass through an authentication check that validates tokens or sessions before invoking route handlers.
  • Route protection: Specific routers (e.g., auth, users) enforce authorization checks using dependency-injected providers.
  • Error handling: Unauthorized or invalid credentials return standardized error responses.
flowchart TD
Start(["Request Received"]) --> CheckAuth["Check Auth Token/Session"]
CheckAuth --> Valid{"Valid?"}
Valid --> |No| Err401["Return 401 Unauthorized"]
Valid --> |Yes| NextHandler["Invoke Route Handler"]
NextHandler --> End(["Response Sent"])
Err401 --> End

Diagram sources - apps/service/routes/auth.py - apps/service/deps.py

Section sources - apps/service/routes/auth.py - apps/service/deps.py

Configuration Management

  • Centralized configuration: Settings are loaded from environment variables and/or config files via a shared configuration module.
  • Dependency injection: Config instances are provided to routes and services through a single source of truth.
  • Validation: Configuration values are validated at startup to fail fast on misconfiguration.
classDiagram
class AppConfig {
+load()
+validate()
+get(key)
}
class DepsProvider {
+provide_config()
}
class RouteHandlers {
+read_config()
+update_config()
}
DepsProvider --> AppConfig : "provides"
RouteHandlers --> DepsProvider : "depends on"

Diagram sources - apps/shared/config.py - apps/service/deps.py - apps/service/routes/config.py

Section sources - apps/shared/config.py - apps/service/routes/config.py - apps/service/deps.py

Database Session and Repository Pattern

  • Session factory: A centralized database session provider ensures consistent connection lifecycle management across requests.
  • Repository modules: Each domain area has a dedicated repository file implementing CRUD and query operations.
  • Separation of concerns: Routes call repositories directly or via use cases; repositories abstract SQL and ORM details.
classDiagram
class DBSessionFactory {
+get_session()
+close_session(session)
}
class DecisionsRepo {
+create_decision(data)
+get_decision(id)
+list_decisions(filters)
}
class HistoryRepo {
+append_history(entry)
+query_history(filters)
}
class LogsRepo {
+log_event(event)
+fetch_logs(filters)
}
class UsersRepo {
+create_user(user)
+authenticate(username,password)
+get_user_by_id(id)
}
DBSessionFactory --> DecisionsRepo : "provides session"
DBSessionFactory --> HistoryRepo : "provides session"
DBSessionFactory --> LogsRepo : "provides session"
DBSessionFactory --> UsersRepo : "provides session"

Diagram sources - apps/shared/db.py - src/database/decisions_db.py - src/database/history_db.py - src/database/logs_db.py - src/database/users_db.py

Section sources - apps/shared/db.py - src/database/decisions_db.py - src/database/history_db.py - src/database/logs_db.py - src/database/users_db.py

External API Integrations

  • Jellyfin client: Encapsulates communication with Jellyfin media server for metadata and playback state.
  • Video API client: Provides methods to interact with video-related external services.
  • Integration pattern: Clients are injected into routes/services to keep HTTP concerns isolated.
classDiagram
class JellyfinClient {
+search(query)
+get_metadata(item_id)
+update_playback_state(state)
}
class VideoAPIClient {
+stream_url(media_id)
+health_check()
}
class JellyfinRouter {
+sync_with_jellyfin()
+get_library_info()
}
JellyfinRouter --> JellyfinClient : "uses"

Diagram sources - src/server/jellyfin.py - src/server/video_api.py - apps/service/routes/jellyfin.py

Section sources - src/server/jellyfin.py - src/server/video_api.py - apps/service/routes/jellyfin.py

Core Business Logic and Use Cases

  • Use case modules: Encapsulate domain-specific workflows and orchestrate repository calls and external clients.
  • Composition: Routes invoke use cases rather than calling repositories directly when complex workflows are involved.
  • Examples: Decision-making, document processing, and image classification workflows are modeled as use cases.
classDiagram
class UseCases {
+make_decision(input)
+execute_workflow(params)
}
class DocumentUseCases {
+parse_document(file_path)
+extract_metadata(doc)
}
class ImageUseCases {
+classify_image(image_path)
+enhance_image(image_data)
}
UseCases <.. DocumentUseCases : "composes"
UseCases <.. ImageUseCases : "composes"

Diagram sources - src/core/usecases.py - src/core/document_usecases.py - src/core/image_usecases.py

Section sources - src/core/usecases.py - src/core/document_usecases.py - src/core/image_usecases.py

Request/Response Handling and Validation

  • Input validation: Pydantic models ensure typed and validated request payloads.
  • Response formatting: Consistent JSON structures are returned, including success envelopes and error codes.
  • Error handling: Global exception handlers map domain errors to appropriate HTTP statuses and messages.
flowchart TD
In(["Incoming Request"]) --> Validate["Pydantic Validation"]
Validate --> Ok{"Valid?"}
Ok --> |No| ErrResp["Standard Error Response"]
Ok --> |Yes| Process["Route Handler + Services"]
Process --> Format["Format Response"]
Format --> Out(["JSON Response"])
ErrResp --> Out

[No sources needed since this diagram shows conceptual workflow, not actual code structure]

Dependency Analysis

The service layer exhibits clear separation: - Routes depend on deps providers and repositories/use cases. - Repositories depend on DB session factories. - External clients are independent modules injected where needed. - Core use cases coordinate repositories and external clients without direct HTTP concerns.

graph LR
Routes["Routes"] --> Deps["Deps Providers"]
Routes --> Repos["Repositories"]
Routes --> Core["Core Use Cases"]
Repos --> DB["DB Session Factory"]
Core --> Repos
Core --> Ext["External Clients"]

Diagram sources - apps/service/app.py - apps/service/deps.py - apps/shared/db.py - src/database/decisions_db.py - src/server/jellyfin.py - src/core/usecases.py

Section sources - apps/service/app.py - apps/service/deps.py - apps/shared/db.py

Performance Considerations

  • Connection pooling: Ensure database connections are pooled and reused efficiently via the session factory.
  • Async I/O: Prefer asynchronous handlers for I/O-bound operations (external APIs, DB queries) to improve throughput.
  • Caching: Introduce caching for frequently accessed read-only data (e.g., configuration, library metadata).
  • Pagination: Apply pagination for list endpoints to reduce payload sizes and memory usage.
  • Rate limiting: Protect endpoints with rate limiting to prevent abuse and resource exhaustion.
  • Logging: Use structured logging with sampling for high-volume endpoints to minimize overhead.

[No sources needed since this section provides general guidance]

Troubleshooting Guide

Common issues and strategies: - Circular dependencies: Avoid importing routes into services and vice versa; rely on dependency injection to break cycles. - Misconfiguration: Validate settings at startup and surface clear errors when required keys are missing. - DB session leaks: Ensure sessions are closed after each request; use context managers or middleware to guarantee cleanup. - External API failures: Implement retries with backoff and circuit breakers for resilient integrations. - Error mapping: Centralize exception handling to convert domain exceptions into consistent HTTP responses.

Section sources - apps/service/app.py - apps/service/deps.py

Conclusion

The service layer implements a clean separation of concerns with FastAPI routes orchestrating requests, use cases encapsulating business logic, and repositories managing data access. Dependency injection centralizes configuration and resources, while middleware enforces cross-cutting concerns like authentication. By adhering to these patterns, the system remains maintainable, testable, and scalable.