Back to Home

gRPC IPC for .NET desktops: separating UI logic

The article describes the architecture of a .NET desktop application with separation of UI and business logic via gRPC and IPC. Headless backend ensures reliability, thin client — UX. Examples of proto, Rx.NET and process lifecycle.

gRPC and IPC in .NET: reliable desktop architecture without monolith
Advertisement 728x90

Splitting UI and Business Logic in .NET Desktop Apps with gRPC and IPC

In WPF or Avalonia UI desktop applications, business logic is tightly coupled to the UI thread. Rendering hangs or XAML crashes bring the entire system to a halt: timers reset, API requests are interrupted, and state is lost. This is critical for background tasks like smart home management, process blocking, or media players.

The solution is splitting into a headless backend (Worker Service) and a thin client (UI). They communicate via gRPC with server-side streaming, ensuring process independence.

Architecture: Two Processes, Secure Communication

Backend (Axorith.Host) — a .NET Worker Service, built as a WinExe. Responsible for:

Google AdInline article slot
  • Storing session state.
  • Timers and module orchestration.
  • Integration with Home Assistant, Spotify, ETW process monitoring.
  • Browser management via Named Pipes.

Idle consumption: 20–30 MB RAM. Runs autonomously, independent of the UI.

Frontend (Axorith.Client) on Avalonia UI with ReactiveUI. Only visualization and input. The UI can be killed in Task Manager — the backend continues running.

Layer dependencies:

Google AdInline article slot
  • Client and Host reference Axorith.Contracts (gRPC proto).
  • Shared Core for the session engine.
  • Shared for utilities and platform-dependent code.
  • Modules (plugins) depend on the Sdk.

gRPC with Server-Side Streaming for Reactivity

gRPC is chosen for protobuf contracts and streaming. Proto for setting updates:

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;
    }
}

On the host: SettingUpdateBroadcaster with Rx.NET Subject. Changes in modules (e.g., track switching) are pushed to the stream.

On the client: A stream is opened on startup, updates go to the ViewModel. ReactiveUI ensures instant redrawing without lag.

Google AdInline article slot

Advantages over polling:

  • No delays (push instead of polling).
  • Efficient traffic usage.
  • Strong protobuf typing prevents desynchronization.

Secure IPC Instead of TCP

Local ports are vulnerable: conflicts, virus access, firewall pop-ups. Switching to IPC:

| Platform | Transport | ACL |

|-----------|-----------|-----|

| Windows | Named Pipes (ListenNamedPipe) | Current user only |

| Linux/macOS | Unix Domain Sockets (ListenUnixSocket) | File permissions |

The host writes the endpoint to %AppData%/Axorith/host-info.json. The client reads and connects. Complete process isolation.

Lifecycle and UX

Client-first approach:

  • Launch Client.exe.
  • Check host-info.json, auto-start Host in the background.
  • Closing the window — minimize to tray (--tray mode).
  • Tray icon for restart/stop Host or restoring the UI.

Connection drop detection: HTTP/2 Keep-Alive detects stream breaks. The host sends a system notification (Windows Toast / D-Bus).

Resilience test:

  • "Work" session: Spotify playing, YouTube blocked.
  • Kill Client in Task Manager.
  • Result: backend alive, session continues.

Implementation Challenges

  • Contracts: Every change requires regenerating proto, updating both sides.
  • Versioning: gRPC Interceptor checks x-axorith-version. Mismatch results in FailedPrecondition + auto-update.
  • Asynchrony: All calls are Task-based, handling timeouts, network errors on localhost.

Key Takeaways

  • Process separation eliminates single point of failure in the UI thread.
  • gRPC streaming + Rx.NET deliver reactive UI without polling.
  • IPC (Named Pipes/UDS) ensures security without ports.
  • Client-first UX hides complexity from the user.
  • Suitable for 24/7 applications with system management.

— Editorial Team

Advertisement 728x90

Read Next