Séparation de l'interface utilisateur et de la logique métier dans les applications .NET de bureau avec gRPC et IPC
Dans les applications de bureau WPF ou Avalonia UI, la logique métier est étroitement couplée au thread de l'interface utilisateur. Les blocages de rendu ou les plantages XAML paralysent l'ensemble du système : les minuteries se réinitialisent, les requêtes API sont interrompues et l'état est perdu. Ceci est critique pour les tâches en arrière-plan comme la gestion domotique, le blocage de processus ou les lecteurs multimédias.
La solution consiste à séparer en un backend sans interface (Worker Service) et un client léger (UI). Ils communiquent via gRPC avec streaming côté serveur, garantissant l'indépendance des processus.
Architecture : Deux processus, communication sécurisée
Backend (Axorith.Host) — un Worker Service .NET, construit en tant que WinExe. Responsable de :
- Stocker l'état de session.
- Minuteries et orchestration des modules.
- Intégration avec Home Assistant, Spotify, surveillance de processus ETW.
- Gestion du navigateur via Named Pipes.
Consommation au repos : 20–30 Mo de RAM. Fonctionne de manière autonome, indépendamment de l'interface utilisateur.
Frontend (Axorith.Client) sur Avalonia UI avec ReactiveUI. Seulement la visualisation et la saisie. L'interface utilisateur peut être tuée dans le Gestionnaire des tâches — le backend continue de fonctionner.
Dépendances des couches :
- Client et Host référencent Axorith.Contracts (proto gRPC).
- Core partagé pour le moteur de session.
- Shared pour les utilitaires et le code dépendant de la plateforme.
- Les modules (plugins) dépendent du Sdk.
gRPC avec streaming côté serveur pour la réactivité
gRPC est choisi pour les contrats protobuf et le streaming. Proto pour les mises à jour des paramètres :
syntax = "proto3";
package axorith.contracts;
service ModulesService {
rpc StreamSettingUpdates (StreamSettingUpdatesRequest) returns (stream SettingUpdate);
}
message SettingUpdate {
string module_instance_id = 1;
string setting_key = 2;
SettingProperty property = 3;
oneof value {
string string_value = 4;
bool bool_value = 5;
int32 int_value = 6;
double number_value = 7;
ChoiceList choice_list = 8;
}
}
Sur l'hôte : SettingUpdateBroadcaster avec Rx.NET Subject. Les changements dans les modules (par exemple, changement de piste) sont poussés vers le flux.
Sur le client : Un flux est ouvert au démarrage, les mises à jour vont vers le ViewModel. ReactiveUI assure un redessin instantané sans délai.
Avantages par rapport au polling :
- Aucun délai (push au lieu du polling).
- Utilisation efficace du trafic.
- Le typage fort protobuf empêche la désynchronisation.
IPC sécurisé au lieu de TCP
Les ports locaux sont vulnérables : conflits, accès par virus, fenêtres contextuelles du pare-feu. Passage à l'IPC :
| Plateforme | Transport | ACL |
|-----------|-----------|-----|
| Windows | Named Pipes (ListenNamedPipe) | Utilisateur actuel uniquement |
| Linux/macOS | Unix Domain Sockets (ListenUnixSocket) | Permissions de fichier |
L'hôte écrit le point de terminaison dans %AppData%/Axorith/host-info.json. Le client lit et se connecte. Isolation complète des processus.
Cycle de vie et expérience utilisateur
Approche client d'abord :
- Lancer Client.exe.
- Vérifier host-info.json, démarrer automatiquement Host en arrière-plan.
- Fermer la fenêtre — réduire dans la zone de notification (mode --tray).
- Icône de la zone de notification pour redémarrer/arrêter Host ou restaurer l'interface utilisateur.
Détection de rupture de connexion : HTTP/2 Keep-Alive détecte les interruptions de flux. L'hôte envoie une notification système (Windows Toast / D-Bus).
Test de résilience :
- Session "Travail" : Spotify en lecture, YouTube bloqué.
- Tuer Client dans le Gestionnaire des tâches.
- Résultat : backend vivant, session continue.
Défis d'implémentation
- Contrats : Chaque changement nécessite de régénérer le proto, de mettre à jour les deux côtés.
- Gestion des versions : L'intercepteur gRPC vérifie
x-axorith-version. Une incompatibilité entraîne FailedPrecondition + mise à jour automatique. - Asynchronie : Tous les appels sont basés sur Task, gérant les délais d'attente, les erreurs réseau sur localhost.
Points clés à retenir
- La séparation des processus élimine le point de défaillance unique dans le thread de l'interface utilisateur.
- Le streaming gRPC + Rx.NET fournit une interface utilisateur réactive sans polling.
- L'IPC (Named Pipes/UDS) garantit la sécurité sans ports.
- L'expérience utilisateur client d'abord masque la complexité pour l'utilisateur.
- Adapté aux applications 24/7 avec gestion système.
— Editorial Team
Aucun commentaire pour le moment.