Migration modular monolith DDD (0.1 → 3.3) #17
Reference in New Issue
Block a user
Delete Branch "integration/modular-monolith-0.1-1.3"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Migration modular monolith DDD — Lesstime (0.1 → 3.3)
Cette MR regroupe l'intégralité de la refonte en monolithe modulaire (strangler progressif, additif). Elle remplace les MR stackées de Phase 1 (#12–#16), désormais incluses ici.
Ne pas merger avant validation fonctionnelle : branche destinée à être testée telle quelle.
Périmètre — 9 modules sous
src/Module/ModuleInterface, mapping Doctrine par modulefrontend/modules/*module.resource.action, sidebar gated)#[Auditable], listener, provider DBAL)src/Entityvide, app 100% modulaireArchitecture
UserInterface,ProjectInterface,TaskInterface,TaskTagInterface,ClientInterface,ClientTicketInterface,LeaveProfileInterface) +resolve_target_entities100% modulaire (aucune cible legacy).Domain/Repository+ implémentationInfrastructure/Doctrine, bindées.Sécurité
/portalet à ses propres tickets (filtrés parallowedProjects), interdit sur toute l'API interne.QA non-régression (branche reconstruite from scratch)
App\Module\*.Points à arbitrer (hors périmètre de cette migration)
userIdexplicite sans scoping sur certains tools TimeTracking/Absence/TaskDocument) — ticket dédié recommandé.⚠️ Déploiement / migration des données — à ne pas oublier
1. Resynchroniser les séquences PostgreSQL après tout import/restore de dump
Si la prod (ou tout environnement) est montée depuis un dump (
pg_restore/COPY), les lignes sont chargées avec leursidexplicites sans avancer les séquences → au premierINSERT:duplicate key value violates unique constraint "..._pkey"(constaté en local surnotification,task,time_entry…).À lancer juste après chaque restore/import :
2. Fix dénormalisation des collections typées-contrat (code, inclus dans la branche)
Les relations to-many typées par une interface
Shared\Domain\Contract\*(TimeEntry::tags→TaskTagInterface,Task::collaborators→UserInterface) étaient indénormalisables par API Platform (mono-valué OK via IRI, collection KO) → tout POST/PATCH portant une telle collection renvoyait 400/500. Corrigé par un dénormaliseur génériqueContractRelationDenormalizer(réutiliseresolve_target_entities, zéro couplage par-entité) + test fonctionnel de non-régression.Tranche 2 of LST-65. Mechanical, behaviour-preserving move of the core business domain into src/Module/ProjectManagement/. API operations, securities, uriTemplates and the 38 MCP tool names are all unchanged. - 10 entities + 2 enums moved to Domain/{Entity,Enum}; intra-module relations stay concrete, cross-module relations go through contracts (Project.client -> ClientInterface, Task/TaskDocument users -> UserInterface). - 9 repositories split into Domain/Repository interfaces + Doctrine impls, bound in services.yaml; consumers inject the interfaces. find() kept off the interfaces (ServiceEntityRepository ?object compat) -> findById(). - State (7), MCP tools (38), controller, CalDavService/RecurrenceCalculator, 3 Doctrine listeners and SwitchWorkflowOutput moved under Infrastructure/. - doctrine.yaml: ProjectManagement mapping + resolve_target_entities of the 3 module contracts repointed to the module (ClientInterface stays legacy). - ProjectManagementModule registered (id project-management, 4 RBAC perms, not re-wired); sidebar my-tasks/projects gated by the module. - Legacy not-yet-modularised consumers (Mail/Gitea/BookStack, Serializer, fixtures, tests) swapped to the module FQCN — transitional coupling to be cleaned in 2.4/2.5/2.6. 159 tests green, mapping valid, no API route regression, cs-fixer clean.Tranche 4 of LST-65. Companion to the backend module migration. - Move pages (my-tasks, projects, projects/[id]/{index,groups,archives}), 18 components (project + task), 10 services and 10 DTOs into frontend/modules/project-management/ (auto-detected layer). - Rewrite explicit ~/services/* and ~/services/dto/* imports across 38 consumers (admin tabs, mail modals, dashboard, mail page, layout) including the time-tracking module whose DTOs referenced project/task/task-tag. - clients.ts and shared DTOs (client, user-data) stay at the root. - Routes /my-tasks, /projects, /projects/:id(/groups|/archives) preserved; i18n stays global. nuxt build passes; routes confirmed.LST-66 (2.3) backend. Behaviour-preserving move of the absences domain into src/Module/Absence/. API operations, securities, routes and the 10 MCP tool names are unchanged. - 3 entities + 3 enums moved to Domain/{Entity,Enum}; user relations stay on UserInterface. 3 repositories split into Domain/Repository interfaces + Doctrine impls (bound in services.yaml); find() kept off interfaces (findById instead). - Pure services (AbsenceDayCalculator, PublicHolidayProvider) -> Domain/Service; AbsenceBalanceService -> Application/Service; State (5), controllers (5), 10 MCP tools and AccrueLeaveCommand -> Infrastructure/. - New LeaveProfileInterface contract (Shared) exposes the HR getters used by AbsenceBalanceService/AccrueLeaveCommand; User implements it -> Absence no longer imports the concrete Core User. MCP tools/command inject UserRepositoryInterface (findById) instead of the concrete repository. - Timestampable/Blamable added to AbsenceBalance and AbsencePolicy (additive migration: created_at/updated_at + created_by/updated_by FK ON DELETE SET NULL + COMMENT). AbsenceRequest untouched (already has createdAt/reviewedAt). - AbsenceModule registered (id absence, 4 RBAC perms, not re-wired); doctrine mapping added; team-absences sidebar item gated by the module. 161 tests green, mapping valid, no API route regression, cs-fixer clean.LST-58 (2.4), part 2 — Prospect (new entity). Completes the Directory backend. - ProspectStatus enum (new/contacted/qualified/won/lost) + Prospect entity (name, company, email, phone, address, status, source, notes, convertedClient -> ClientInterface) with Timestampable/Blamable + #[Auditable]. - API: GetCollection/Get (ROLE_USER), Post/Patch/Delete (ROLE_ADMIN), custom POST /prospects/{id}/convert (ConvertProspectProcessor: creates a Client from the prospect, links convertedClient, sets status=Won; idempotent). SearchFilter on status. - Repository interface + Doctrine impl (bound); 6 MCP tools (list/get/create/ update/delete/convert-prospect); Serializer::prospect(). Module perms directory.prospects.view/manage. Demo fixtures (3 prospects, one converted). - Additive migration: CREATE TABLE prospect + FKs ON DELETE SET NULL + COMMENT. 163 tests green (incl. conversion test), mapping valid, cs-fixer clean.LST-58 (2.4) front. Completes the Directory module. - New frontend/modules/directory/ layer (auto-detected): /directory page with Clients and Prospects tabs. - Client front moved into the layer (clients service + client DTO + ClientDrawer). New prospects service, prospect DTO and ProspectDrawer (with a "Convert to client" action calling POST /prospects/{id}/convert). - Consumers repointed to ~/modules/directory/... (admin client tab, PM project drawer + project pages + project DTO, time-tracking page + export drawer). - Sidebar admin item /directory gated by the directory module; /directory protected by the admin middleware. i18n keys added (directory.*, prospects.*). nuxt build passes; routes preserved. Adds the 2.4 plan doc.LST-59 (3.1) backend. New native reporting module that aggregates across TimeTracking/ProjectManagement/Absence with ZERO direct inter-module imports — coupled only to the physical SQL schema via read-only DBAL (AuditLog provider pattern). - 4 read-only reports (ApiResource + DBAL provider + readonly DTO, paginationEnabled false, security reporting.view): /api/reports/ {time-per-project, time-per-user, tasks-by-status, absences-by-type}. All filters bound-param, dates validated YYYY-MM-DD (default = current month), int filters validated by regex (cs-fixer-stable). - No Doctrine entity, no migration. ReportFilterTrait centralises validation. Absence status compared by literal 'approved' to avoid importing the enum. - ReportingModule registered (id reporting, reporting.view/export perms); sidebar /reporting item gated by module + permission (ROLE_ADMIN section). 169 tests green (163 + 6), 4 routes exposed, cs-fixer clean.LST-60 (3.3). Closes the modular-monolith migration. src/Entity was already empty; this removes the last legacy residue. - Doctrine: drop the legacy "App" mapping (empty src/Entity). resolve_target_ entities already targets modules only. - MCP User tools (Reference/) -> Core/Infrastructure/Mcp/Tool; MCP Serializer -> Shared/Infrastructure/Mcp (33 usages repointed). - Controllers (mark-all-read, notification unread-count, regenerate-api-token, user-avatar) -> Core/Infrastructure/Controller. TokenEncryptor -> Shared/ Infrastructure/Service (11 usages). AppVersion resource+provider -> Shared. ContractType enum -> Core/Domain/Enum. - src/{Entity,State,Controller,Service,Enum,ApiResource} now empty; routes, MCP tool names and public API unchanged. 180 tests green, mapping valid, no route regression, cs-fixer clean. Note: final Malio visual harmonisation (subjective) left to the PO.