Vue d'ensemble architecturale
Principes fondateurs
Section intitulée « Principes fondateurs »Logmis est construit sur trois principes non-négociables qui contraignent l’ensemble du code :
-
Architecture hexagonale stricte — le domaine n’importe aucun framework. NestJS, Drizzle, pg, NATS, Temporal, Redis sont confinés dans les couches infrastructure et composition. Le domaine ne dépend que de ports (interfaces).
-
Result<T, DomainError>no-throw — aucune erreur métier ne lève d’exception. Chaque use-case retourneok(value)oufail(origin, code, message). Lesswitchdiscriminants utilisentassertNeverpour l’exhaustivité. -
DB remplaçable — toute requête base de données transite par le port neutre
Executor/Transactional<Executor>. Nipgnidrizzlene sont jamais importés dansmodules/*/domain/oumodules/*/application/. La fitness functionlint:archbloque même lesimport typede ces modules dans le domaine.
Structure du monorepo
Section intitulée « Structure du monorepo »/data/PMS/├── apps/│ ├── api/ Backend NestJS 11 + Fastify│ ├── admin/ Console control-plane (Angular 20, port 4301)│ ├── tenant/ Cockpit property-manager (Angular 20, port 4302)│ ├── reception/ Front-desk (Angular 20, port 4303)│ ├── mobile-staff/ Personnel terrain (Capacitor 6, port 4300)│ ├── tenant-mobile/ Gestionnaire nomade (Capacitor 6, port 4304)│ └── docs/ Ce site (Astro + Starlight, port 4400)├── packages/│ ├── shared-kernel/ Primitives pures zéro-dépendance│ ├── events-proto/ Schemas Protobuf + Envelope HMAC│ ├── api-client/ Client typé OpenAPI (v1, 128 paths)│ ├── api-client-admin/ Client typé API admin (9 paths)│ ├── api-client-internal/ Client interne (généré, non consommé en V1)│ └── ui/ Design system Osmose (@pms/ui 0.1.0)├── docker-compose.dev.yml├── pnpm-workspace.yaml└── CLAUDE.mdLayering de l’API backend
Section intitulée « Layering de l’API backend »apps/api/src/├── modules/ 36 bounded contexts│ └── <bc>/│ ├── domain/ Agrégats, VOs, ports, use-cases (pur TS)│ ├── application/ Use-cases (orchestrent le domaine)│ └── infrastructure/ Repositories, adapters, migrations├── interface/http/ Couche HTTP centralisée (controllers, guards, pipes)│ ├── v1/ Endpoints /v1/** (141 opérations)│ └── admin/ Endpoints /admin/** (9 opérations)├── composition/ Seul lieu croisant plusieurs modules│ ├── *.orchestrator.ts Golden paths cross-BC│ ├── consumer-worker.ts 17 consumers NATS│ └── saga/ Accès Temporal (interdit aux modules)└── platform/ Infrastructure transversale ├── persistence/ Pool, Executor, withTenantTransaction ├── config/ Validation Zod des env vars ├── crypto/ SecretStorePort KMS-agnostique ├── temporal/ 6 workflows └── messaging/ NATS JetStreamRègle de dépendance
Section intitulée « Règle de dépendance »domain/ ←── application/ ←── infrastructure/ ↑ composition/ ↑ interface/http/composition/ est le seul endroit qui peut importer plusieurs modules simultanément. Un module A ne doit jamais importer de module B directement — toujours passer par composition/.
10 Fitness functions (toutes CI-bloquantes)
Section intitulée « 10 Fitness functions (toutes CI-bloquantes) »Chaque règle est vérifiée à chaque CI sur push et PR :
| Commande | Règle vérifiée |
|---|---|
lint:arch | Hexagonal strict + no-cross-BC + C1 (dependency-cruiser, tsPreCompilationDeps:true — bloque aussi les import type) |
lint:jsonb | Zéro JSONB en base (ADR-0014) — attributs en EAV typé relationnel |
lint:rls | Anti current_setting(...,true) fail-open (ADR-D08) |
lint:payment-pci | Zéro donnée carte (PAN/CVV/CVC/track/card_number) — PCI SAQ A |
lint:org | Zéro ltree direct |
lint:audit | Piste d’audit inaltérable — zéro UPDATE/DELETE sur les tables append-only (T13) |
lint:distribution | Définition unique de la disponibilité stock (INV-6b-2) |
lint:pos-bridge | Isolation ACL OsmoVente (ADR-D23) |
lint:openapi | Non-régression contrat OpenAPI (oasdiff) |
lint:fiscal-accounts | Zéro numéro de compte comptable 4xx/7xx en dur dans le domaine |
Pipeline CI (GitHub Actions)
Section intitulée « Pipeline CI (GitHub Actions) »5 jobs en parallèle avec concurrency cancel-in-progress :
- security — gitleaks +
pnpm audit --audit-level=high - lint-unit — build packages → typecheck -r → 10 fitness → buf lint → oasdiff → unit tests → coverage unit
- integration — services réels (Postgres 17, Redis, NATS, Temporal) → migrations → tests d’intégration
- coverage-gate — merge unit+int → seuil ≥ 90 % sur money/fiscal/audit
- e2e-fronts — matrice 5 apps Angular → Playwright + axe-core (violations critical/serious bloquantes)
Règles de build ESM
Section intitulée « Règles de build ESM »- Imports relatifs en
.js(NodeNext resolution) —import './foo.js'même si le fichier source estfoo.ts import typeobligatoire partout (verbatimModuleSyntax)reflect-metadataimporté en premier dansmain.ts(DI NestJS)- Build les packages avant les apps :
pnpm -r run buildrespecte l’ordre topologique
@pms/shared-kernel
Section intitulée « @pms/shared-kernel »Primitives pures, zéro dépendance framework, partagées domaine + fronts :
Money— bigint centimes, devise-safe, opérationsadd/sub/mul(entier uniquement), half-up basis-pointsResult<T, E>—ok(value)/fail(origin, code, message)- IDs brandés —
TenantId,ReservationId,FolioId… (nominal typing, pas de confusion accidentelle) BusinessDate— date métier distincte du temps systèmeOrgPath— chemin d’organisation (comparaison par préfixe pour le RBAC)MoneyRatio—applyBasisPoints/applyBasisPointsCeil/decomposeFromTtcCancellationPolicy— snapshot inaltérable à la confirmationMandateEstablishmentReader— port pour lire le mandat/établissement