Modular Monolith Architecture
Relevant source files
The following files were used as context for generating this wiki page:
Athena is an osu! bancho-compatible private server designed as a layered modular monolith. The architecture focuses on hexagonal adapters, a strict separation between command and query use cases, and localized persistence through Unit of Work patterns. This structure ensures that business rules remain transport-independent and internal ownership boundaries are strictly maintained.
Sources: README.md:1-25, AGENTS.md:120-125
Architectural Layers and Dependency Direction
The project follows a specific dependency direction where outer layers import inner layers, but inner layers (like Domain and Shared) remain pure and independent of infrastructure or transport concerns.
The diagram illustrates the production dependency direction as defined in the project's design philosophy.
Sources: AGENTS.md:144-150, README.md:37-40
Layer Responsibilities
| Layer | Responsibility |
|---|---|
| Composition | Dishka providers and runtime graph construction. Wires dependencies for app and worker. |
| Transports | Adapters for client families (stable, lazer, API). Handles wire parsing/building. |
| Services | Domain-scoped business logic divided into state-changing (commands) and read-only (queries). |
| Repositories | Persistence ports and concrete implementations (SQLAlchemy, Memory, Valkey). |
| Infrastructure | Low-level adapters for DB (PostgreSQL), Cache (Valkey), Storage, and Jobs (taskiq). |
| Domain | Core business language using standard Python dataclasses. Independent of I/O. |
| Shared | Primitive shared errors, constants, and types used across all layers. |
Sources: AGENTS.md:152-162, README.md:42-50, pyproject.toml:132-140
Command and Query Responsibility Segregation (CQRS)
Athena enforces a strict split between state-changing workflows and read-only operations at the service and repository levels.
Command Use-Cases
Commands own business rules, authorization, idempotency, and mutation workflows. They utilize the Unit of Work pattern to manage durable consistency and transaction timing. Command repositories are accessed via interfaces that define mutation and consistency-check ports.
Sources: AGENTS.md:179-181, AGENTS.md:191-193
Query Use-Cases
Queries are read-only workflows that provide display, search, and compatibility views. They do not open Unit of Work and do not mutate state. If data is missing, they represent it as empty results rather than attempting repairs.
Sources: AGENTS.md:183-185, AGENTS.md:196-197
Sequence of a state-changing operation using the Command/Unit of Work pattern.
Sources: AGENTS.md:179-185, AGENTS.md:191-195
Dependency Injection and Composition
The project uses Dishka for dependency composition, managing the lifecycle of resources across different scopes.
- APP Scope: Owns long-lived resources like configuration, DB engines, Valkey clients, and taskiq brokers.
- REQUEST Scope: Owns per-request dependencies such as the Unit of Work factories.
Services, domain objects, and repository interfaces are prohibited from importing Dishka or specific provider types, ensuring they remain decoupled from the DI framework.
Sources: AGENTS.md:167-175, tests/unit/test_architecture_baseline.py:27-28
Domain and Compatibility Boundaries
Domain packages utilize standard @dataclass(slots=True) models and are strictly forbidden from importing I/O libraries like Pydantic, SQLAlchemy, or Starlette.
Compatibility Semantics
Compatibility for different client families (e.g., stable Bancho) is separated from core business meaning. Stable-specific values (like Bancho Client Permissions) live in domain/compatibility/stable. These values may be derived from core domain values but are never used as internal scoring or authorization inputs.
Sources: AGENTS.md:213-220, AGENTS.md:222-226
Relationship between core domain models and compatibility-specific representations.
Sources: AGENTS.md:213-228, tests/unit/test_architecture_boundary_contract.py:165-175
Two-Process Execution Model
Athena operates using two distinct process types to handle different categories of workloads:
- App Process (uvicorn): Handles immediate responses such as authentication, chat delivery, and score intake.
- Worker Process (taskiq): Manages heavy asynchronous processing like performance (PP) calculation, leaderboard updates, and medal grants.
Volatile state (sessions, presence, packet queues) is centralized in Valkey to allow process restarts without losing session data and to facilitate horizontal scaling.
Sources: AGENTS.md:315-321, README.md:27-35
Validation and Quality Gates
Architecture boundaries are mechanically enforced using import-linter. Contracts in pyproject.toml define forbidden imports to prevent layer leakage.
# Example of forbidden dependency enforcement from pyproject.toml
[[tool.importlinter.contracts]]
name = "Services stay adapter independent"
type = "forbidden"
source_modules = ["osu_server.services"]
forbidden_modules = [
"osu_server.composition",
"osu_server.jobs",
"osu_server.repositories.sqlalchemy",
"sqlalchemy",
"starlette",
"fastapi",
]
Sources: pyproject.toml:148-160, tests/unit/test_architecture_boundary_contract.py:315-330
Summary
Athena's modular monolith architecture provides a structured environment where OSU server compatibility and domain logic are isolated from one another. By enforcing strict dependency directions, segregating commands from queries, and utilizing a centralized state infrastructure, the project maintains a Pythonic and scalable codebase capable of supporting diverse client families without incurring technical debt through mixed concerns.
Sources: README.md:20-35, AGENTS.md:120-140