# 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` (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` 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` 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` 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.