Architecture Overview
Thelia 3 runs on Symfony and adds the pieces an e-commerce application needs on top of it.
Core components
Technology stack
| Layer | Technology | Purpose |
|---|---|---|
| Language | PHP 8.3 or 8.4 | composer.json requires >= 8.3, and the CI matrix runs both |
| Framework | Symfony 7.4 LTS | HTTP handling, DI, routing, security |
| ORM | Propel ORM (not Doctrine) | Database abstraction and queries |
| API | API Platform 4.3 (standalone) | RESTful API generation (api-platform/symfony) |
| Front-Office | Twig + Symfony UX (Flexy bundle) | Reactive UI components |
| Back-Office | Twig (default-twig bundle) | Admin interface templating |
Propel is not Doctrine: there is no EntityManager and no flush(). Every ->save() persists immediately. Respect the strict native Propel types (string for decimal columns, int for tinyint columns).
The Twig back-office (default-twig bundle) is the reference admin theme. The legacy Smarty default back-office still ships, but it is no longer recommended and is expected to be dropped in a later release. Build new admin features on the default-twig bundle.
Directory structure
thelia/
├── core/
│ └── lib/Thelia/
│ ├── Api/ # API Platform integration
│ │ ├── Resource/ # API resources
│ │ ├── Bridge/Propel/ # Propel state providers
│ │ └── Service/DataAccess/ # DataAccessService
│ ├── Domain/ # Business logic facades (17 folders)
│ │ ├── Cart/ # e.g. Cart, Customer, Order, Checkout,
│ │ ├── Customer/ # Catalog, Promotion, Shipping,
│ │ ├── Order/ # Taxation, Media, CMS, Addressing,
│ │ └── Checkout/ # Admin, Localization, Marketing,
│ │ # Module, DataTransfer, Shared
│ ├── Core/ # Kernel, security, forms
│ └── Model/ # Propel models
├── templates/
│ ├── frontOffice/flexy/ # Front-office Twig theme (FlexyBundle)
│ ├── backOffice/default-twig/ # Back-office Twig theme (reference)
│ └── backOffice/default/ # Legacy Smarty back-office (deprecated)
├── vendor/thelia/
│ └── modules/ # Official modules
└── local/modules/ # Custom modules
The front-office theme is a Symfony bundle: templates/frontOffice/flexy/ exposes namespace FlexyBundle (class src/FlexyBundle.php). The back-office reference theme is the bundle in templates/backOffice/default-twig/ (namespace BackOfficeDefaultTwigBundle, class src/BackOfficeDefaultTwigBundle.php). Both ship their own controllers, Twig/Live components, Stimulus controllers, form themes and assets inside the bundle.
Architectural patterns
API-first design
All data access in Thelia 3 goes through the API layer:
┌─── ──────────────┐ ┌─────────────────┐
│ Front-Office │ │ External │
│ (Templates) │ │ Clients │
└────────┬────────┘ └────────┬────────┘
│ │
│ DataAccessService │ HTTP
│ (internal PHP) │ (JSON)
│ │
▼ ▼
┌─────────────────────────────────────────┐
│ API Platform │
│ /api/admin/ /api/front/ │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Propel ORM │
└─────────────────────────────────────────┘
This gives you:
- Single source of truth for data validation
- Consistent serialization across all consumers
- Caching at the API level
- A direct path for integrating external systems
Domain layer (facades)
Business logic lives in facades that orchestrate services:
// CartFacade orchestrates cart operations
$cartFacade->addItem($dto); // Validates, applies rules, persists
$cartFacade->getCartFromSession(); // Retrieves current cart
See Facades for detailed documentation.
Twig everywhere
Both the front-office and the back-office reference themes are Twig bundles:
| Front-Office (Flexy bundle) | Back-Office (default-twig bundle) |
|---|---|
| Twig + LiveComponents | Twig + LiveComponents |
DataAccessService (resources()) | Repositories / Services |
| Stimulus controllers | Stimulus controllers |
| AssetMapper + Tailwind CSS | AssetMapper + Sass + Bootstrap 5 |
The legacy Smarty default back-office is still shipped for backward compatibility, but it is deprecated. New back-office work should target the default-twig bundle.
See Dual Templating for details.
Module system
Modules extend Thelia. A modern Thelia 3 module is almost free of XML: routes are #[Route] PHP 8 attributes auto-scanned by ModuleAttributeLoader, services are declared in configureServices() with autowire() + autoconfigure(), and hooks and loops are auto-discovered from their base classes. The only XML the core still requires is Config/module.xml (metadata, XSD module-2_2.xsd), plus Config/schema.xml when the module creates its own database tables.
local/modules/MyModule/
├── Config/
│ ├── module.xml # REQUIRED - metadata (XSD module-2_2.xsd)
│ ├── schema.xml # REQUIRED only if the module has DB tables
│ ├── TheliaMain.sql # Generated SQL (applied by `module:schema:apply`)
│ └── config.xml # OPTIONAL - exports/imports/parameters/loop aliases only
├── Controller/ # #[Route] PHP 8 attributes (no routing.xml)
├── Api/
│ ├── Resource/ # API resources (auto-discovered)
│ └── Addon/ # Resource enrichments (ResourceAddonInterface)
├── LiveComponent/ # Front-office components (#[AsLiveComponent])
├── Hook/ # Back-office hooks (extends BaseHook, auto-tagged)