Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GitHub

This documentation is part of the "Projects with Books" initiative at zenOSmosis.

The source code for this project is available on GitHub.

Layered Architecture

Relevant source files

Purpose and Scope

This page documents the separation of concerns in rust-muxio's three-layer architecture: the core multiplexing layer, the RPC abstraction layer, and the transport implementation layer. Each layer has distinct responsibilities and well-defined interfaces, enabling modularity, testability, and platform independence.

For information about the specific binary protocol used in the core layer, see Binary Framing Protocol. For details on how to create service definitions in the RPC layer, see Creating Service Definitions. For information about specific transport implementations, see Transport Implementations.


Architectural Overview

The rust-muxio system is structured as three independent layers, each building upon the previous one without creating tight coupling. This design allows developers to use only the layers they need and to implement custom components at any level.

Sources: Cargo.toml:19-31 README.md:16-23 extensions/README.md

graph TB
    subgraph TransportLayer["Transport Layer (Platform-Specific)"]
TokioServer["muxio-tokio-rpc-server\nRpcServer struct"]
TokioClient["muxio-tokio-rpc-client\nRpcClient struct"]
WasmClient["muxio-wasm-rpc-client\nRpcWasmClient struct"]
end
    
    subgraph RpcLayer["RPC Abstraction Layer (Transport-Agnostic)"]
RpcService["muxio-rpc-service\nRpcMethodPrebuffered trait"]
CallerInterface["muxio-rpc-service-caller\nRpcServiceCallerInterface trait"]
EndpointInterface["muxio-rpc-service-endpoint\nRpcServiceEndpointInterface trait"]
end
    
    subgraph CoreLayer["Core Multiplexing Layer (Runtime-Agnostic)"]
RpcDispatcher["muxio/src/rpc_dispatcher.rs\nRpcDispatcher struct"]
RpcRequest["muxio/src/rpc_request_response.rs\nRpcRequest/RpcResponse"]
BinaryFraming["muxio/src/rpc_dispatcher.rs\nBinary framing protocol"]
end
    
 
   TokioServer --> CallerInterface
 
   TokioServer --> EndpointInterface
 
   TokioClient --> CallerInterface
 
   WasmClient --> CallerInterface
    
 
   CallerInterface --> RpcService
 
   EndpointInterface --> RpcService
    
 
   RpcService --> RpcDispatcher
 
   CallerInterface --> RpcDispatcher
 
   EndpointInterface --> RpcDispatcher
    
 
   RpcDispatcher --> RpcRequest
 
   RpcDispatcher --> BinaryFraming

Layer 1: Core Multiplexing Layer

The core layer, contained entirely within the muxio crate, provides transport-agnostic and runtime-agnostic stream multiplexing. This layer has zero knowledge of RPC semantics, serialization formats, or network transports.

Core Components

ComponentFile PathResponsibility
RpcDispatchermuxio/src/rpc_dispatcher.rsManages concurrent request/response correlation and frame routing
RpcRequestmuxio/src/rpc_request_response.rsDefines request structure with method ID, params, and payload
RpcResponsemuxio/src/rpc_request_response.rsDefines response structure with result or error
RpcHeadermuxio/src/rpc_request_response.rsWraps requests/responses with metadata
Binary framingmuxio/src/rpc_dispatcher.rsLow-level protocol for chunking and reassembling byte streams

Key Characteristics

The core layer operates exclusively on raw bytes (Vec<u8>). It provides callbacks for receiving data and functions for sending data, but never interprets the semantic meaning of the data. The RpcDispatcher handles:

  • Assigning unique request IDs for correlation
  • Multiplexing multiple concurrent requests over a single connection
  • Routing incoming responses to the correct waiting caller
  • Fragmenting large payloads into transmission frames
  • Reassembling frames into complete messages

Sources: muxio/src/rpc_dispatcher.rs muxio/src/rpc_request_response.rs README.md:28-29


Layer 2: RPC Abstraction Layer

The RPC layer provides type-safe abstractions for defining and invoking remote procedures without dictating transport implementation. This layer consists of three cooperating crates.

graph TB
    subgraph ServiceDefinition["muxio-rpc-service"]
RpcMethodPrebuffered["RpcMethodPrebuffered trait\nMETHOD_ID: u64\nencode_request()\ndecode_request()\nencode_response()\ndecode_response()"]
end
    
    subgraph CallerSide["muxio-rpc-service-caller"]
CallerInterface["RpcServiceCallerInterface trait\ncall_prebuffered()\nget_dispatcher()"]
CallImpl["RpcCallPrebuffered trait\ncall() - default implementation"]
end
    
    subgraph EndpointSide["muxio-rpc-service-endpoint"]
EndpointInterface["RpcServiceEndpointInterface trait\nregister_prebuffered()\ndispatch_request()"]
HandlerRegistry["Method ID → Handler mapping"]
end
    
 
   RpcMethodPrebuffered --> CallImpl
 
   RpcMethodPrebuffered --> HandlerRegistry
 
   CallerInterface --> CallImpl
 
   EndpointInterface --> HandlerRegistry

RPC Layer Components

Trait Responsibilities

RpcMethodPrebuffered (defined in extensions/muxio-rpc-service)

This trait defines the contract for a single RPC method. Each implementation provides:

  • A compile-time constant METHOD_ID generated from the method name
  • Encoding/decoding functions for request parameters
  • Encoding/decoding functions for response data

RpcServiceCallerInterface (defined in extensions/muxio-rpc-service-caller)

This trait abstracts the client-side capability to invoke RPC methods. Any type implementing this trait can:

  • Send prebuffered requests via call_prebuffered()
  • Access the underlying RpcDispatcher for low-level operations
  • Be used interchangeably across native and WASM clients

RpcServiceEndpointInterface (defined in extensions/muxio-rpc-service-endpoint)

This trait abstracts the server-side capability to handle RPC requests. Any type implementing this trait can:

  • Register handler functions via register_prebuffered()
  • Route incoming requests to appropriate handlers based on METHOD_ID
  • Execute handlers and return responses through the dispatcher

Separation of Concerns

The RPC layer enforces a clean separation:

ConcernResponsible Component
Method signature and data formatRpcMethodPrebuffered implementation
Client invocation mechanicsRpcServiceCallerInterface implementation
Server dispatch mechanicsRpcServiceEndpointInterface implementation
Request correlation and multiplexingCore layer RpcDispatcher
Network transmissionTransport layer implementations

Sources: extensions/muxio-rpc-service extensions/muxio-rpc-service-caller extensions/muxio-rpc-service-endpoint README.md:46-49


Layer 3: Transport Implementation Layer

The transport layer provides concrete implementations of the RPC abstraction layer interfaces for specific runtime environments and network transports. Each implementation handles platform-specific concerns like connection management, state tracking, and async runtime integration.

graph TB
    subgraph TokioServerImpl["muxio-tokio-rpc-server"]
RpcServer["RpcServer struct\nserve_with_listener()\nendpoint() → RpcEndpoint"]
RpcEndpoint["RpcEndpoint struct\nimplements RpcServiceEndpointInterface\nregister_prebuffered()"]
AxumWs["Axum WebSocket handler\ntokio-tungstenite integration"]
end
    
    subgraph TokioClientImpl["muxio-tokio-rpc-client"]
RpcClient["RpcClient struct\nimplements RpcServiceCallerInterface\nnew(host, port)\nset_state_change_handler()"]
ClientWs["tokio-tungstenite WebSocket\nConnection management"]
end
    
    subgraph WasmClientImpl["muxio-wasm-rpc-client"]
RpcWasmClient["RpcWasmClient struct\nimplements RpcServiceCallerInterface\nnew(url)\nwasm-bindgen bridge"]
BrowserWs["JavaScript WebSocket API\nvia wasm-bindgen"]
end
    
 
   RpcServer --> RpcEndpoint
 
   RpcServer --> AxumWs
 
   RpcClient --> ClientWs
 
   RpcWasmClient --> BrowserWs

Transport Implementations

Implementation Comparison

Featuremuxio-tokio-rpc-servermuxio-tokio-rpc-clientmuxio-wasm-rpc-client
RuntimeTokio asyncTokio asyncBrowser event loop
TransportAxum + tokio-tungstenitetokio-tungsteniteJavaScript WebSocket
InterfaceRpcServiceEndpointInterfaceRpcServiceCallerInterfaceRpcServiceCallerInterface
State trackingBuilt-inRpcTransportState enumRpcTransportState enum
PlatformNative (server-side)Native (client-side)WebAssembly (browser)

Transport Layer Responsibilities

Each transport implementation handles:

  1. Connection Lifecycle : Establishing, maintaining, and closing connections
  2. State Management : Tracking connection state and notifying callbacks via set_state_change_handler()
  3. Byte Transport : Reading from and writing to the underlying socket
  4. Dispatcher Integration : Creating an RpcDispatcher and wiring its callbacks to network I/O
  5. Error Propagation : Translating transport errors to RPC errors

Sources: extensions/muxio-tokio-rpc-server extensions/muxio-tokio-rpc-client extensions/muxio-wasm-rpc-client README.md:36-40


Layer Interaction and Data Flow

The following diagram traces how a single RPC call flows through all three layers, from application code down to the network and back:

sequenceDiagram
    participant App as "Application Code"
    participant Method as "RpcMethodPrebuffered\n(Layer 2: RPC)"
    participant Caller as "RpcServiceCallerInterface\n(Layer 2: RPC)"
    participant Dispatcher as "RpcDispatcher\n(Layer 1: Core)"
    participant Transport as "Transport Implementation\n(Layer 3)"
    participant Network as "WebSocket\nConnection"
    
    App->>Method: Add::call(rpc_client, [1.0, 2.0, 3.0])
    Method->>Method: encode_request() → Vec<u8>
    Method->>Caller: call_prebuffered(METHOD_ID, request_bytes)
    Caller->>Dispatcher: dispatch_request(REQUEST_ID, METHOD_ID, bytes)
    Dispatcher->>Dispatcher: Store pending request with REQUEST_ID
    Dispatcher->>Dispatcher: Serialize to binary frames
    Dispatcher->>Transport: send_bytes_callback(frame_bytes)
    Transport->>Network: Write binary data
    
    Network->>Transport: Receive binary data
    Transport->>Dispatcher: receive_bytes(frame_bytes)
    Dispatcher->>Dispatcher: Reassemble frames
    Dispatcher->>Dispatcher: Match REQUEST_ID to pending request
    Dispatcher->>Caller: Response ready
    Caller->>Method: decode_response(response_bytes)
    Method->>App: Return typed result: 6.0

Layer Boundaries

The boundaries between layers are enforced through well-defined interfaces:

BoundaryInterfaceDirection
Application → RPCRpcMethodPrebuffered::call()Typed parameters → Vec<u8>
RPC → Corecall_prebuffered() on RpcServiceCallerInterfaceVec<u8> + METHOD_ID → Request correlation
Core → TransportCallbacks (send_bytes_callback, receive_bytes)Binary frames ↔ Network I/O
Transport → NetworkPlatform-specific APIsRaw socket operations

Sources: extensions/muxio-rpc-service-caller/src/caller_interface.rs extensions/muxio-rpc-service-endpoint/src/endpoint_interface.rs muxio/src/rpc_dispatcher.rs


Benefits of Layered Separation

Modularity

Each layer can be developed, tested, and evolved independently. Changes to the binary framing protocol in Layer 1 do not require modifications to Layer 2 or Layer 3 code, as long as the callback interface remains stable.

Testability

Layers can be tested in isolation:

  • Core layer : Unit tests with mock callbacks can verify frame reassembly without network I/O
  • RPC layer : Integration tests can use in-memory transports to verify method dispatch
  • Transport layer : Integration tests can verify connection management against real servers
graph TB
    SharedDef["example-muxio-rpc-service-definition\nAdd, Mult, Echo methods\nRpcMethodPrebuffered implementations"]
SharedDef --> TokioClient["muxio-tokio-rpc-client\nNative Tokio runtime"]
SharedDef --> WasmClient["muxio-wasm-rpc-client\nBrowser WebAssembly"]
SharedDef --> TokioServer["muxio-tokio-rpc-server\nServer endpoint handlers"]
TokioClient --> NativeApp["Native Application"]
WasmClient --> BrowserApp["Browser Application"]
TokioServer --> ServerApp["Server Application"]

Platform Independence

The same service definition can be used across all platforms because Layers 1 and 2 have no platform-specific dependencies:

Extensibility

New transport implementations can be added without modifying existing code:

  • Implement RpcServiceCallerInterface for client-side transports
  • Implement RpcServiceEndpointInterface for server-side transports
  • Use the same service definitions and core dispatcher logic

Examples of potential future transports:

  • HTTP/2 with binary frames
  • Unix domain sockets
  • Named pipes
  • In-process channels for testing

Sources: README.md:34-35 README.md:42-52 extensions/README.md


Code Organization by Layer

The workspace structure directly reflects the layered architecture:

LayerCratesLocation
Core (Layer 1)muxioRoot directory
RPC Abstraction (Layer 2)muxio-rpc-service
muxio-rpc-service-caller
muxio-rpc-service-endpointextensions/
Transport (Layer 3)muxio-tokio-rpc-server
muxio-tokio-rpc-client
muxio-wasm-rpc-clientextensions/
Service Definitionsexample-muxio-rpc-service-definitionexamples/
Testing Utilitiesmuxio-ext-testextensions/

Dependency Graph

This dependency structure ensures that:

  • The core has no dependencies on higher layers
  • The RPC abstraction layer has no knowledge of transport implementations
  • Transport implementations depend on both core and RPC layers
  • Service definitions depend only on muxio-rpc-service

Sources: Cargo.toml:19-31 Cargo.toml:40-47

Dismiss

Refresh this wiki

Enter email to refresh