Architecture Overview¶
Project type¶
Structured PHP monolith with a modular extension layer.
- No DDD, no Hexagonal, no Clean Architecture — intentional.
- Split by context with minimal abstractions:
Controller → Service → Repository. - Two build artifacts from one codebase: MAIN (full panel) and LB (load balancer subset).
Source tree¶
| Path | Role |
|---|---|
src/Core/ |
Framework primitives: DI container, events, HTTP/router, config, auth, logging |
src/Domain/ |
Business contexts: Stream, VOD, Line, User, Server, Security, etc. |
src/Infrastructure/ |
External adapters: DatabaseFactory, cache readers, Redis, TMDb |
src/Streaming/ |
Streaming subsystem: bootstrap, auth, delivery, balancer, protection |
src/Modules/ |
Optional extension layer — loaded by ModuleLoader |
src/Public/ |
Front controller, router, controllers, views, assets |
src/Cli/ |
Console commands and cron entry points |
src/Ministra/ |
Stalker Portal — in core; served at /home/xc_vm/Ministra |
Runtime model¶
Dependencies flow inward — modules may use core and domain, never the reverse.
Public/index.php
└── XC_Bootstrap::boot(BootContext::Admin)
└── ServiceContainer (DI)
├── EventDispatcher (PSR-14)
├── ModuleLoader → loadAll() → bootAll()
└── Router → dispatch()
Domain and module classes do not take $db in their constructor. They
use \XcVm\Infrastructure\Database\DatabaseAware and call self::db(), which lazily
resolves the shared connection. bootstrap.php::wireDomainDatabase() sets that connection
once per boot (via DatabaseAware::setDb()) — there is no per-class wiring and no
global $db in the web request path.
Module system¶
Modules are isolated directories under src/Modules/ with a module.json manifest
and a class extending BaseModule. See Module Authoring for the full reference (and the linked Lifecycle / Extension Points pages).
src/Modules/my-module/
├── module.json # metadata
├── MyModuleModule.php # extends BaseModule, namespace XcVm\Module\MyModule
└── ...
Bootstrap contexts¶
Four contexts control which subsystems initialize. See Bootstrap Contexts.
| Context | Used for |
|---|---|
BootContext::Minimal |
Scripts needing only paths/config |
BootContext::Cli |
Cron jobs and CLI commands |
BootContext::Stream |
Streaming endpoints |
BootContext::Admin |
Admin/reseller panel |
Build variants (MAIN vs LB)¶
| MAIN | LB | |
|---|---|---|
| Admin panel | ✅ | ❌ |
| Streaming | ✅ | ✅ |
| Module system | ✅ | subset |
Controlled by the ServerEnvironment enum and each module.json's environment field
(main / lb / any). At boot, ModuleLoader::getCurrentEnvironment() resolves the node's
environment from the SERVER_TYPE constant ('lb' → ServerEnvironment::LoadBalancer, else
ServerEnvironment::Main); a module whose environment doesn't match the node is skipped, so
the LB gets a subset of modules.
Key extension points¶
| Mechanism | How to use |
|---|---|
| PSR-14 events | EventDispatcher::listen() / #[ListensTo] — see Event System |
| Service decoration | $container->decorate('id', callable, priority) — see Module Extension Points |
| Stream middleware | Implement StreamMiddlewareProviderInterface — see Module Extension Points |
| Cron entries | getCronEntries() in the module class — see Module Extension Points |
| DB migrations | MigratableInterface::getMigrations() — see Module Extension Points |
Contributor rules¶
- Modules must not modify core files.
- No
eval, monkey patching, or runtime file replacement. - Any module can be disabled via
config/modules.phpwithout touching core. - Protected services (
db,settings,config,auth) cannot be decorated. - Keep EN and RU docs in sync in the same commit.