Separando la Interfaz de Usuario y la Lógica de Negocio en Aplicaciones de Escritorio .NET con gRPC e IPC
En aplicaciones de escritorio con WPF o Avalonia UI, la lógica de negocio está fuertemente acoplada al hilo de la interfaz. Los bloqueos en el renderizado o las caídas de XAML detienen todo el sistema: los temporizadores se reinician, las peticiones a la API se interrumpen y se pierde el estado. Esto es crítico para tareas en segundo plano como la gestión de hogares inteligentes, el bloqueo de procesos o los reproductores multimedia.
La solución es dividir en un backend sin interfaz (Servicio Worker) y un cliente ligero (UI). Se comunican mediante gRPC con streaming del lado del servidor, garantizando la independencia de procesos.
Arquitectura: Dos Procesos, Comunicación Segura
Backend (Axorith.Host) — un Servicio Worker de .NET, compilado como WinExe. Responsable de:
- Almacenar el estado de la sesión.
- Temporizadores y orquestación de módulos.
- Integración con Home Assistant, Spotify, monitorización de procesos ETW.
- Gestión del navegador mediante Named Pipes.
Consumo en reposo: 20–30 MB de RAM. Se ejecuta de forma autónoma, independiente de la UI.
Frontend (Axorith.Client) en Avalonia UI con ReactiveUI. Solo visualización y entrada. La interfaz puede cerrarse en el Administrador de tareas — el backend sigue funcionando.
Dependencias de capas:
- Cliente y Host referencian Axorith.Contracts (proto gRPC).
- Core compartido para el motor de sesión.
- Shared para utilidades y código dependiente de la plataforma.
- Los módulos (plugins) dependen del Sdk.
gRPC con Streaming del Lado del Servidor para Reactividad
gRPC se elige por los contratos protobuf y el streaming. Proto para actualizaciones de configuración:
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;
}
}
En el host: SettingUpdateBroadcaster con Rx.NET Subject. Los cambios en los módulos (por ejemplo, cambio de pista) se envían al stream.
En el cliente: Se abre un stream al inicio, las actualizaciones van al ViewModel. ReactiveUI garantiza el redibujado instantáneo sin retrasos.
Ventajas sobre el polling:
- Sin retrasos (push en lugar de polling).
- Uso eficiente del tráfico.
- El tipado fuerte de protobuf evita la desincronización.
IPC Seguro en Lugar de TCP
Los puertos locales son vulnerables: conflictos, acceso de virus, ventanas emergentes del firewall. Cambiando a IPC:
| Plataforma | Transporte | ACL |
|-----------|-----------|-----|
| Windows | Named Pipes (ListenNamedPipe) | Solo el usuario actual |
| Linux/macOS | Unix Domain Sockets (ListenUnixSocket) | Permisos de archivo |
El host escribe el endpoint en %AppData%/Axorith/host-info.json. El cliente lee y se conecta. Aislamiento completo de procesos.
Ciclo de Vida y Experiencia de Usuario
Enfoque cliente-primero:
- Lanzar Client.exe.
- Verificar host-info.json, iniciar Host automáticamente en segundo plano.
- Cerrar la ventana — minimizar a la bandeja (modo --tray).
- Icono en la bandeja para reiniciar/detener Host o restaurar la UI.
Detección de caída de conexión: HTTP/2 Keep-Alive detecta interrupciones del stream. El host envía una notificación del sistema (Windows Toast / D-Bus).
Prueba de resiliencia:
- Sesión de "trabajo": Spotify reproduciendo, YouTube bloqueado.
- Cerrar Cliente en el Administrador de tareas.
- Resultado: backend vivo, la sesión continúa.
Desafíos de Implementación
- Contratos: Cada cambio requiere regenerar el proto, actualizar ambos lados.
- Control de versiones: El Interceptor de gRPC verifica
x-axorith-version. La discrepancia resulta en FailedPrecondition + actualización automática. - Asincronía: Todas las llamadas son basadas en Task, manejando tiempos de espera, errores de red en localhost.
Conclusiones Clave
- La separación de procesos elimina el punto único de fallo en el hilo de la UI.
- El streaming de gRPC + Rx.NET proporciona una UI reactiva sin polling.
- IPC (Named Pipes/UDS) garantiza seguridad sin puertos.
- La experiencia de usuario cliente-primero oculta la complejidad al usuario.
- Adecuado para aplicaciones 24/7 con gestión del sistema.
— Editorial Team
Aún no hay comentarios.