10 KiB
Architecture
Analysis Date: 2026-02-04
Pattern Overview
Overall: Modular Layer-based MVC with Plugin System
WinterCMS implements a three-layer modular architecture consisting of Core Modules (System, Backend, CMS) that form the framework foundation, with a Plugin system overlaid for extensibility. The framework uses Laravel 9.x as its base, with Winter's Storm library acting as a compatibility buffer.
Key Characteristics:
- Laravel-based HTTP kernel with module-level routing
- CMS-first approach: frontend pages route through CMS module, backend admin routes through Backend module
- Plugin dependency resolution with automatic component/controller/model discovery
- Extensibility through plugin interdependencies and event-driven architecture
- YAML/JSON configuration for models, forms, lists, and behaviors
- Static theme system with file-based page, layout, and partial management
Layers
Core Modules (modules/ directory):
- System Module: Base framework, plugin management, mail system, asset handling, error management
- Backend Module: Admin interface, form/list widgets, user role management, controller behaviors
- CMS Module: Frontend page rendering, theme management, component system, layout composition
Plugin Layer (plugins/ directory):
- Vendor-namespaced plugins (winter/, golem15/)
- Each plugin extends and integrates with core modules
- Plugin dependencies declared via
$requirearrays in Plugin.php - Core plugins: Apparatus (DI/scenarios), User (auth), PaymentGateway (commerce)
- Domain plugins: Blog, Menu, Translate, AI, Chat, etc.
Frontend/Theme Layer (themes/ directory):
- Static theme files organized in theme directories
- Pages, layouts, partials stored as .htm files with Twig syntax
- Assets (CSS, JS, images) co-located with pages/layouts
- URL routing to pages via Theme manager and CMS controller
Data Flow
Frontend Request (Page View):
- HTTP request to any URL arrives at
index.php - Bootstrap loads application (
bootstrap/app.php) - HTTP Kernel processes request through middleware
- Laravel Router evaluates all registered routes
- Backend/System routes handled by their controllers
- CMS module catch-all route
{slug?}routes toCms\Classes\CmsController@run() - CMS Controller delegates to
Cms\Classes\Controller(primary frontend controller) - Primary Controller:
- Looks up URL in Theme using
Cms\Classes\Router - Resolves Page, Layout, and Component objects from theme files
- Renders page through Twig template engine
- Components injected into page output via ComponentManager
- Looks up URL in Theme using
- Response sent back to client
Backend Request (Admin Interface):
- HTTP request to
/backend/...arrives - Backend Router matches
Backend\Classes\BackendController@run() - BackendController parses remaining URL segments to determine target plugin/controller
- Controller loaded from plugin (e.g.,
Golem15\Blog\Controllers\Posts) - Controller action determined by remaining URL parts
- Behaviors attached (FormController, ListController, etc.) handle CRUD operations
- Views rendered with form/list widgets
- JSON/HTML response returned
Component Lifecycle:
- Page YAML/Twig specifies component code (
{% component 'componentName' %}) - ComponentManager resolves code to fully-qualified class name via codeMap
- Component class instantiated with properties from page markup
- Component's
onRun()method called by page renderer - Component renders its partial template (HTM file) in page context
- AJAX handlers on component (e.g.,
onUpdateCart) handled by Snowboard framework
State Management:
- Models: Database-backed via Winter\Storm\Database\Model (extends Laravel Eloquent)
- Sessions: Laravel session middleware, stored in
storage/framework/sessions/ - Cache: Laravel cache (configurable drivers: file, redis, memcached)
- Parameters: System\Models\Parameter table for application-level configuration
- Translation: Text keys cached in parameters, loaded by Translate plugin
Key Abstractions
Plugin Architecture:
Purpose: Enables modular, independently-deployable features with dependency resolution
- Each plugin is a Laravel ServiceProvider extending
System\Classes\PluginBase - Plugin.php declares:
pluginDetails(),register(),boot(),registerComponents(),registerControllers(), etc. - Examples:
golem15-wintercms-starter/plugins/golem15/blog/Plugin.php,golem15-wintercms-starter/plugins/golem15/user/Plugin.php - Pattern: Plugins register services, components, controllers, and extend existing models
Component System:
Purpose: Reusable, composable page elements with their own controllers and views
- Components extend
Cms\Classes\ComponentBase - Register in plugin via
registerComponents()returning [ClassName => 'code'] - ComponentManager loads all registered components into codeMap
- Example:
golem15-wintercms-starter/modules/cms/components/SoftComponent.php - Pattern: Component renders partial, handles AJAX, injects CSS/JS
Theme/Page System:
Purpose: File-based, non-database-backed content management
- Theme directory contains pages/, layouts/, partials/ subdirectories
- Page class
Cms\Classes\PageextendsCmsCompoundObject- represents page file as object - Page file format: URL metadata → YAML → Twig markup
- Page references layout (inherited structure) and components (functional blocks)
- Example page structure:
themes/demo/pages/home.htm - Pattern: Page objects loaded on-demand from disk, cached in memory
Backend Form/List System:
Purpose: YAML-driven admin CRUD interfaces
- Controllers use FormController and ListController behaviors
- forms.yaml/fields.yaml define form structure and validation
- lists.yaml/columns.yaml define table columns and filtering
- FormController handles create/read/update/delete operations
- Example files:
plugins/*/models/*/fields.yaml,plugins/*/models/*/columns.yaml - Pattern: Behaviors parse YAML and generate HTML forms/tables dynamically
Service Providers & Dependency Injection:
Purpose: Central registration of services and extensibility hooks
- Plugins register services in
register()andboot()methods - Services bound to Laravel's DI container via
$this->app->singleton() - Example: Apparatus framework registers DI container for component auto-wiring
- Pattern: Late binding allows plugin A to extend plugin B's services
Entry Points
Frontend (CMS Page Serving):
- Location:
golem15-wintercms-starter/modules/cms/classes/CmsController.php(HTTP entry),golem15-wintercms-starter/modules/cms/classes/Controller.php(business logic) - Triggers: Any URL not matching backend or other routes
- Responsibilities:
- Routes URL to page via Theme manager
- Loads Page, Layout, and Components
- Renders through Twig with component injection
- Returns HTML response
Backend (Admin Interface):
- Location:
golem15-wintercms-starter/modules/backend/classes/BackendController.php - Triggers: URLs prefixed with
/backend/ - Responsibilities:
- Parses plugin.controller.action URL pattern
- Instantiates controller from plugin
- Applies middleware and behaviors
- Routes to controller action
- Renders admin interface
System Initialization:
- Location:
golem15-wintercms-starter/bootstrap/app.php,golem15-wintercms-starter/index.php - Triggers: Every HTTP request
- Responsibilities:
- Creates Laravel application instance
- Binds kernel, console kernel, exception handler
- Loads plugin manager and auto-discovers plugins
- Registers module routes
CLI Commands:
- Location:
golem15-wintercms-starter/artisan(entry point),golem15-wintercms-starter/modules/*/console/(command classes) - Triggers:
php artisan <command> - Responsibilities:
- Plugin scaffold commands
- Database migrations
- Asset management
- Cache management
- Payment gateway operations (via golem15/paymentgateway)
Error Handling
Strategy: Exception-driven with error page rendering
Patterns:
- All exceptions thrown as
SystemException,ApplicationException, or Framework exceptions - HTTP exceptions converted to error pages by Exception Handler (
modules/system/classes/ErrorHandler.php) - In production: Shows generic error page, logs details
- In debug mode: Shows full stack trace and context
- Model validation: Throws ApplicationException with validation messages
- Frontend: 404 errors trigger Page not found response
- Backend: 404 errors redirect to backend dashboard, show toast notifications
Cross-Cutting Concerns
Logging:
- Uses Laravel's log facade (
Log::info(),Log::error()) - Stored in
storage/logs/ - Configured in
config/app.phpand environment variables - EventLog model (
modules/system/models/EventLog.php) tracks application events - RequestLog model tracks HTTP requests for debugging
Validation:
- Model-level via
$rulesproperty (Laravel validation rules) - Form widget validation via fields.yaml
validationproperty - Backend behaviors apply validation before save
- Errors returned as JSON validation responses in AJAX handlers
Authentication:
- Winter Backend: Database-backed user with role-based access control
- Frontend API: JWT tokens (via golem15/user plugin)
- Middleware:
Backend\Middleware\AuthenticateBackend,User\Middleware\JwtAuth - Guards:
web(session-based),api(JWT)
Extensibility Mechanisms
Model Extension:
- Plugins extend models dynamically using
Model::extend(Closure) - Example:
User::extend(function($model) { $model->hasMany['payments'] = ...; }) - Used for adding relationships, attributes, query scopes without modifying core
Event System:
- Global events via
Event::listen()andEvent::fire() - Module-level events:
system.route,cms.beforeRoute,cms.route,backend.beforeRoute - Model events:
model.saveInternal,model.afterCreate,model.afterUpdate,model.afterDelete - Custom events fired by plugins for extensibility
Behaviors:
- Controllers implement behaviors via
$implementarray - Backend behaviors: FormController, ListController, RelationController, ImportExportController
- Each behavior adds methods (e.g.,
create,update,index) to controller - Example:
modules/backend/behaviors/FormController.php
Architecture analysis: 2026-02-04