# Architecture Modulaire Monolith — MALIO

## Contexte

Projet monolith Symfony API Platform (back) + Nuxt (front dans un dossier `frontend/`).
L'objectif est une architecture **modular monolith DDD** permettant de vendre des modules indépendamment à chaque client (ex : un client achète GestionRH + Formation, un autre GestionRH + Paie + Pointage).

---

## Principes fondamentaux

1. **Chaque module est un bounded context autonome** — son propre Domain, Application, Infrastructure.
2. **Communication inter-modules uniquement par events ou contrats** — jamais d'import direct d'une entité d'un autre module.
3. **Les modules sont activables/désactivables** par tenant sans casser le reste.
4. **Le module `Core` est obligatoire** — il gère users, tenants, auth.
5. **Le dossier `Shared/`** contient le noyau technique commun (interfaces, value objects de base, bus).
6. **API Platform** : les `#[ApiResource]` sont sur des classes Resource dédiées dans `Infrastructure/ApiPlatform/Resource/`, jamais directement sur les entités du Domain.
7. **CQRS** : Command/Query handlers dans la couche Application, DTO d'entrée/sortie découplés des entités.
8. **Multi-tenant natif** : chaque entité porte un `tenantId`.

---

## Structure cible — Backend (`src/`)

```
src/
├── Kernel.php
│
├── Shared/
│   ├── Domain/
│   │   ├── ValueObject/
│   │   │   ├── AggregateId.php
│   │   │   └── Email.php
│   │   ├── Event/
│   │   │   └── DomainEventInterface.php
│   │   └── Contract/                    ← Interfaces inter-modules
│   │       ├── UserResolverInterface.php
│   │       └── TenantAwareInterface.php
│   ├── Application/
│   │   └── Bus/
│   │       ├── CommandBusInterface.php
│   │       └── QueryBusInterface.php
│   └── Infrastructure/
│       ├── Doctrine/
│       ├── Messenger/
│       └── ApiPlatform/
│           └── OpenApi/
│
├── Module/
│   ├── Core/                            ← Module obligatoire
│   │   ├── Domain/
│   │   │   ├── Entity/
│   │   │   │   ├── User.php
│   │   │   │   └── Tenant.php
│   │   │   ├── Repository/
│   │   │   │   └── UserRepositoryInterface.php
│   │   │   └── Event/
│   │   │       └── UserCreated.php
│   │   ├── Application/
│   │   │   ├── Command/
│   │   │   │   ├── CreateUser.php
│   │   │   │   └── CreateUserHandler.php
│   │   │   ├── Query/
│   │   │   │   ├── GetUserById.php
│   │   │   │   └── GetUserByIdHandler.php
│   │   │   └── DTO/
│   │   │       └── UserOutput.php
│   │   ├── Infrastructure/
│   │   │   ├── Doctrine/
│   │   │   │   ├── DoctrineUserRepository.php
│   │   │   │   └── mapping/
│   │   │   └── ApiPlatform/
│   │   │       ├── Resource/
│   │   │       │   └── UserResource.php
│   │   │       ├── State/
│   │   │       │   ├── Provider/
│   │   │       │   └── Processor/
│   │   │       └── Filter/
│   │   └── CoreModule.php               ← Déclaration : config, routes, dépendances
│   │
│   ├── GestionRH/                       ← Module vendable
│   │   ├── Domain/
│   │   │   ├── Entity/
│   │   │   │   ├── Employee.php
│   │   │   │   ├── Contract.php
│   │   │   │   └── Leave.php
│   │   │   ├── ValueObject/
│   │   │   ├── Repository/
│   │   │   │   └── EmployeeRepositoryInterface.php
│   │   │   ├── Event/
│   │   │   │   └── EmployeeHired.php
│   │   │   ├── Exception/
│   │   │   └── Service/
│   │   ├── Application/
│   │   │   ├── Command/
│   │   │   ├── Query/
│   │   │   ├── DTO/
│   │   │   └── Listener/               ← Réagit aux events d'autres modules
│   │   ├── Infrastructure/
│   │   │   ├── Doctrine/
│   │   │   │   ├── DoctrineEmployeeRepository.php
│   │   │   │   ├── mapping/
│   │   │   │   └── migrations/          ← Migrations propres au module
│   │   │   └── ApiPlatform/
│   │   │       ├── Resource/
│   │   │       │   └── EmployeeResource.php
│   │   │       └── State/
│   │   │           ├── Provider/
│   │   │           └── Processor/
│   │   └── GestionRHModule.php
│   │
│   ├── Formation/                       ← Module vendable
│   │   ├── Domain/
│   │   ├── Application/
│   │   ├── Infrastructure/
│   │   └── FormationModule.php
│   │
│   ├── Pointage/                        ← Module vendable
│   │   ├── Domain/
│   │   ├── Application/
│   │   ├── Infrastructure/
│   │   └── PointageModule.php
│   │
│   └── Paie/                            ← Module vendable
│       ├── Domain/
│       ├── Application/
│       ├── Infrastructure/
│       └── PaieModule.php
│
└── config/
    └── modules.php                      ← Liste des modules activés
```

---

## Structure cible — Frontend (`frontend/`)

```
frontend/
├── app/
│   ├── layouts/
│   │   └── default.vue                  ← Menu dynamique selon modules activés
│   ├── middleware/
│   │   └── modules.global.ts            ← Bloque les routes de modules désactivés
│   └── app.vue
│
├── shared/
│   ├── composables/
│   │   ├── useAuth.ts
│   │   ├── useApi.ts
│   │   ├── useTenant.ts
│   │   └── useModules.ts               ← Expose les modules activés (via API)
│   ├── components/
│   │   ├── ui/                          ← Design system (boutons, tables, modals…)
│   │   ├── AppSidebar.vue
│   │   └── AppHeader.vue
│   ├── types/
│   │   └── index.ts
│   ├── utils/
│   └── stores/
│       ├── auth.ts
│       └── tenant.ts
│
├── modules/
│   ├── core/
│   │   ├── pages/
│   │   │   ├── login.vue
│   │   │   ├── dashboard.vue
│   │   │   └── users/
│   │   │       ├── index.vue
│   │   │       └── [id].vue
│   │   ├── components/
│   │   ├── composables/
│   │   ├── stores/
│   │   │   └── users.ts
│   │   ├── types/
│   │   └── core.module.ts
│   │
│   ├── gestion-rh/
│   │   ├── pages/
│   │   │   ├── employees/
│   │   │   │   ├── index.vue
│   │   │   │   └── [id].vue
│   │   │   ├── contracts/
│   │   │   └── leaves/
│   │   ├── components/
│   │   │   ├── EmployeeCard.vue
│   │   │   └── LeaveCalendar.vue
│   │   ├── composables/
│   │   │   └── useEmployees.ts
│   │   ├── stores/
│   │   │   └── employees.ts
│   │   ├── types/
│   │   │   └── index.ts
│   │   └── gestion-rh.module.ts
│   │
│   ├── formation/
│   │   ├── pages/
│   │   ├── components/
│   │   ├── composables/
│   │   ├── stores/
│   │   ├── types/
│   │   └── formation.module.ts
│   │
│   ├── pointage/
│   │   ├── pages/
│   │   ├── components/
│   │   ├── composables/
│   │   ├── stores/
│   │   ├── types/
│   │   └── pointage.module.ts
│   │
│   └── paie/
│       ├── pages/
│       ├── components/
│       ├── composables/
│       ├── stores/
│       ├── types/
│       └── paie.module.ts
│
├── plugins/
│   └── modules-loader.ts               ← Charge dynamiquement les modules activés
├── nuxt.config.ts
└── package.json
```

---

## Règles d'implémentation

### Backend

#### 1. Communication inter-modules
- **INTERDIT** : `use Module\GestionRH\Domain\Entity\Employee` depuis le module Paie.
- **AUTORISÉ** : passer par `Shared\Domain\Contract\EmployeeResolverInterface` ou écouter un domain event comme `EmployeeHired`.
- Les contrats (interfaces) partagés vivent dans `Shared/Domain/Contract/`.

#### 2. Couche Domain (aucune dépendance Symfony)
- Entités avec logique métier encapsulée (pas d'anemic model).
- Value Objects pour la validation (Email, Money, OrderStatus…).
- Repository = interface uniquement.
- Domain Events pour notifier les autres modules.
- Aucun `use Symfony\...` dans ce dossier.

#### 3. Couche Application
- CQRS : Command (écriture) + Query (lecture) avec leurs Handlers.
- Les Handlers orchestrent : ils appellent le Domain et les interfaces Repository.
- DTO Input/Output pour le contrat d'entrée/sortie, découplés des entités.
- Listeners pour réagir aux events d'autres modules.

#### 4. Couche Infrastructure
- Implémentations Doctrine des repositories.
- Mapping et migrations **propres à chaque module** (pas de migration centralisée).
- API Platform :
  - `Resource/` : classes avec `#[ApiResource]`, jamais posé sur les entités Domain.
  - `State/Provider/` : fournisseurs de données (GET).
  - `State/Processor/` : traitement des mutations (POST/PUT/PATCH/DELETE), délègue au bus ou aux handlers.
  - `Filter/` : filtres API Platform spécifiques au module.
- Les endpoints n'apparaissent dans l'OpenAPI que si le module est activé.

#### 5. Module declaration (`*Module.php`)
Chaque module déclare :
- Son identifiant unique.
- Ses dépendances (ex : GestionRH dépend de Core).
- Sa configuration de services.
- Ses routes.

#### 6. Activation/désactivation
- Fichier `config/modules.php` ou variable d'environnement listant les modules actifs.
- Le Kernel ne charge que les services/routes/migrations des modules activés.
- Endpoint API : `GET /api/tenant/modules` retourne la liste des modules activés pour le tenant courant.

#### 7. Multi-tenant
- Chaque entité porte un `tenantId`.
- Filtrage automatique Doctrine par tenant (Doctrine Filter ou listeners).

### Frontend

#### 1. Déclaration de module (`*.module.ts`)
```typescript
export default defineAppModule({
  id: 'gestion-rh',
  label: 'Gestion RH',
  icon: 'i-lucide-users',
  requiredModules: ['core'],
  navigation: [
    { label: 'Employés', to: '/employees', icon: 'i-lucide-user' },
    { label: 'Contrats', to: '/contracts', icon: 'i-lucide-file-text' },
    { label: 'Congés', to: '/leaves', icon: 'i-lucide-calendar' },
  ],
  permissions: ['employee.read', 'employee.write', 'leave.manage'],
})
```

#### 2. Chargement dynamique (`useModules`)
```typescript
export const useModules = () => {
  const enabledModules = useState<string[]>('modules', () => [])

  const isEnabled = (moduleId: string) =>
    enabledModules.value.includes(moduleId)

  return { enabledModules, isEnabled }
}
```
Au boot de l'app, appel `GET /api/tenant/modules` pour récupérer les modules activés.

#### 3. Middleware de protection des routes
```typescript
export default defineNuxtRouteMiddleware((to) => {
  const { isEnabled } = useModules()
  const moduleId = resolveModuleFromRoute(to.path)
  if (moduleId && !isEnabled(moduleId)) {
    return navigateTo('/dashboard')
  }
})
```

#### 4. Sidebar dynamique
Le layout `default.vue` itère sur les modules activés et affiche leurs entrées `navigation`.

#### 5. Isolation
- Chaque module front a ses propres pages, components, composables, stores, types.
- Les composants partagés (design system) sont dans `shared/components/ui/`.
- Un module front ne doit jamais importer depuis un autre module front. Si besoin de données croisées, passer par l'API ou par un composable partagé dans `shared/`.

---

## Résumé des conventions de nommage

| Élément | Convention | Exemple |
|---------|-----------|---------|
| Module back | PascalCase | `Module/GestionRH/` |
| Module front | kebab-case | `modules/gestion-rh/` |
| Entity | PascalCase singulier | `Employee.php` |
| Repository interface | `*RepositoryInterface` | `EmployeeRepositoryInterface.php` |
| Repository impl | `Doctrine*Repository` | `DoctrineEmployeeRepository.php` |
| Command | Verbe + Nom | `CreateEmployee.php` |
| Command Handler | `*Handler` | `CreateEmployeeHandler.php` |
| DTO | `*Input` / `*Output` | `EmployeeOutput.php` |
| API Resource | `*Resource` | `EmployeeResource.php` |
| Provider | `*Provider` | `EmployeeProvider.php` |
| Processor | `*Processor` | `CreateEmployeeProcessor.php` |
| Module declaration back | `*Module.php` | `GestionRHModule.php` |
| Module declaration front | `*.module.ts` | `gestion-rh.module.ts` |
| Composable | `use*` | `useEmployees.ts` |
| Store | nom du domaine | `employees.ts` |

---

## Checklist de migration

Si le projet existe déjà avec une structure plate, voici l'ordre de migration recommandé :

1. Créer `Shared/` et y déplacer les interfaces/VO de base.
2. Créer `Module/Core/` et y migrer users, auth, tenants.
3. Pour chaque futur module vendable, créer le dossier `Module/<Nom>/` avec les 3 couches (Domain, Application, Infrastructure).
4. Déplacer les entités, repositories, services dans le bon module.
5. Remplacer les imports directs inter-modules par des contrats (`Shared/Domain/Contract/`) ou des events.
6. Isoler les migrations Doctrine par module.
7. Adapter les resources API Platform (les sortir des entités, créer les Providers/Processors).
8. Côté front, créer `shared/` et `modules/`, migrer les pages/composants dans le bon module.
9. Implémenter `useModules` + middleware de routes + sidebar dynamique.
10. Tester l'activation/désactivation d'un module de bout en bout.
