Files
Omika/docs/architecture-solution.md
2026-10-23 18:46:50 +02:00

319 lines
17 KiB
Markdown

# Architecture de la solution Omika
Ce document décrit la structure initiale de la solution .NET **Omika**, son
organisation en projets, les dépendances entre eux, et les conventions retenues
pour la suite (migration HFSQL, application mobile, API, futur Windows).
> **Portée** : phase d'architecture. Les modèles métier détaillés et la connexion
> HFSQL réelle ne sont **pas** encore développés (voir les sections dédiées).
---
## 1. Vue d'ensemble
```
┌────────────────────────────────────────────────────────────┐
│ Omika.App │
│ Application Avalonia (code partagé UI / ViewModels) │
│ Cibles : Android · iOS · Windows (desktop) │
└───────────────┬─────────────────────────────┬──────────────┘
│ │
▼ ▼
Omika.App.Desktop Omika.App.Android
Omika.App.iOS (hôtes plateformes)
(hôtes plateformes)
│
│ ProjectReference
▼
┌─────────────────────────────────────────────┐ ┌──────────────────────────────┐
│ Omika.Core │ │ Omika.Api │
│ Modèles · Interfaces · Constantes · │◄───────│ API ASP.NET Core │
│ Logique métier (indépendant UI/API) │ │ Endpoints / DI / OpenAPI │
└──────────────────┬──────────────────────────┘ └──────────────┬───────────────┘
│ ProjectReference │ ProjectReference
▼ ▼
┌─────────────────────────────────────────────┐ ┌──────────────────────────────┐
│ Omika.Data │ │ Omika.Tests │
│ Connexion HFSQL · Repositories · Mapping │ │ Tests unitaires (xUnit) │
│ Migration Batik/OMIKA │ └──────────────────────────────┘
└─────────────────────────────────────────────┘
```
## 2. Projets créés
| Projet | Type | Rôle |
|-----------------------|-----------------------------------|------|
| `Omika.App` | Bibliothèque Avalonia (net10.0) | Code UI partagé (Views, ViewModels, Services). Cible commune aux hôtes Android / iOS / Windows. |
| `Omika.App.Desktop` | Exécutable Avalonia (net10.0) | Hôte desktop Windows/Linux/macOS. Point d'entrée pour le futur client Windows. |
| `Omika.App.Android` | Exécutable Avalonia (net10.0-android) | Hôte Android (APK/AAB). |
| `Omika.App.iOS` | Exécutable Avalonia (net10.0-ios) | Hôte iOS (simulateur/appareil). **La compilation d'un .app pour iOS exige macOS + Xcode.** |
| `Omika.Api` | API ASP.NET Core (net10.0) | Expose les fonctionnalités métier et données via HTTP + OpenAPI. |
| `Omika.Core` | Bibliothèque (net10.0) | Modèles communs, interfaces, constantes, types partagés, logique métier. Aucune dépendance UI/API. |
| `Omika.Data` | Bibliothèque (net10.0) | Accès aux données HFSQL : options de connexion, contrats, repositories, mapping de tables, migration Batik/OMIKA. |
| `Omika.Tests` | xUnit (net10.0) | Tests unitaires des éléments critiques (Result, validation HFSQL, mapping de tables). |
## 3. Dépendances entre projets
```
Omika.App.Desktop ──► Omika.App ──► Omika.Core
Omika.App.Android ──► Omika.App ──► Omika.Core
Omika.App.iOS ──► Omika.App ──► Omika.Core
Omika.Api ──► Omika.Core
──► Omika.Data
Omika.Data ──► Omika.Core
Omika.Tests ──► Omika.Core
──► Omika.Data
```
- **Omika.Core** n'a aucune dépendance : c'est le noyau partagé.
- **Omika.Data** dépend de **Omika.Core** (interfaces, constantes, types).
- **Omika.Api** dépend de **Omika.Core** et **Omika.Data**.
- **Omika.App** et ses hôtes dépendent de **Omika.Core** (les appels à l'API se feront
via HTTP, pas par référence directe au projet Data).
- **Omika.Tests** référence **Omika.Core** et **Omika.Data**.
## 4. Gestion des packages
Le fichier racine **`Directory.Packages.props`** active la gestion centralisée
des packages (CPM). Toutes les versions NuGet y sont déclarées une seule fois ;
les fichiers `.csproj` référencent les packages **sans version**.
| Groupe | Package |
|------------------|---------|
| Avalonia | `Avalonia`, `Avalonia.Themes.Fluent`, `Avalonia.Fonts.Inter`, `Avalonia.Desktop`, `Avalonia.Android`, `Avalonia.iOS` |
| Diagnostique | `AvaloniaUI.DiagnosticsSupport` |
| Android | `Xamarin.AndroidX.Core.SplashScreen` |
| MVVM | `CommunityToolkit.Mvvm` |
| API | `Microsoft.AspNetCore.OpenApi` |
| Configuration | `Microsoft.Extensions.Options.ConfigurationExtensions` |
| Tests | `xunit`, `xunit.runner.visualstudio`, `Microsoft.NET.Test.Sdk`, `coverlet.collector` |
## 5. Structure des dossiers
```
Omika.slnx → solution (format .slnx)
Directory.Packages.props → versions centralisées des packages NuGet
docs/architecture-solution.md → ce document
docs/schema-compatibilite-batik-omika.json
→ dictionnaire machine-readable du schéma (tables, rubriques, liens)
analyse-sources.md → analyse WINDEV existante
BATIK.md / OMIKA.md → documentation historique
tools/generate_models.py → générateur des modèles physiques HFSQL (Batik/Omika)
src/
Omika.Core/
Constants/ → constantes applicatives et base de données
Interfaces/ → contrats (IRepository, IUnitOfWork)
Abstractions/ → types partagés (Result)
Runtime/ → version active de la base (BATIK/OMIKA), contexte global
Models/ → (vide pour l'instant) futurs modèles métier
Omika.Data/
Configuration/ → HfsqlOptions + validation
Connection/ → contrats et squelette de connexion HFSQL
Repositories/ → base commune des repositories
Mapping/ → référentiel de tables + mapping Batik/OMIKA
HfSql/Metadata/ → attributs de mapping HFSQL (Table, Column, PrimaryKey, CompositeKey, LegacyField)
HfSql/Models/ → modèles physiques générés (Batik/ et Omika/) — 106 classes
Migrations/ → service de migration Batik → OMIKA
Omika.Api/
Endpoints/ → endpoints HTTP (santé, futur métier)
Omika.App/
Views/ → MainWindow (desktop) et MainView (partagé)
ViewModels/ → MainViewModel, ViewModelBase
Omika.App.Desktop/
Omika.App.Android/
Omika.App.iOS/
tests/
Omika.Tests/ → tests xUnit (47 tests)
```
## 6. Conventions et règles
1. **Séparation des responsabilités** : l'UI (App) et l'API ne contiennent aucune
logique métier ; celle-ci vit dans **Omika.Core**.
2. **Accès aux données** : uniquement via **Omika.Data**, derrière les interfaces
`IRepository<TEntity,TId>` et `IUnitOfWork` définies dans **Omika.Core**.
3. **Pas de dépendance directe App → Data** : l'application mobile consommera
l'API (HTTP). Si un accès direct est nécessaire (hors ligne), il passera par
une façade dédiée à ajouter ultérieurement.
4. **Configuration** : les options de connexion HFSQL sont typées
(`HfsqlOptions`, section `"Hfsql"`), validées avant usage, et remplaçables
par la configuration .NET standard (appsettings / user-secrets / variables
d'environnement).
5. **Résultats d'opération** : les services renvoient `Result` / `Result<T>`
(Omika.Core) plutôt que de lever des exceptions pour les cas métier.
## 7. État de la couche HFSQL (phase d'architecture)
À ce stade, **aucune connexion HFSQL réelle n'est ouverte**. La couche fournit :
- `HfsqlOptions` et `HfsqlOptionsValidator` : préparation et validation des
paramètres (serveur, port 4900, base, groupes de protection ProtectionStd /
ProtectionPlus, comptes techniques) ;
- `IHfsqlConnection` / `HfsqlConnection` : contrat + implémentation squelette
(`OpenAsync` lève volontairement `NotImplementedException`) ;
- `RepositoryBase<TEntity,TId>` : base commune prête à câbler le provider HFSQL ;
- `LegacyTableMap` : référentiel des 58 tables OMIKA et des tables de l'ancienne
version (48 tables, e.g. `Intervention_Collaborateur` supprimée) ;
- `TableAttribute` : association future entité → table HFSQL ;
- `IMigrationService` / `MigrationService` : contrat de compatibilité Batik →
OMIKA (conversion de clés numériques vers textuelles, suivi ElementModifie /
ElementSupprime), non implémenté.
### 7.1 Modèles physiques générés (Batik / Omika)
Le dictionnaire machine-readable **`docs/schema-compatibilite-batik-omika.json`**
décrit le schéma WINDEV (tables, rubriques, types HFSQL, liens, marquage zombie).
Le générateur **`tools/generate_models.py`** produit à partir de ce fichier les
**106 modèles physiques** dans `Omika.Data/HfSql/Models` :
| Dossier | Contenu |
|---|---|
| `HfSql/Models/Batik/` | Modèles physiques de l'ancienne base (Isis_BO, 48 tables) |
| `HfSql/Models/Omika/` | Modèles physiques de la nouvelle base (OMIKA, 58 tables) |
Chaque classe `XxxRow` porte les attributs de mapping suivants :
- `[HfSqlTable]` : nom de la table, nom du fichier disque (`.fic`), groupes de
protection, versions de schéma concernées (Batik et/ou Omika) ;
- `[HfSqlColumn]` : nom physique de la rubrique, type HFSQL, longueur,
valeur par défaut, marquage « zombie » ou « Omika uniquement » ;
- `[HfSqlPrimaryKey]` / `[HfSqlCompositeKey]` : clé primaire (automatique ou composée) ;
- `[HfSqlLegacyField]` : rubrique historique à migrer, avec éventuellement la
rubrique de remplacement (`ReplacementColumn`).
Ces modèles sont **purement descriptifs** (attributs) : aucune requête n'est
exécutée. Ils constituent le socle du futur provider HFSQL et du mapping
table → entité.
Pour régénérer les modèles après modification du dictionnaire :
```powershell
python .\tools\generate_models.py
```
**Prochaine étape HFSQL** : choisir et brancher le provider HFSQL (.NET), puis
définir les entités du domaine en s'appuyant sur ces modèles physiques.
## 8. Sélection de base (BATIK / OMIKA)
Le mécanisme de sélection de la base est centralisé dans **`Omika.Core.Runtime`**
et **`Omika.Core.Compatibility`**, en distinguant deux notions :
- **`DatabaseSchemaSelection`** (`Omika.Core.Runtime`) — enum de la **sélection
demandée par la configuration** :
- `Auto` (0) : détection automatique de la base réelle (valeur par défaut sûre) ;
- `Batik` (1) : forcer l'ancienne base ;
- `Omika` (2) : forcer la nouvelle base.
- **`DatabaseSchemaVersion`** (`Omika.Core.Compatibility`) — enum de la
**provenance / version réellement détectée** d'une donnée :
- `Unknown` (0) : état non déterminé ;
- `Batik` (1) : ancienne version ;
- `Omika` (2) : nouvelle version.
Ce type ne doit pas être confondu avec `DatabaseSchemaSelection` : l'un décrit
la sélection demandée au démarrage (configuration), l'autre la version
réellement détectée du schéma dont provient une donnée.
- **`DatabaseSchemaSelectionParser`** — analyse une chaîne de configuration
(« Auto », « Batik » ou « Omika », insensible à la casse et aux espaces).
Il retourne un `Result<DatabaseSchemaSelection>` et refuse toute valeur
inconnue avec un message d'erreur explicite.
- **`ApplicationRuntimeContext`** — contexte global de l'application :
- `ConfigurationKey` : clé conventionnelle `"Omika:DatabaseVersion"` ;
- `Configure(...)` : définit la sélection demandée au démarrage (accepte
l'enum ou une chaîne de configuration) ;
- `SetDetectedVersion(...)` : fournit la version réellement détectée
(Batik / Omika) et la **valide** contre la sélection demandée ;
- `ValidateDetectedVersion(...)` : valide une version détectée sans modifier
le contexte global ;
- `Current` : accès au contexte actif (propriétés `Selection`,
`DetectedVersion`, `IsBatik`, `IsOmika`) ;
- `FromConfiguration(...)` : crée un contexte isolé sans toucher au global
(utile pour les tests et les traitements par lot) ;
- `Reset()` : réinitialise le contexte global.
**Sélection demandée ≠ version active**
Une valeur de configuration n'est **jamais** présentée comme version réellement
active : seule la base **détectée et validée** (`DetectedVersion`) détermine la
base utilisée. Tant que `SetDetectedVersion` n'a pas été appelé, la version
détectée reste `Unknown`, même si une sélection forcée (`Batik` / `Omika`) est
configurée.
**Comment changer la sélection demandée ?**
| Méthode | Exemple |
|---|---|
| **Fichier de configuration** (recommandé pour l'API) | Modifier la clé `"Omika:DatabaseVersion"` dans `appsettings.json` (valeur `"Auto"`, `"Batik"` ou `"Omika"`) |
| **Variable d'environnement** | `Omika__DatabaseVersion=Batik` (la convention .NET `__` est lue automatiquement) |
| **Au démarrage du code** | `ApplicationRuntimeContext.Configure(DatabaseSchemaSelection.Batik)` ou `Configure("Auto")` |
Une valeur inconnue lève une erreur explicite au démarrage. En mode `Auto`,
toute version réelle détectée (Batik ou Omika) est acceptée. En mode forcé, la
version détectée doit correspondre à la sélection demandée ; dans le cas
contraire, `SetDetectedVersion` retourne un échec explicite et **aucune version
active n'est affirmée** (la version détectée reste `Unknown`).
## 9. État des modèles métier
Les **modèles physiques HFSQL** (rubriques brutes du schéma WINDEV) vivent dans
`Omika.Data/HfSql/Models` (voir § 7.1). `Omika.Core/Models`, qui accueillera les
**entités du domaine**, est volontairement vide. La prochaine phase devra :
1. s'appuyer sur l'inventaire machine-readable du schéma (déjà produit) et sur
les modèles physiques pour définir les entités du domaine (Client, Dossier,
Intervention, RDV, …) ;
2. distinguer les propriétés utiles au domaine des rubriques « zombie »
(conservées pour la lecture d'une base existante, à ne pas utiliser comme
référence dans le nouveau domaine) ;
3. définir les DTO exposés par l'API et consommés par l'application.
## 10. Commandes utiles
```powershell
# Restaurer les packages
dotnet restore .\Omika.slnx
# Compiler toute la solution (hôtes desktop + API + bibliothèques + tests)
dotnet build .\Omika.slnx
# Compiler uniquement l'API
dotnet build .\src\Omika.Api\Omika.Api.csproj
# Compiler le client desktop (Windows)
dotnet build .\src\Omika.App.Desktop\Omika.App.Desktop.csproj
# Compiler l'hôte Android (APK)
dotnet build .\src\Omika.App.Android\Omika.App.Android.csproj
# Compiler l'hôte iOS (simulateur) — nécessite macOS + Xcode
dotnet build .\src\Omika.App.iOS\Omika.App.iOS.csproj
# Lancer les tests unitaires
dotnet test .\tests\Omika.Tests\Omika.Tests.csproj
# Lancer l'API en développement
dotnet run --project .\src\Omika.Api\Omika.Api.csproj
# → http://localhost:5xxx/health (état de la solution)
# Lancer le client desktop
dotnet run --project .\src\Omika.App.Desktop\Omika.App.Desktop.csproj
```
## 11. Prochaines étapes
- [ ] Choisir et intégrer le provider HFSQL (.NET) dans `Omika.Data`.
- [ ] Enrichir le dictionnaire `docs/schema-compatibilite-batik-omika.json`
(liens, règles de migration) et régénérer les modèles physiques.
- [ ] Définir les entités métier dans `Omika.Core/Models`.
- [ ] Implémenter les endpoints métier de `Omika.Api`.
- [ ] Brancher l'application Avalonia sur l'API (client HTTP) et préparer la
navigation mobile.
- [ ] Ajouter un projet `Omika.App.Windows` si un client Windows dédié est requis
(le desktop actuel en tient déjà lieu).