mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-05-28 07:26:52 +00:00
8.3 KiB
8.3 KiB
05 — Architecture and Modules
Layer Overview
The application is structured as four distinct layers. Each layer has a defined responsibility, and dependency may only flow in permitted directions.
┌──────────────────────────────────────────────────┐
│ Entrypoints │
│ (API Endpoints / CLI / Agent / Scheduler / │
│ Webhook / Message Interaction) │
└────────────────────┬─────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ Chain Layer (app/chain/) │
│ Business orchestration: search, download, │
│ subscribe, transfer, message, recommend, etc. │
└──────┬──────────────┬───────────────┬────────────┘
│ │ │
▼ ▼ ▼
┌────────────┐ ┌──────────┐ ┌────────────────┐
│ Module │ │ Helper │ │ DB / Oper │
│ Layer │ │ Layer │ │ Layer │
│ (app/ │ │ (app/ │ │ (app/db/) │
│ modules/) │ │ helper/)│ │ │
└────────────┘ └──────────┘ └────────────────┘
Layer Responsibilities and Boundaries
Entrypoint Layer
Directories: app/api/endpoints/, moviepilot (CLI), app/agent/, scheduler callbacks, webhook handlers, message interactions.
Responsibilities:
- HTTP concerns: authentication, parameter parsing, response model serialization, streaming adaptation, simple input validation.
- Simple list, detail, toggle, settings read/write, and pure CRUD endpoints may call
app/db/or a helper directly. - Any logic that coordinates multiple modules, triggers events, touches caches, or combines workflows must be moved into
chain.
Rules:
- Prefer adding new endpoints to an existing domain file. Create a new endpoint file only when introducing a new top-level resource domain.
- After adding a new endpoint, register it in
app/api/apiv1.py. - Endpoints must not contain business logic that belongs in
chain.
Chain Layer
Directory: app/chain/
Responsibilities:
- Business orchestration shared by API, CLI, agent, scheduler, and other entrypoints.
- Composes module capabilities, helpers, database access, events, and caches.
- Focuses on use cases and workflows.
Rules:
- Call module capabilities via
run_module()orasync_run_module(). UseModuleManagerdirectly only when enumerating, inspecting, or running health checks. - Do not hold low-level protocol details, HTTP request objects, or page-specific parameter assembly.
- Before creating a new chain file, verify the workflow is genuinely reused across multiple entrypoints, or coordinates multiple modules. If it is short logic for a single endpoint, keep it in the endpoint.
- Chain-to-chain calls are allowed when reusing stable domain logic. Avoid introducing new circular dependencies.
Module Layer
Directory: app/modules/
Responsibilities:
- Pluggable capability implementations: downloaders, media servers, message channels, metadata sources, storage backends, subtitle backends, filter backends, etc.
- Manages lifecycle (init, stop), configuration switches, priority ordering, and independent testability.
Module categories (defined in app/schemas/types.py):
| Enum | Examples |
|---|---|
ModuleType.Downloader |
qBittorrent, Transmission, rTorrent |
ModuleType.MediaServer |
Emby, Jellyfin, Plex, TrimMedia, Zspace, Ugreen |
ModuleType.MessageChannel |
Telegram, WeChat, Feishu, Slack, Discord |
ModuleType.MetaData |
TMDB, TheTVDB, Douban, Bangumi, Fanart |
ModuleType.Indexer |
Site-specific torrent indexers |
ModuleType.Storage |
Alist, rclone, u115, local storage |
Rules:
- A module must focus on one backend or one capability. It returns domain result objects, not HTTP responses, and must not depend on FastAPI request objects or endpoint auth.
- Do not add direct
module → modulecoupling for new code. Cross-module orchestration must go throughchain. - Do not expand the historical
module → chainusage pattern. If a module needs shared business logic, move that logic intochainor down intohelper.
Helper Layer
Directory: app/helper/
Responsibilities:
- Reusable low-level support: path handling, config aggregation, site index loading, protocol wrappers, rate limiting, cache utilities, page parsing, notification helpers.
Rules:
- Add a new helper only when the logic is reused in multiple places, or it is clearly a standalone low-level concern.
- If logic is used only by a single chain or module, keep it in the original file. Do not turn
helperinto a dumping ground. - If the code needs configuration switches, runtime loading, priorities, or multi-implementation dispatch, it is a
module, not ahelper. helpermust not contain full business workflows.
DB / Oper Layer
Directory: app/db/
Responsibilities:
- SQLAlchemy models under
app/db/models/. - Data access wrappers (
*_oper.py) that encapsulate all database queries.
Rules:
- Never issue SQLAlchemy queries directly from chain, module, or endpoint code. Always use the corresponding
*_oper.pyclass. - Any schema change requires a new Alembic migration under
database/versions/.
Permitted Call Directions
| Direction | Status |
|---|---|
endpoint / CLI / agent / scheduler → chain |
✅ Preferred |
endpoint / CLI / agent / scheduler → db / helper |
✅ Allowed for simple CRUD and input normalization only |
chain → chain |
✅ Allowed when reusing stable, non-circular domain logic |
chain → module |
✅ Via run_module() / async_run_module() |
chain → helper |
✅ Allowed |
chain → db |
✅ Via *_oper.py classes |
module → chain |
⚠️ Exists in legacy code; do not expand in new code |
module → module |
❌ Forbidden in new code |
helper → chain |
❌ Forbidden |
helper → endpoint |
❌ Forbidden |
Key File Locations
| Path | Purpose |
|---|---|
app/api/apiv1.py |
API router registration — register new endpoints here |
app/core/config.py |
ConfigModel and Settings — all deployment/env-level config |
app/schemas/types.py |
SystemConfigKey, EventType, ModuleType, and all shared enums |
app/core/module.py |
ModuleManager — discovers and manages module instances |
app/core/plugin.py |
PluginManager — discovers and manages plugin instances |
app/core/event.py |
EventManager + Event — the application event bus |
app/core/context.py |
Context, MediaInfo, TorrentInfo — shared domain context objects |
app/main.py |
Application startup and FastAPI instance |
database/versions/ |
Alembic migration scripts |
Where New Capabilities Go
| Scenario | Action |
|---|---|
| New business workflow shared by multiple entrypoints | app/chain/ |
| New downloader, media server, message channel, or storage backend | app/modules/<backend>/ |
| New public HTTP API endpoint | app/api/endpoints/, register in app/api/apiv1.py |
| New low-level utility reused in multiple places | app/helper/ |
| New deployment/env/startup config (ports, paths, API keys) | ConfigModel in app/core/config.py |
| New runtime business config, user-editable rule, or persistent system option | SystemConfigKey + SystemConfigOper |
| Config change should reload a long-lived object | Add CONFIG_WATCH + on_config_changed() to the relevant class |
| Few dozen lines of private logic in one chain or module | Private function in the same file; do not create a new helper |
| New module category or subtype | Also update app/schemas/types.py |
Last Updated: 2026-05-25