feat : mise à jour de la structure du projet
This commit is contained in:
255
CLAUDE.md
255
CLAUDE.md
@@ -1,69 +1,154 @@
|
||||
# Coltura
|
||||
|
||||
CRM/ERP. Monorepo Symfony 8 (API Platform 4) + Nuxt 4. **Architecture DDD (Domain-Driven Design).**
|
||||
CRM/ERP. Monorepo Symfony 8 (API Platform 4) + Nuxt 4. **Architecture Modular Monolith DDD.**
|
||||
|
||||
## Architecture DDD
|
||||
## Architecture Modulaire
|
||||
|
||||
Le projet suit une architecture DDD cote backend ET frontend. Le code est organise par **domaine metier** (Bounded Context), pas par type technique.
|
||||
Le projet suit une architecture **modular monolith** pilotee par le backend : chaque module metier est un bounded context autonome, activable/desactivable par tenant. Le module `Core` est obligatoire.
|
||||
|
||||
### Backend — Organisation par domaine
|
||||
**Principe fondamental : le backend est la source de verite unique.**
|
||||
- Le backend dicte quels modules sont actifs (`config/modules.php`).
|
||||
- Le backend dicte l'organisation de la sidebar (`config/sidebar.php`), decouplee des modules eux-memes.
|
||||
- Le frontend ne connait rien : il scanne automatiquement les modules comme layers Nuxt et demande la sidebar au backend.
|
||||
|
||||
### Backend — Organisation par module
|
||||
|
||||
```
|
||||
src/
|
||||
Domain/ # Couche domaine (logique metier pure, aucune dependance framework)
|
||||
{BoundedContext}/ # Ex: Customer, Sales, Catalog, Invoice...
|
||||
Entity/ # Entites et Aggregates du domaine
|
||||
ValueObject/ # Value Objects (Money, Address, Email...)
|
||||
Repository/ # Interfaces des repositories (ports)
|
||||
Service/ # Services domaine (logique metier)
|
||||
Event/ # Domain Events
|
||||
Exception/ # Exceptions metier
|
||||
Application/ # Couche application (cas d'usage, orchestration)
|
||||
{BoundedContext}/
|
||||
Command/ # Commands (write) + Handlers
|
||||
Query/ # Queries (read) + Handlers
|
||||
DTO/ # Data Transfer Objects
|
||||
Infrastructure/ # Couche infrastructure (implementations techniques)
|
||||
{BoundedContext}/
|
||||
Repository/ # Implementations Doctrine des repositories
|
||||
Persistence/ # Mapping Doctrine (si XML/YAML)
|
||||
Shared/ # Services techniques partages (mail, storage, etc.)
|
||||
Api/ # Couche API (exposition HTTP)
|
||||
{BoundedContext}/
|
||||
Resource/ # ApiResource API Platform
|
||||
State/ # Providers & Processors API Platform
|
||||
Kernel.php
|
||||
Shared/ # Noyau technique partage
|
||||
Domain/
|
||||
ValueObject/ # VO de base (Email...)
|
||||
Event/ # DomainEventInterface
|
||||
Contract/ # Interfaces inter-modules (UserResolverInterface, TenantAwareInterface)
|
||||
Application/
|
||||
Bus/ # CommandBusInterface, QueryBusInterface (interfaces seules)
|
||||
Infrastructure/
|
||||
ApiPlatform/
|
||||
Resource/ # AppVersion, ModulesResource, SidebarResource
|
||||
State/ # AppVersionProvider, ModulesProvider, SidebarProvider
|
||||
Module/
|
||||
Core/ # Module obligatoire (auth, users)
|
||||
CoreModule.php # Declaration (ID, LABEL, REQUIRED)
|
||||
Domain/
|
||||
Entity/ # Entites Doctrine + API Platform (User)
|
||||
Repository/ # Interfaces repositories (UserRepositoryInterface)
|
||||
Event/ # Domain events (UserCreated)
|
||||
Application/
|
||||
DTO/ # UserOutput
|
||||
Infrastructure/
|
||||
Doctrine/ # DoctrineUserRepository, Migrations/
|
||||
ApiPlatform/
|
||||
State/
|
||||
Provider/ # MeProvider
|
||||
Processor/ # UserPasswordHasherProcessor
|
||||
Console/ # CreateUserCommand
|
||||
DataFixtures/ # AppFixtures
|
||||
Commercial/ # Autre module (exemple)
|
||||
CommercialModule.php
|
||||
config/
|
||||
modules.php # Liste des modules actifs (source de verite activation)
|
||||
sidebar.php # Structure de la sidebar (source de verite navigation)
|
||||
version.yaml
|
||||
jwt/ # Cles JWT
|
||||
packages/ # Config Symfony
|
||||
migrations/ # Anciennes migrations Doctrine
|
||||
infra/dev/ # Docker dev
|
||||
infra/prod/ # Docker prod (multi-stage)
|
||||
```
|
||||
|
||||
**Regles DDD backend :**
|
||||
- Le domaine (`Domain/`) ne depend de RIEN (pas de Doctrine, pas de Symfony, pas d'API Platform)
|
||||
- Les repositories dans `Domain/` sont des **interfaces** ; les implementations Doctrine sont dans `Infrastructure/`
|
||||
- Les entites API Platform (`Api/Resource/`) sont decouples des entites domaine si necessaire
|
||||
- Chaque Bounded Context est autonome — pas d'import croise entre contextes (communiquer via events ou services application)
|
||||
- `User` et `Auth` restent dans `src/` (hors DDD) car c'est du framework pur (Security Bundle)
|
||||
|
||||
### Frontend — Organisation par domaine
|
||||
### Frontend — Organisation modulaire (auto-detectee)
|
||||
|
||||
```
|
||||
frontend/
|
||||
domains/ # Modules metier
|
||||
{bounded-context}/ # Ex: customer, sales, catalog, invoice...
|
||||
components/ # Composants Vue specifiques au domaine
|
||||
composables/ # Composables specifiques au domaine
|
||||
services/ # Services API du domaine
|
||||
dto/ # Types TypeScript du domaine
|
||||
pages/ # Pages du domaine (optionnel, ou dans pages/)
|
||||
stores/ # Store Pinia du domaine (si necessaire)
|
||||
components/ # Composants UI partages (non lies a un domaine)
|
||||
composables/ # Composables partages (useApi, useAppVersion)
|
||||
stores/ # Stores globaux (auth, ui)
|
||||
services/ # Services partages
|
||||
app/ # Shell applicatif
|
||||
layouts/ # default.vue, auth.vue
|
||||
middleware/ # auth.global.ts, modules.global.ts
|
||||
shared/ # Code partage (hors modules)
|
||||
composables/ # useApi, useAppVersion, useSidebar
|
||||
components/ui/ # AppTopNav, ...
|
||||
stores/ # auth, ui
|
||||
services/ # auth
|
||||
types/ # SidebarSection, SidebarItem, UserData
|
||||
utils/ # api (Hydra)
|
||||
modules/ # Modules auto-detectes comme layers Nuxt
|
||||
core/
|
||||
nuxt.config.ts # Marqueur layer (vide)
|
||||
pages/ # index.vue, login.vue
|
||||
commercial/
|
||||
nuxt.config.ts
|
||||
pages/ # commercial.vue
|
||||
app.vue # Composant racine
|
||||
nuxt.config.ts # Scanne modules/*/ automatiquement
|
||||
i18n/locales/ # Traductions (cles sidebar.*, etc.)
|
||||
assets/ # CSS, images
|
||||
public/ # Fichiers statiques
|
||||
```
|
||||
|
||||
**Regles DDD frontend :**
|
||||
- Chaque domaine est un dossier autonome dans `frontend/domains/`
|
||||
- Un domaine ne doit pas importer depuis un autre domaine — utiliser les composables/stores partages
|
||||
- Les composants, services et types partages restent a la racine (`components/`, `composables/`, etc.)
|
||||
- Les pages peuvent etre dans `frontend/pages/` (routing Nuxt) et importer les composants du domaine
|
||||
### Endpoints API cles
|
||||
|
||||
- `GET /api/version` (public) — version de l'app
|
||||
- `GET /api/modules` (public) — liste des IDs de modules actifs
|
||||
- `GET /api/sidebar` (public) — sections de la sidebar + `disabledRoutes`
|
||||
- Filtre automatiquement les items dont le `module` owner n'est pas actif
|
||||
- Les sections vides apres filtrage sont supprimees
|
||||
- `disabledRoutes` = `to` des items filtres (utilise par le middleware front)
|
||||
- `GET /api/me` (auth) — user courant
|
||||
|
||||
### Flux d'activation/desactivation d'un module
|
||||
|
||||
Pour activer/desactiver un module, tu touches **uniquement** `config/modules.php` :
|
||||
|
||||
```php
|
||||
return [
|
||||
\App\Module\Core\CoreModule::class,
|
||||
// \App\Module\Commercial\CommercialModule::class, // commente = desactive
|
||||
];
|
||||
```
|
||||
|
||||
Cascade automatique :
|
||||
1. `GET /api/modules` ne retourne plus `commercial`
|
||||
2. `GET /api/sidebar` filtre les items `module: 'commercial'` → section "Commercial" disparait, ses routes passent dans `disabledRoutes`
|
||||
3. Frontend : sidebar se met a jour, middleware `modules.global.ts` redirige toute navigation vers `/commercial` ou `/commercial/*`
|
||||
4. Le code du module reste dans le bundle Nuxt (layer auto-detecte) → reactivation instantanee sans rebuild
|
||||
|
||||
### Reorganiser la sidebar sans toucher aux modules
|
||||
|
||||
Pour deplacer un item (ex: "Commandes fournisseurs") d'une section a une autre, tu edites juste `config/sidebar.php` :
|
||||
|
||||
```php
|
||||
// Avant : sous Commercial
|
||||
['label' => 'sidebar.commercial.suppliers', 'to' => '/commercial/suppliers', 'module' => 'commercial'],
|
||||
|
||||
// Apres : sous Production (l'item reste "owned" par Commercial, seule sa place change)
|
||||
[
|
||||
'label' => 'sidebar.production.section',
|
||||
'items' => [
|
||||
['label' => 'sidebar.commercial.suppliers', 'to' => '/commercial/suppliers', 'module' => 'commercial'],
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
Le code du module Commercial n'est pas touche.
|
||||
|
||||
### Regles d'architecture
|
||||
|
||||
**Backend :**
|
||||
- Le domaine (`Domain/`) peut garder les attributs ORM (approche pragmatique) mais les repositories sont des interfaces
|
||||
- Communication inter-modules par `Shared/Domain/Contract/` ou domain events — jamais d'import direct entre modules
|
||||
- Chaque module declare un `*Module.php` avec `ID`, `LABEL`, `REQUIRED`
|
||||
- `config/modules.php` = seule source de verite pour l'activation
|
||||
- `config/sidebar.php` = seule source de verite pour l'organisation de la sidebar (chaque item reference son module owner via la cle `module`)
|
||||
- Migrations par module dans `src/Module/{Module}/Infrastructure/Doctrine/Migrations/`
|
||||
|
||||
**Frontend :**
|
||||
- Chaque module est un layer Nuxt auto-detecte (`modules/*/nuxt.config.ts` minimal)
|
||||
- Un module front ne doit pas importer depuis un autre module — utiliser `shared/`
|
||||
- `useSidebar()` fetch `/api/sidebar` et expose `sections`, `disabledRoutes`, `isRouteDisabled()`
|
||||
- Le layout `default.vue` itere sur les sections retournees par l'API, applique `t()` sur les labels
|
||||
- Middleware `auth.global.ts` charge la sidebar apres authentification
|
||||
- Middleware `modules.global.ts` redirige si la route demandee est dans `disabledRoutes`
|
||||
- **Interdit** : `.module.ts`, `modules-loader.ts`, hardcode de la sidebar, edition manuelle de `extends` dans `nuxt.config.ts`
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -72,42 +157,6 @@ frontend/
|
||||
- **Auth** : JWT HTTP-only cookie (lexik/jwt-authentication-bundle), login a `/login_check`, cookie `BEARER`
|
||||
- **Docker** : PHP-FPM + Node 24, Nginx (port 8083), PostgreSQL (port 5436)
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
src/
|
||||
Domain/{Context}/Entity/ # Entites domaine
|
||||
Domain/{Context}/ValueObject/ # Value Objects
|
||||
Domain/{Context}/Repository/ # Interfaces repositories
|
||||
Domain/{Context}/Service/ # Services domaine
|
||||
Domain/{Context}/Event/ # Domain Events
|
||||
Application/{Context}/Command/ # Commands + Handlers
|
||||
Application/{Context}/Query/ # Queries + Handlers
|
||||
Application/{Context}/DTO/ # Data Transfer Objects
|
||||
Infrastructure/{Context}/Repository/ # Implementations Doctrine
|
||||
Api/{Context}/Resource/ # ApiResource API Platform
|
||||
Api/{Context}/State/ # Providers & Processors
|
||||
Entity/ # Entites framework (User)
|
||||
DataFixtures/ # Fixtures
|
||||
config/ # Config Symfony
|
||||
config/jwt/ # Cles JWT
|
||||
migrations/ # Migrations Doctrine
|
||||
infra/dev/ # Docker dev
|
||||
infra/prod/ # Docker prod (multi-stage)
|
||||
frontend/
|
||||
domains/{context}/components/ # Composants du domaine
|
||||
domains/{context}/composables/ # Composables du domaine
|
||||
domains/{context}/services/ # Services API du domaine
|
||||
domains/{context}/dto/ # Types TS du domaine
|
||||
domains/{context}/stores/ # Store Pinia du domaine
|
||||
components/ # Composants UI partages
|
||||
composables/ # Composables partages (useApi, useAppVersion)
|
||||
stores/ # Stores globaux (auth, ui)
|
||||
pages/ # Pages (routing Nuxt)
|
||||
layouts/ # Layouts
|
||||
i18n/locales/ # Traductions
|
||||
```
|
||||
|
||||
## Commandes
|
||||
|
||||
```bash
|
||||
@@ -128,6 +177,12 @@ make php-cs-fixer-allow-risky # Fix code style PHP
|
||||
make logs-dev # Tail logs Symfony
|
||||
```
|
||||
|
||||
Si `make cache-clear` echoue pour cause de permissions sur `var/` :
|
||||
```bash
|
||||
docker exec -t -u root php-coltura-fpm chown -R www-data:www-data /var/www/html/var
|
||||
docker exec -t -u www-data php-coltura-fpm php bin/console cache:clear
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
### Commits
|
||||
@@ -145,10 +200,28 @@ Exemples : `feat : add login page`, `fix(auth) : prevent null token crash`
|
||||
- Faire un commit separe de bump : `chore : bump version to v<X.Y.Z>`
|
||||
- Puis creer le tag et pusher : `git tag v<X.Y.Z> && git push origin develop --tags`
|
||||
|
||||
### Nommage
|
||||
|
||||
| Element | Convention | Exemple |
|
||||
|---------|-----------|---------|
|
||||
| Module back | PascalCase | `Module/Commercial/` |
|
||||
| Module front | kebab-case | `modules/commercial/` |
|
||||
| Module ID | snake_case | `commercial`, `gestion_rh` |
|
||||
| Entity | PascalCase singulier | `User.php` |
|
||||
| Repository interface | `*RepositoryInterface` | `UserRepositoryInterface.php` |
|
||||
| Repository impl | `Doctrine*Repository` | `DoctrineUserRepository.php` |
|
||||
| DTO | `*Output` / `*Input` | `UserOutput.php` |
|
||||
| API Resource | classe dans `Infrastructure/ApiPlatform/Resource/` | `UserResource.php` |
|
||||
| Provider | `*Provider` | `MeProvider.php` |
|
||||
| Processor | `*Processor` | `UserPasswordHasherProcessor.php` |
|
||||
| Module declaration back | `*Module.php` | `CommercialModule.php` |
|
||||
| Composable front | `use*` | `useSidebar.ts` |
|
||||
| Cles i18n sidebar | `sidebar.<module>.*` | `sidebar.commercial.overview` |
|
||||
|
||||
### Backend
|
||||
|
||||
- Toujours `declare(strict_types=1)` en haut des fichiers PHP
|
||||
- API Platform : utiliser ApiResource, Providers (`src/State/`), Processors — pas de controllers
|
||||
- API Platform : utiliser ApiResource, Providers, Processors — pas de controllers
|
||||
- Routes API prefixees `/api` (via `config/routes/api_platform.yaml`)
|
||||
- Le login (`/login_check`) est hors prefix `/api`, nginx reecrit `REQUEST_URI` vers `/login_check`
|
||||
- PHP CS Fixer : regles Symfony + PSR-12 + strict types
|
||||
@@ -163,9 +236,11 @@ Exemples : `feat : add login page`, `fix(auth) : prevent null token crash`
|
||||
- TypeScript strict
|
||||
- Composable `useApi()` pour tous les appels API (gere cookies, erreurs, toasts, i18n)
|
||||
- Stores Pinia : `useAuthStore` (auth), `useUiStore` (ui)
|
||||
- Middleware global `auth.global.ts` protege les routes
|
||||
- Traductions dans `frontend/i18n/locales/`
|
||||
- Middleware global `auth.global.ts` protege les routes + charge la sidebar apres login
|
||||
- Middleware global `modules.global.ts` redirige les routes des modules desactives
|
||||
- Traductions dans `frontend/i18n/locales/` avec le namespace `sidebar.*` pour la nav
|
||||
- 4 espaces d'indentation
|
||||
- Les labels de sidebar sont des cles i18n, jamais du texte brut (le layout applique `t()` dessus)
|
||||
|
||||
### Nginx
|
||||
|
||||
|
||||
Reference in New Issue
Block a user