Files
Omika/docs/niveau-3-modele-canonique.md
2026-10-23 18:46:50 +02:00

328 lines
12 KiB
Markdown

# Niveau 3 — Modèle métier canonique et contrats API
## Rôle des dossiers de `Omika.Core`
### `Domain/`
Le domaine contient les **entités métier canoniques**, indépendantes du stockage.
Elles correspondent aux concepts fonctionnels de l'application :
- Client, Adresse, Contact, Site ;
- Societe, Collaborateur, Equipe, Utilisateur ;
- Dossier, Intervention, ActionIntervention, CompteRendu, Checklist ;
- RendezVous et ses éléments associés ;
- Document et modèles de courrier/rapport ;
- Role, Permission, DroitAcces ;
- ParametreMetier, ParametreApplication, PreferenceUtilisateur.
Elles ne portent :
- aucun attribut HFSQL ;
- aucun préfixe de rubrique physique (`CL_`, `IN_`, `PL_`, `RDV_`…) ;
- aucun champ zombie.
Les **objets de valeur** (`ValueObjects/`) représentent des valeurs sans identité :
- `ReferenceValue` (valeur de référentiel configurable) ;
- `DateTimeRange` (plage horaire validée) ;
- `PostalAddress`, `ContactDetails` ;
- `ExternalReference` (référence vers Batigest / base externe).
Les **exceptions** (`Exceptions/`) portent les erreurs de validation du domaine.
### `Contracts/`
Les contrats sont utilisés entre `Omika.Api` et `Omika.App`.
Ce sont des DTO purs :
- résumés de liste ;
- détails ;
- requêtes de création / modification / recherche ;
- DTO communs (pagination, enveloppe d'erreur, référence, adresse, coordonnées).
**Distinction `AddressDto` / `AdresseDto` :**
- `AddressDto` (`Contracts/Common`) : adresse postale simple, sans identité
(libellé, lignes, code postal, ville, pays). Réutilisée partout où une
adresse postale brute est nécessaire.
- `AdresseDto` (`Contracts/Clients`) : adresse d'une entité du domaine Clients,
avec identifiant, type, indicateurs (« principale », « archivée ») et une
adresse postale optionnelle (`AdressePostale`). Il ne s'agit pas d'un doublon
de `AddressDto` : l'un est une valeur, l'autre un conteneur d'entité.
Ils ne contiennent :
- aucun attribut HFSQL ;
- aucune propriété à préfixe physique ;
- aucune dépendance vers `Omika.Data` ;
- aucun champ zombie ;
- aucun mot de passe ni objet de connexion ;
- aucun graphe cyclique.
### `Compatibility/`
Les types de compatibilité conservent la **provenance** des données sans exposer
la structure physique :
- `DatabaseSchemaVersion` (Unknown / Batik / Omika) ;
- `DatabaseSource` (version + nom logique de base) ;
- `LegacyEntityReference` (référence vers l'enregistrement d'origine) ;
- `SchemaCapabilities` (capacités déclarées d'un schéma) ;
- `CanonicalMappingResult<T>` (résultat d'un mapping physique → canonique).
### `Interfaces/`, `Abstractions/`, `Constants/`, `Runtime/`, `Mapping/`
- `Interfaces/` : services métier (voir ci-dessous).
- `Abstractions/` : abstractions techniques existantes, non touchées.
- `Constants/` : constantes existantes.
- `Runtime/` : contexte d'exécution existant, non touché.
- `Mapping/` : mappers purs entité métier → DTO.
## Différence entre une entité métier et un modèle physique
| Modèle physique (Omika.Data) | Entité métier (Omika.Core.Domain) |
|---|---|
| Correspond aux tables HFSQL (`Document.fic`, `Planning_RDV`…) | Correspond aux concepts fonctionnels (Document, RendezVous…) |
| Porte des rubriques préfixées (`DC_Categorie`, `PL_PKID`…) | Porte des propriétés métier nommées sans préfixe |
| Marque les rubriques zombie | Ne contient jamais de champ zombie |
| Connaît les clés internes et la version physique | Connaît uniquement les identifiants canoniques et la provenance |
La chaîne attendue :
```
Modèle physique Batik (Isis_BO) Modèle physique Omika
| |
+----------> Modèle métier canonique <-+
|
v
Contrat API
|
v
Application Avalonia
```
Le mapping **physique → métier** reste dans `Omika.Data`.
Le mapping **métier → DTO** est dans `Omika.Core/Mapping`.
## Différence entre une entité métier et un DTO
| Entité métier | DTO (contrat) |
|---|---|
| Évolue dans le temps (classe) | Immutable par convention (`record` avec propriétés `init`) |
| Expose des collections contrôlées (`Add*`) | Expose des listes `IReadOnlyList<T>` en lecture seule |
| Peut contenir des objets de valeur du domaine | Ne contient que des DTO ou types primitifs |
| Vit dans `Domain/` | Vit dans `Contracts/` |
| N'est jamais sérialisé tel quel | Est la frontière de l'API |
## Stratégie des identifiants
Les identifiants publics des contrats sont des **chaînes** afin de permettre
temporairement :
- le GUID Omika ;
- l'identifiant historique Batik (Isis_BO) ;
- une clé métier.
Le domaine utilise `Guid Id` (identifiant canonique) plus
`LegacyEntityReference? SourceReference` pour conserver la référence d'origine
sans exposer la clé physique.
`LegacyEntityReference.PublicIdentifier` fournit une représentation publique
stable (GUID → clé métier → identifiant numérique → GUID physique → ImportId).
Un helper de représentation publique des identifiants peut être ajouté plus tard
si le besoin se fait sentir côté API/mobile.
## Stratégie des paramètres métier
Les paramètres métier (type de client, statut, type d'activité, priorité…) sont
représentés par `ReferenceValue` :
- `Key` : clé métier stable (nouveau schéma Omika) ;
- `LegacyId` : identifiant historique (ancien schéma Batik) ;
- `Code` : code court éventuel ;
- `Label` : libellé d'affichage.
Ils ne sont **pas** transformés automatiquement en `enum` : ils restent
configurables. Le passage en enum n'est envisagé que si un paramètre devient
vraiment stable et fermé.
## Stratégie des champs zombie
Les rubriques physiques obsolètes (anciens identifiants de civilité, de statut,
clés internes…) sont marquées **zombie** dans les modèles physiques de
`Omika.Data`.
Dans `Omika.Core` :
- elles n'apparaissent jamais comme propriétés publiques ;
- le mapper de `Omika.Data` est responsable de convertir les valeurs en
propriétés métier (`ReferenceValue`, libellés, etc.) ;
- les tests d'architecture interdisent leur apparition dans les contrats.
## Stratégie de compatibilité Batik (Isis_BO) / Omika
- Le domaine est **unique** pour les deux bases.
- La provenance est conservée via `LegacyEntityReference` / `DatabaseSource`.
- `SchemaCapabilities` permet de savoir, sans connaître la structure physique,
si une fonctionnalité est disponible dans la version source
(clés métier, GUID, rôles, nouveau modèle de rendez-vous…).
- `CanonicalMappingResult<T>` permet à un mapper physique → canonique de
retourner la valeur plus les warnings, champs ignorés et erreurs non bloquantes.
## Cas particulier des rendez-vous
Un **agrégat métier unique** `RendezVous` reçoit les données des deux versions :
- ancien modèle : `Planning_RDV`, `Planning_RDV_Personne` ;
- nouveau modèle : `RDV_Planning`, `RDV_Collaborateur`, `RDV_Contact`.
L'entité porte :
- une `DateTimeRange Periode` ;
- des `AffectationCollaborateur` (responsable, période particulière, statut,
temps validé) ;
- des `ParticipantRendezVous` (contact, coordonnées, type, présence confirmée) ;
- une `AnnulationRendezVous` éventuelle ;
- des liens optionnels vers Client / Site / Dossier / Intervention.
Toute information absente dans une version est **nullable**.
Le contrat `RendezVousDetailsDto` est **identique** pour les deux versions et ne
contient aucune notion de `Planning_RDV`, `RDV_Planning`, `PL_PKID`,
`RDP_GUID` ou autre nom physique.
## Liste des fichiers créés
### `Omika.Core/Compatibility`
- `DatabaseSchemaVersion.cs`
- `DatabaseSource.cs`
- `LegacyEntityReference.cs`
- `SchemaCapabilities.cs`
- `CanonicalMappingResult.cs`
### `Omika.Core/Domain/Common`
- `EntityBase.cs`
### `Omika.Core/Domain/ValueObjects`
- `ReferenceValue.cs`
- `DateTimeRange.cs`
- `PostalAddress.cs`
- `ContactDetails.cs`
- `ExternalReference.cs`
### `Omika.Core/Domain/Exceptions`
- `DomainException.cs`
- `DomainValidationException.cs`
### `Omika.Core/Domain/Entities`
- `Clients/Client.cs`, `Adresse.cs`, `Contact.cs`, `Site.cs`
- `Organisation/Societe.cs`, `Collaborateur.cs`, `Equipe.cs`, `Utilisateur.cs`
- `Interventions/Dossier.cs`, `Intervention.cs`, `ActionIntervention.cs`,
`CompteRendu.cs`, `EtatIntervention.cs`, `Checklist.cs`, `ChecklistItem.cs`
- `Planning/RendezVous.cs`, `AffectationCollaborateur.cs`,
`ParticipantRendezVous.cs`, `AnnulationRendezVous.cs`, `SuiviTemps.cs`
- `Documents/Document.cs`, `NoteDocument.cs`, `MessageInterne.cs`,
`MessageExterne.cs`, `ModeleMail.cs`, `ModeleRapport.cs`
- `Security/Role.cs`, `Permission.cs`, `DroitAcces.cs`
- `Configuration/ParametreMetier.cs`, `ParametreApplication.cs`,
`PreferenceUtilisateur.cs`
### `Omika.Core/Contracts/Common`
- `ApiErrorDto.cs`, `ApiResult.cs`, `PagedRequest.cs`, `PagedResult.cs`
- `ReferenceValueDto.cs`, `AddressDto.cs`, `ContactDetailsDto.cs`
### `Omika.Core/Contracts/Clients`
- `ClientSummaryDto.cs`, `ClientDetailsDto.cs`
- `CreateClientRequest.cs`, `UpdateClientRequest.cs`, `SearchClientsRequest.cs`
- `AdresseDto.cs`, `ContactDto.cs`, `SiteDto.cs`
### `Omika.Core/Contracts/Organisation`
- `CollaborateurSummaryDto.cs`, `CollaborateurDetailsDto.cs`
- `EquipeDto.cs`, `SocieteDto.cs`, `UtilisateurDto.cs`
### `Omika.Core/Contracts/Interventions`
- `DossierSummaryDto.cs`, `DossierDetailsDto.cs`
- `InterventionSummaryDto.cs`, `InterventionDetailsDto.cs`
- `CreateInterventionRequest.cs`, `UpdateInterventionRequest.cs`,
`SearchInterventionsRequest.cs`
- `ActionInterventionDto.cs`, `CompteRenduDto.cs`, `EtatInterventionDto.cs`
### `Omika.Core/Contracts/Planning`
- `RendezVousSummaryDto.cs`, `RendezVousDetailsDto.cs`
- `CreateRendezVousRequest.cs`, `UpdateRendezVousRequest.cs`,
`SearchRendezVousRequest.cs`
- `AffectationCollaborateurDto.cs`, `ParticipantRendezVousDto.cs`,
`AnnulationRendezVousDto.cs`, `SuiviTempsDto.cs`, `CreateSuiviTempsRequest.cs`
### `Omika.Core/Contracts/Documents`
- `DocumentSummaryDto.cs`, `DocumentDetailsDto.cs`, `CreateDocumentRequest.cs`
- `ModeleMailDto.cs`, `ModeleRapportDto.cs`
### `Omika.Core/Contracts/Security`
- `LoginRequest.cs`, `CurrentUserDto.cs`, `RoleDto.cs`, `PermissionDto.cs`
### `Omika.Core/Contracts/Configuration`
- `ParametreMetierDto.cs`, `ApplicationConfigurationDto.cs`,
`PreferenceUtilisateurDto.cs`
### `Omika.Core/Interfaces`
- `IClientService.cs`, `IInterventionService.cs`, `IRendezVousService.cs`,
`IDocumentService.cs`, `IReferenceDataService.cs`, `ICurrentUserService.cs`
### `Omika.Core/Mapping`
- `ClientContractMapper.cs`, `InterventionContractMapper.cs`,
`RendezVousContractMapper.cs`, `DocumentContractMapper.cs`
### `Omika.Tests`
- Architecture : `CoreMustNotReferenceDataTests`,
`CoreMustNotReferenceAvaloniaTests`, `CoreMustNotReferenceAspNetCoreTests`,
`PublicContractsMustNotContainHfSqlNamesTests`,
`PublicContractsMustNotContainZombieFieldsTests`
- Contrats : `PagedResultTests`, `ClientContractTests`,
`InterventionContractTests`, `RendezVousContractTests`
- Domaine : `DateTimeRangeTests`, `ClientDomainTests`,
`InterventionDomainTests`, `RendezVousDomainTests`,
`RendezVousCrossSchemaTests`
- Compatibilité : `LegacyEntityReferenceTests`, `SchemaCapabilitiesTests`
## Décisions encore à valider
1. **Identifiants publics** : chaînes temporaires (GUID / legacy / clé métier).
Un helper dédié ou un type valeur « PublicId » reste possible.
2. **États et statuts** : conservés en `ReferenceValue`. À confirmer si certains
deviendront des enums stables.
3. **`DateTime` vs `DateTimeOffset`** : pas de gestion multi-fuseaux documentée
pour l'instant ; `DateTime` est utilisé partout. À revoir si le mobile doit
synchroniser sur plusieurs fuseaux.
4. **Collections dans les entités** : `List<T>` privées avec méthodes `Add*`.
À confirmer si des collections doivent devenir remplaçables (remplacement
complet lors d'une mise à jour).
5. **Contrat de recherche** : `SearchClientsRequest` hérite de `PagedRequest`
avec des propriétés additionnelles. Les filtres seront consolidés lors de
l'implémentation des services.
6. **Sécurité** : `LoginRequest` accepte un mot de passe temporaire, mais aucun
service d'authentification n'est implémenté à ce niveau. Le stockage des
identifiants (hash, salt) sera décidé au niveau supérieur.
7. **Fichiers physiques des documents** : les contrats exposent des identifiants
logiques et des métadonnées ; la gestion du téléversement/téléchargement sera
définie avec l'API.