Skip to content

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

  1. Modules must not modify core files.
  2. No eval, monkey patching, or runtime file replacement.
  3. Any module can be disabled via config/modules.php without touching core.
  4. Protected services (db, settings, config, auth) cannot be decorated.
  5. Keep EN and RU docs in sync in the same commit.