Skip to content

Architecture

Detailed architecture documentation for the Virtufin API gateway.

System Overview

Virtufin API is a gRPC-based API gateway that provides:

  1. Service Discovery - Dynamic discovery via gRPC reflection
  2. Method Invocation - Transparent forwarding to backend services
  3. Event Streaming - gRPC streaming and Dapr pub/sub
  4. State Management - Dapr state store integration
  5. Multi-Protocol Support - REST, gRPC, and gRPC-Web

System Architecture

flowchart TB
    subgraph Clients["Clients"]
        CSharp[C# Client]
        Python[Python Client]
        Browser[Browser<br/>gRPC-Web]
        REST[REST Client<br/>Swagger]
    end

    subgraph ProtocolLayer[Protocol Layer]
        Shared[Dual Ports<br/>HTTP:5001 / gRPC:5002]
    end

    subgraph ApplicationLayer["Application Layer"]
        Kestrel[Kestrel Server]
        GatewaySvc[GatewayService]
        EventSubMgr[EventSubscriptionManager]
        SystemEventMgr[SystemEventManager]
        StateSvc[StateService]
    end

    subgraph Infrastructure["Infrastructure"]
        DaprClient[Dapr Client]
        ChannelPool[gRPC Channel Pool]
    end

    subgraph Backend["Backend Services"]
        WM[WorkManager<br/>:5002]
        WSM[WebSocketManager<br/>:5002]
    end

    subgraph Dapr["Dapr Sidecar"]
        State[Dapr State]
        PubSub[Dapr PubSub]
    end

    Clients --> ProtocolLayer
    ProtocolLayer --> ApplicationLayer
    ApplicationLayer --> Infrastructure
    Infrastructure --> Backend
    Infrastructure --> Dapr

Component Architecture

flowchart LR
    subgraph Clients
        C[C#]
        P[Python]
        B[Browser]
    end

    subgraph Gateway["Virtufin.Api"]
        G[gRPC Gateway]
        E[Event System]
        S[State Service]
    end

    subgraph Backends
        WM[WorkManager]
        WSM[WebSocketManager]
    end

    C --> G
    P --> G
    B --> G
    G --> WM
    G --> WSM
    G <--> S
    S <--> State
    E <--> PubSub

Component Details

Component Architecture

flowchart TB
    subgraph Clients[Clients]
        CSharp[C# Client]
        Python[Python Client]
        Browser[Browser gRPC-Web]
        REST[REST HTTP Swagger]
    end

    subgraph ProtocolLayer[Protocol Layer]
        Shared[Dual Ports<br/>HTTP:5001 / gRPC:5002]
    end

    subgraph ApplicationLayer[Application Layer]
        Kestrel[Kestrel Server]
        RESTCtrl[REST Controllers]
        GatewaySvc[GatewayService gRPC]
        ConfigSvc[ConfigService gRPC]
        GrpcReflSvc[GrpcReflectionService gRPC]
        EventSubMgr[EventSubscriptionManager]
        SystemEventMgr[SystemEventManager]
        StateSvc[StateService]
    end

    subgraph Infrastructure[Infrastructure]
        ChannelPool[GrpcChannelPool]
        DaprClient[DaprClient]
        ConfigLoader[ServicesConfigurationLoader]
    end

    subgraph Backend[Backend Services]
        WM[WorkManager :5002]
        WSM[WebSocketManager :5002]
    end

    subgraph Dapr[Dapr Sidecar]
        State[Dapr State]
        PubSub[Dapr PubSub]
    end

    Clients --> ProtocolLayer
    ProtocolLayer --> ApplicationLayer
    ApplicationLayer --> Infrastructure
    Infrastructure --> Backend
    Infrastructure --> Dapr

Data Flow Diagrams

flowchart TB
    subgraph Clients[Clients]
        CSharp[C# Client]
        Python[Python Client]
        Browser[Browser gRPC-Web]
        REST[REST HTTP Swagger]
    end

    subgraph ProtocolLayer[Protocol Layer]
        Shared[Dual Ports<br/>HTTP:5001 / gRPC:5002]
    end

    subgraph ApplicationLayer[Application Layer]
        Kestrel[Kestrel Server]
        RESTCtrl[REST Controllers]
        GatewaySvc[GatewayService gRPC]
        ConfigSvc[ConfigService gRPC]
        GrpcReflSvc[GrpcReflectionService gRPC]
        EventSubMgr[EventSubscriptionManager]
        SystemEventMgr[SystemEventManager]
        StateSvc[StateService]
    end

    subgraph Infrastructure[Infrastructure]
        ChannelPool[GrpcChannelPool]
        DaprClient[DaprClient]
        ConfigLoader[ServicesConfigurationLoader]
    end

    subgraph Backend[Backend Services]
        WM[WorkManager :5002]
        WSM[WebSocketManager :5002]
    end

    subgraph Dapr[Dapr Sidecar]
        State[Dapr State]
        PubSub[Dapr PubSub]
    end

    Clients --> ProtocolLayer
    ProtocolLayer --> ApplicationLayer
    ApplicationLayer --> Infrastructure
    Infrastructure --> Backend
    Infrastructure --> Dapr

Method Invocation (REST)

sequenceDiagram
    participant Client as curl
    participant Kestrel as Kestrel
    participant Gateway as GrpcGateway Extensions
    participant Pool as GrpcChannel Pool
    participant Backend as Backend Service :5002
    participant Reflection as gRPC Reflection

    Client->>Kestrel: HTTP/REST JSON
    Kestrel->>Gateway: JSON
    Gateway->>Pool: GetChannel(service)
    Pool->>Backend: gRPC Call
    Backend->>Reflection: Reflection Request
    Reflection-->>Backend: Service Descriptors
    Backend-->>Pool: Response
    Pool-->>Gateway: InvokeResponse
    Gateway-->>Kestrel: JSON
    Kestrel-->>Client: HTTP Response

gRPC Invocation

sequenceDiagram
    participant Client as C# Client
    participant Kestrel as Kestrel
    participant Gateway as Gateway Service
    participant Pool as GrpcChannel Pool
    participant Backend as Backend Service :5002

    Client->>Kestrel: gRPC
    Kestrel->>Gateway: InvokeRequest
    Gateway->>Pool: GetChannel(service)
    Pool->>Backend: gRPC Call
    Backend-->>Pool: Response
    Pool-->>Gateway: InvokeResponse
    Gateway-->>Kestrel: InvokeResponse
    Kestrel-->>Client: gRPC Response

Event Streaming (Dapr → gRPC)

flowchart LR
    Dapr[Dapr Sidecar] --> PubSub[PubSub Event]
    PubSub --> DaprStreamMgr[DaprStreamingSub Manager]
    DaprStreamMgr --> EventSubMgr[EventSub Manager]
    EventSubMgr --> GrpcClient[gRPC Client Subscribe]

Configuration Architecture

flowchart TB
    ConfigFile[services.json] --> Loader[ServicesConfigurationLoader]
    Loader --> ServicesConfig[ServicesConfiguration]
    ServicesConfig --> ChannelPool[GrpcChannelPool]
    ServicesConfig --> Reflection[GrpcReflectionService]
    ServicesConfig --> PubsubSvc[PubSubService]
    ServicesConfig --> State[StateService]
    ServicesConfig --> Config[ConfigService]

GrpcChannelPool

Location: Services/GrpcChannelPool.cs

The GrpcChannelPool maintains a thread-safe pool of gRPC channels, one per backend service.

Key Features: - Lazy channel creation (created on first access) - Channel reuse across invocations - LRU eviction when channel limit is reached - Automatic cleanup on application shutdown - Configuration-driven service endpoints

Flow:

sequenceDiagram
    participant Client
    participant Gateway as GatewayService
    participant Pool as GrpcChannelPool
    participant Cache as Channel Cache
    participant Backend as Backend Service

    Client->>Gateway: Invoke(service, method, data)
    Gateway->>Pool: GetChannel(serviceName)
    Pool->>Cache: Check cached?
    Cache-->>Pool: Not found
    Pool->>Pool: Create new GrpcChannel
    Pool->>Cache: Store in cache
    Cache-->>Pool: Channel
    Pool-->>Gateway: Channel
    Gateway->>Backend: gRPC Call
    Backend-->>Gateway: Response
    Gateway-->>Client: InvokeResponse

Configuration (services.json):

services:
  - name: workmanager
    host: localhost
    port: 5002
    protocol: grpc


GrpcReflectionService

Location: Services/GrpcReflectionService.cs

Provides dynamic service discovery and method invocation using gRPC reflection.

Key Features: - Fetches service descriptors from backend services via reflection - Caches descriptors per service for performance - Uses TypeRegistry for robust JSON↔Protobuf conversion - Handles well-known type descriptors

Class Structure:

Class Purpose
GrpcReflectionService Public API, manages cache instances
ServiceDescriptorCache Per-service cache, loads and parses descriptors
ServiceMethodInfo Method metadata (name, input/output types, streaming)
MethodSchema Schema for request/response types
FieldSchema Individual field definitions
GrpcCallResult Invocation result wrapper

Discovery Flow:

sequenceDiagram
    participant Client as GatewayClient
    participant Cache as ServiceDescriptorCache
    participant Reflection as gRPC Reflection
    participant Backend as Backend Service

    Client->>Cache: GetMethodsAsync(serviceName)
    Cache->>Cache: GetOrCreateCache()
    Cache->>Reflection: ServerReflectionInfo()
    Reflection->>Backend: ServerReflectionInfo()
    Backend-->>Reflection: FileDescriptorProtos
    Reflection-->>Cache: Descriptors
    Cache->>Cache: Parse and Index
    Cache-->>Client: List<MethodInfo>

Invocation Flow:

sequenceDiagram
    participant Client
    participant Gateway as GatewayService
    participant Cache as ServiceDescriptorCache
    participant Channel as gRPC Channel
    participant Backend as Backend Service

    Client->>Gateway: Invoke(request)
    Gateway->>Cache: GetOrCreateCache(service)
    Cache-->>Gateway: Cached descriptors
    Gateway->>Gateway: Create protobuf from JSON
    Gateway->>Channel: AsyncUnaryCall()
    Channel->>Backend: gRPC Request
    Backend-->>Channel: gRPC Response
    Channel-->>Gateway: Response bytes
    Gateway->>Gateway: Parse to JSON
    Gateway-->>Client: GrpcCallResult

Invocation Flow:

flowchart TD
    A[ExecuteCallAsync<br/>service, method, json] --> B[GetOrCreateCache<br/>EnsureLoadedAsync]
    B --> C[Find method by name]
    C --> D[Create protobuf message<br/>from JSON]
    D --> E[Create generic<br/>Method byte array byte array]
    E --> F[channel.CreateCallInvoker<br/>AsyncUnaryCall]
    F --> G[Parse response bytes<br/>to JSON]
    G --> H[Return GrpcCallResult]

GatewayService

Location: Services/GatewayService.cs

The main gRPC service implementing the Gateway protocol.

gRPC Methods:

Method Type Description
Invoke Unary Invoke a method on a backend service
ListServices Unary List all registered services
ListMethods Unary List methods for a service
Subscribe Server Streaming Subscribe to events

Invoke Flow:

flowchart TD
    A[Client calls<br/>Gateway.Invoke<br/>InvokeRequest] --> B[Extract request headers<br/>as metadata]
    B --> C[Call ReflectionService<br/>ExecuteCallAsync]
    C --> D[Map result to<br/>InvokeResponse]
    D --> E[Return to client]

EventSubscriptionManager

Location: Services/EventSubscriptionManager.cs

Manages event subscriptions for the Subscribe gRPC method.

Scope System: Subscriptions use hierarchical scopes for filtering:

graph TD
    global[global<br/>All events]
    service[service<br/>All events from service]
    topic[service/topic<br/>Specific topic]
    type[type<br/>By event type]
    typeService[type/service<br/>By type and service]

    global --> service
    global --> topic
    global --> type
    topic --> typeService
    type --> typeService

Subscription Flow:

sequenceDiagram
    participant Client
    participant SubMgr as EventSubscriptionManager
    participant Scope as Scope Dictionary

    Client->>SubMgr: Subscribe(request)
    SubMgr->>SubMgr: Generate subscriptionId
    SubMgr->>SubMgr: BuildScope(request)
    SubMgr->>Scope: Add subscription
    SubMgr->>SubMgr: Start health monitor
    SubMgr-->>Client: subscriptionId

Broadcast Flow:

sequenceDiagram
    participant Source
    participant SubMgr as EventSubscriptionManager
    participant Scope as Scopes
    participant Sub as Subscribers

    Source->>SubMgr: BroadcastEventAsync()
    SubMgr->>SubMgr: Create StreamEvent
    SubMgr->>SubMgr: GetMatchingScopes()
    loop For each matching scope
        SubMgr->>Scope: Get subscriptions
        loop For each subscription
            SubMgr->>Sub: Write event
        end
        SubMgr->>Scope: Remove dead subscriptions
    end

DaprStreamingSubscriptionManager

Location: Services/DaprStreamingSubscriptionManager.cs

Integrates Dapr pub/sub with Virtufin's event streaming.

Flow:

sequenceDiagram
    participant App as Virtufin.Api
    participant DaprMgr as DaprStreamingSubMgr
    participant Dapr as Dapr Sidecar
    participant SubMgr as EventSubscriptionManager

    App->>DaprMgr: StartAllSubscriptionsAsync()
    loop For each subscription
        DaprMgr->>Dapr: SubscribeAsync(topic, handler)
        Dapr-->>DaprMgr: Subscription
        DaprMgr->>SubMgr: BroadcastConnectionStatus(connected)
    end

    Note over Dapr,App: Normal operation
    Dapr->>DaprMgr: Message received
    DaprMgr->>SubMgr: BroadcastToTopicAsync()
    SubMgr->>SubMgr: Broadcast to gRPC streams
    DaprMgr-->>Dapr: TopicResponseAction.Success

Message Handler:

sequenceDiagram
    participant Dapr as Dapr Sidecar
    participant DaprMgr as DaprStreamingSubMgr
    participant SubMgr as EventSubscriptionManager

    Dapr->>DaprMgr: TopicMessage
    DaprMgr->>DaprMgr: Deserialize JSON
    DaprMgr->>SubMgr: BroadcastToTopicAsync()
    SubMgr->>SubMgr: Broadcast to gRPC clients
    DaprMgr-->>Dapr: TopicResponseAction.Success

SystemEventManager

Location: Services/EventSubscriptionManager.cs (same file)

Manages system-level event subscriptions (connection status, errors).

Event Types: - pubsub_subscription - Pub/sub connection status changes - Connection/disconnection notifications


Data Flow Diagrams

Method Invocation (REST)

sequenceDiagram
    participant Client as curl
    participant Kestrel as Kestrel
    participant Gateway as GrpcGateway Extensions
    participant Pool as GrpcChannel Pool
    participant Backend as Backend Service :5002
    participant Reflection as gRPC Reflection

    Client->>Kestrel: HTTP/REST JSON
    Kestrel->>Gateway: JSON
    Gateway->>Pool: GetChannel(service)
    Pool->>Backend: gRPC Call
    Backend->>Reflection: Reflection Request
    Reflection-->>Backend: Service Descriptors
    Backend-->>Pool: Response
    Pool-->>Gateway: InvokeResponse
    Gateway-->>Kestrel: JSON
    Kestrel-->>Client: HTTP Response

gRPC Invocation

sequenceDiagram
    participant Client as C# Client
    participant Kestrel as Kestrel
    participant Gateway as Gateway Service
    participant Pool as GrpcChannel Pool
    participant Backend as Backend Service :5002

    Client->>Kestrel: gRPC
    Kestrel->>Gateway: InvokeRequest
    Gateway->>Pool: GetChannel(service)
    Pool->>Backend: gRPC Call
    Backend-->>Pool: Response
    Pool-->>Gateway: InvokeResponse
    Gateway-->>Kestrel: InvokeResponse
    Kestrel-->>Client: gRPC Response

Event Streaming (Dapr → gRPC)

flowchart LR
    Dapr[Dapr Sidecar] -->|Pub/Sub| DaprMgr[DaprStreamingSub Manager]
    DaprMgr -->|event| EventSubMgr[EventSub Manager]
    DaprMgr --> Removed[(removed)]
    EventSubMgr --> GrpcClient[gRPC Client Subscribe]

Configuration Architecture

flowchart TB
    ConfigFile[services.json] --> Loader[ServicesConfigurationLoader]
    Loader --> ServicesConfig[ServicesConfiguration]
    ServicesConfig --> ChannelPool[GrpcChannelPool]
    ServicesConfig --> Reflection[GrpcReflectionService]
    ServicesConfig --> DaprMgr[DaprStreamingSubscriptionManager]
    ServicesConfig --> State[StateService]
    ServicesConfig --> Config[ConfigService]

Health Checks

Check Component Status When
DaprHealthCheck DaprClient Dapr sidecar is reachable
GrpcChannelPoolHealthCheck GrpcChannelPool Channel pool is initialized

Both checks run on /health endpoint.