Component concept

A component in Concept Core is an isolated functional application module (modular monolith): its own routes, templates, migrations, CLI commands, and optionally DI providers. Core assembles active components through ComponentRegistry and ComponentsServiceProvider.

See also: directory structure, component providers, creating a component (AuthAdmin).

Why components

  • Package by feature — admin, cabinet, catalog code lives together, not spread across App/Controllers layers.
  • Enable/disable — a module is enabled with one line in config/components.php.
  • Team boundaries — changes in AuthAdmin/ rarely touch UserCabinet/.
  • Shared core — HTTP, validation, Twig, DB remain in Concept Core and App/.

Where to put what

src/
├── App/                          # shared application code
│   ├── Controllers/              # global pages (home, about)
│   ├── Models/                   # BaseModel, shared entities
│   └── Extensions/Twig/          # global Twig extensions
└── Components/
    └── AuthAdmin/                # example module
        ├── AuthAdminComponent.php
        ├── routes.php            # HTTP routes
        ├── Controllers/
        ├── Middlewares/
        ├── Requests/  Dto/  Models/
        ├── Services/
        ├── Views/                # module templates (@auth-admin/...)
        ├── Database/
        │   ├── Migrations/
        │   └── Seeders/
        ├── Commands/             # console commands
        ├── Extensions/           # Twig for module
        └── Assets/               # JS/CSS → public via CLI

config/
└── components.php                # which modules are active

resources/views/                # shared layouts (@dashboard, @frontend)
routes/web.php                  # global routes + middleware stack
WhatComponentApplication (App/)
Feature routesComponent/routes.phproutes/web.php
Module templatesComponent/Views/resources/views/
Module migrationsComponent/Database/Migrations/database/migrations/
Base model, utilitiesApp/Models/
Shared admin layoutextends @dashboard/...resources/views/dashboard/

ComponentInterface contract

The component class implements Concept\Core\Services\Component\Contracts\ComponentInterface directly — metadata and registration data are returned from its methods:

MethodPurpose
name(), version(), description()Metadata for component:list
routes()Absolute path to routes.php or null
providers()Additional module service providers
viewPaths()Twig namespace → path (auth-admin/src/Components/.../Views)
viewContexts()URI prefix → context (/admindashboard)
viewExtensions()Twig\Extension\ExtensionInterface classes
commands()Symfony Console commands
seeders()Seeder classes
migrationPaths()Paths to migration directories
assets()Map of source → public for component:publish-assets

Use PathManager in the constructor to build paths relative to the application root (see AuthAdminComponent).

Application registration

// config/components.php
return [
    'components' => [
        AuthAdminComponent::class => AuthAdminComponent::class,
        UserCabinetComponent::class => UserCabinetComponent::class,
    ],
];

Key and value are the component class FQCN (container creates instance via DI). In config/dev/components.php you can add dev-only modules (e.g. DebugBar).

The class list goes into ComponentRegistry when ComponentsServiceProvider is registered (last in bootstrap/providers/app.php).

What happens on boot

ComponentsServiceProvider::boot() for each active component:

  1. Records telemetry FRAMEWORK_COMPONENT_REGISTERED (Telemetry)
  2. Registers console commands, migration paths, and seeders
  3. Registers nested service providers from providers()
  4. requires routes.php files ($router variable available)
  5. In HTTP (not CLI) — adds Twig paths, extensions, and view contexts

Order: core providers → ComponentsServiceProvider → module routes and views.

Moving existing code into a component

Typical refactor from a “flat” App/:

  1. Create src/Components/MyFeature/ with subdirectories following AuthAdmin.
  2. Move controllers, models, FormRequest, views, middleware into the component directory; update namespace to Concept\Components\MyFeature\....
  3. Extract feature routes from routes/web.php into MyFeature/routes.php.
  4. Add MyFeatureComponent.php implementing ComponentInterface.
  5. Register the class in config/components.php.
  6. Leave only shared code in App/: BaseModel, global layouts, utilities.
  7. Run php bin/console.php db:migrate and component:publish-assets as needed.

If a module uses a shared layout (@dashboard), templates in resources/views/dashboard/ are not moved — the component only extends them in Twig.

Reference components

The demo application ships several ready-made modules (code in src/Components/):

ComponentPurposeRegistration
AuthAdmin Admin login, dashboard, user CRUD config/components.php
Acl Role-based access control, route interceptor, admin UI config/components.php + routes.interceptors
SettingsManager Typed application settings, admin CRUD, Twig helpers config/components.php
DebugBar PHP Debug Bar for HTML responses (dev only) config/dev/components.php

Acl also requires AclRouteAuthorization in config/routes.php and HandleAccessDeniedMiddleware as the first global middleware — see Route interceptors.

Component vs App code

Not everything must go into components. Keep in App/:

  • landing, static pages, shared API gateway;
  • base model classes, interfaces, cross-module services;
  • global Twig namespaces (frontend, dashboard) in config/view.php.

A separate component — when there is a clear boundary: “admin”, “user cabinet”, “project catalog”.

One component — one responsibility. If two modules import each other directly, move shared code to App/ or a separate application “core” package.