Architecture
Detailed architecture documentation for the Virtufin API gateway.
System Overview
Virtufin API is a gRPC-based API gateway that provides:
- Service Discovery - Dynamic discovery via gRPC reflection
- Method Invocation - Transparent forwarding to backend services
- Event Streaming - gRPC streaming and Dapr pub/sub
- State Management - Dapr state store integration
- 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.