Directory structure
Concept Core does not impose any directory structure. The location of all folders —
including src, config, storage, database —
can be customized in bootstrap/paths.php
and PSR-4 autoloading in composer.json.
Concept Skeleton ships a ready scaffold with
src/App/ layout (component-ready). Below is the recommended project root structure
and three common approaches to organizing code inside src.
These are guidelines, not a mandatory standard.
Root directories (public, bootstrap, config, etc.) —
recommended convention for getting started. They can be renamed or moved if you update
paths.php and web server settings.
Recommended root structure
Matches Concept Skeleton
and keys in bootstrap/paths.php:
project/
├── public/ # document root: index.php, assets, vendor/
│ ├── index.php
│ ├── assets/
│ └── vendor/
├── bootstrap/
│ ├── app.php
│ ├── paths.php
│ └── providers/ # app.php, console.php
├── bin/console.php
├── config/ # PHP config + config/{dev,production}/
├── routes/ # routes (loaded from config)
├── resources/
│ ├── lang/validator/ # PathName::VALIDATOR_TRANSLATIONS
│ └── views/
│ ├── errors/fallback/ # PathName::ERRORS_FALLBACK_VIEWS
│ └── frontend/
├── database/
│ ├── migrations/ # PathName::MIGRATIONS
│ └── seeders/ # PathName::SEEDERS
├── storage/
│ ├── logs/ # PathName::LOGS
│ └── cache/ # PathName::CACHE
├── src/ # PathName::SRC — application code
├── tests/
├── .env.example
└── composer.json
The path to each directory is set in
paths.php
via PathName constants (PathName::CONFIG, PathName::SRC,
PathName::LOGS, etc.). Core accesses them through
PathManager::get(PathName::…), not hard-coded strings.
For example, code can live in app/ instead of src/ — just change
the value for PathName::SRC and Composer namespaces.
Concept Skeleton default
The starter template uses a component-ready layout: shared code in
src/App/, global routes in routes/web.php, landing templates in
resources/views/frontend/. Components (src/Components/) are added as needed.
src/App/
├── Controllers/ # Concept\App\Controllers\* (IndexController)
└── Extensions/Twig/ # Concept\App\Extensions\Twig\*
routes/web.php # global routes + middleware stack
resources/views/frontend/ # @frontend/… — landing, shared pages
Composer (skeleton):
"autoload": {
"psr-4": {
"Concept\\": "src/",
"Database\\Seeders\\": "database/seeders/"
}
}
paths.php: full set of PathName keys
(details).
Providers are already wired in bootstrap/providers/ — see
Setup.
Organizing code in src
Besides the skeleton layout (src/App/), you can choose layered MVC or modular components.
All three variants are compatible with Concept Core.
1. Layered MVC (package by layer)
Classic layered architecture: code is grouped by type (controllers, models, requests), not by module. In the PHP community this is often called package by layer — the opposite of “package by feature”.
Layers sit directly in the code directory (e.g. src/), without intermediate folders
like App or Components:
src/
├── Controllers/ # Concept\Controllers\*
├── Models/ # Concept\Models\*
├── Requests/ # FormRequest, DTO requests
├── Validation/
│ └── Rules/
├── Extensions/
│ └── Twig/
├── Services/ # optional: business logic
└── Middlewares/ # optional: HTTP middleware
Namespace follows PSR-4: with "Concept\\": "src/", class
src/Controllers/HomeController.php has namespace
Concept\Controllers, not Concept\App\Controllers.
When to choose: small applications, single domain, few modules; team used to classic MVC (Symfony 2–4, early Laravel).
Drawbacks: as the project grows, it is hard to find “everything about orders” — files are scattered across layers.
Composer (example):
"autoload": {
"psr-4": {
"Concept\\": "src/"
}
}
paths.php: PathName::SRC => 'src'
(details).
2. Components (modular monolith)
Modular monolith with pluggable components: shared application core + isolated functional modules. This combines package by feature (inside each component) with MVC layers. Supported by the Concept Core component system.
src/
├── App/ # shared application code
│ ├── Controllers/ # global controllers
│ ├── Models/ # BaseModel, shared traits
│ ├── Requests/
│ ├── Validation/Rules/
│ ├── Extensions/Twig/
│ └── Services/
└── Components/ # functional modules
├── AuthAdmin/
│ ├── AuthAdminComponent.php
│ ├── routes.php
│ ├── Controllers/
│ ├── Models/
│ ├── Requests/
│ ├── Dto/
│ ├── Middlewares/
│ ├── Commands/
│ ├── Services/
│ ├── Enums/
│ ├── Extensions/
│ ├── Views/ # module Twig (@auth-admin/…)
│ ├── Assets/ # JS/CSS → public via CLI
│ └── Database/
│ ├── Migrations/
│ └── Seeders/
├── UserCabinet/
└── ProjectManager/
Each component implements ComponentInterface directly in its
{Name}Component.php class. Active modules are listed in
config/components.php or config/{env}/components.php.
Routes, views, migrations, seeders, CLI commands, assets, and service providers are declared
via interface methods (routes(), viewPaths(), migrationPaths(), etc.).
The App directory is for shared pieces (base models, Twig extensions, validation rules).
Step-by-step example — AuthAdmin.
When to choose: several subsystems (admin, cabinet, public site), need to enable or disable modules via config, different teams work on separate components.
Comparison
| Layered MVC | Components | Concept Skeleton | |
|---|---|---|---|
| Academic term | Layered / package by layer | Modular monolith, package by feature | Component-ready App/ |
| Project size | Small — medium | Medium — large | Start — any |
src organization |
Controllers/ at top level |
App/ + Components/ |
App/ (+ optional Components/) |
| Namespace (example) | Concept\Controllers\* |
Concept\App\*, Concept\Components\* |
Concept\App\* |
| Routes | routes/web.php |
+ routes.php in each component |
routes/web.php |
| Concept Core | Full support | + component subsystem | Ready scaffold + providers |
| Composer PSR-4 | Concept\\ → src/ |
Concept\\ → src/ |
+ Database\\Seeders\\ |
Views, lang, storage
Paths are configured in paths.php
via Concept\Core\Foundation\PathName constants.
Core accesses them through PathManager::get(PathName::…).
| What | PathName | Path in skeleton |
|---|---|---|
| Shared Twig templates | PathName::VIEWS |
resources/views/ (@frontend/, layouts) |
| Fallback error templates | PathName::ERRORS_FALLBACK_VIEWS |
resources/views/errors/fallback/ |
| Language files | PathName::LANG |
resources/lang/ |
| Validator translations | PathName::VALIDATOR_TRANSLATIONS |
resources/lang/validator/ |
| Module templates | ComponentInterface::viewPaths() |
src/Components/{Name}/Views/ → @auth-admin/… |
| Application migrations | PathName::MIGRATIONS |
database/migrations/ + paths in config/migrations.php |
| Application seeders | PathName::SEEDERS |
database/seeders/ (namespace Database\Seeders\) |
| Logs | PathName::LOGS |
storage/logs/ |
| Cache | PathName::CACHE |
storage/cache/ (Twig, Valinor, etc.) |
| Component assets | ComponentInterface::assets() |
component:publish-assets → public/components/… (php bin/console.php component:publish-assets) |
Full list of keys — in the PathName table.
Component migrations and seeders are registered via migrationPaths() and
seeders() on the component class, not via global
PathName::MIGRATIONS / PathName::SEEDERS.
What to choose
- Concept Skeleton — quick start: HTTP + Twig,
src/App/, ready providers; add components as needed. - Layered MVC — simple start without
App/, single domain, few modules. - Components — several subsystems, need module isolation and Concept core support.
Structure can be changed at any time: update
paths.php, composer.json, and config.
Next: request lifecycle and setup of entry points.