SubgraphActor
The SubgraphActor wraps an entire inner Network as a single Actor, allowing a graph to be embedded inside another graph as a composable unit. This is the foundation of hierarchical workflow composition in reflow_network.
Architecture
graph TB
subgraph "Parent Network"
EXT_IN[External Inport] --> SA[SubgraphActor]
SA --> EXT_OUT[External Outport]
end
subgraph "SubgraphActor (inner Network)"
IN_A[Actor A] --> IN_B[Actor B]
IN_B --> OB[OutportBridge]
end
EXT_IN -.->|inport_map| IN_A
OB -.->|external_sender| EXT_OUT
The parent network treats the SubgraphActor as an opaque actor with external inports and outports. Internally, it manages a full Network with its own actors, connectors, and message routing.
SubgraphActor Struct
#![allow(unused)] fn main() { pub struct SubgraphActor { inner_network: Arc<Mutex<Network>>, inport_map: HashMap<String, (String, String)>, // ext_port → (actor_id, port_name) outport_map: HashMap<String, (String, String)>, // ext_port → (actor_id, port_name) inports: Port, outports: Port, load: Arc<ActorLoad>, shutdown_tx: Arc<tokio::sync::watch::Sender<bool>>, shutdown_rx: tokio::sync::watch::Receiver<bool>, } }
inner_network— the wrappedNetworkcontaining all inner actors and connectionsinport_map/outport_map— maps external port names to(inner_actor_id, inner_port_name)tuplesinports/outports— the external ports exposed to the parent networkshutdown_tx/shutdown_rx— tokio watch channel for graceful termination
Constructing from GraphExport
The primary constructor from_graph_export() builds a SubgraphActor from a serialized graph:
- Creates a new inner
Network - Registers all actors from the
actorsmap - Adds nodes and internal connections from the graph export
- Inport mapping — maps each external inport to
(inner_node_id, inner_port_name)for direct message injection - Outport bridging — for each external outport, creates an
OutportBridgeactor inside the inner network, connected to the source actor's port via a standardConnector
#![allow(unused)] fn main() { let actors: HashMap<String, Arc<dyn Actor>> = /* component instances */; let subgraph = SubgraphActor::from_graph_export(graph_export, actors)?; // subgraph implements Actor — add it to a parent Network as any other actor }
OutportBridge
The OutportBridge is a lightweight internal actor that forwards messages from an inner actor's outport to the SubgraphActor's external outport channel:
#![allow(unused)] fn main() { struct OutportBridge { external_sender: flume::Sender<Packet>, // → SubgraphActor outport external_port_name: String, inner_port_name: String, inports: Port, outports: Port, load: Arc<ActorLoad>, } }
Bridge actors are registered inside the inner network with generated IDs (__outport_bridge_{ext_port}) and connected to source actors via standard Connectors. The bridge's create_process() receives packets on its inport, extracts the message by inner_port_name, wraps it with external_port_name, and sends it via external_sender.
Boundary Mapping
Inbound flow — The create_process() loop receives packets on external inports, looks up the target (actor_id, port) in inport_map, and calls network.send_to_actor() to inject the message into the inner network.
Outbound flow — Inner actors send messages through normal Connectors to OutportBridge actors. Each bridge extracts the message and sends it via external_sender, making it appear on the SubgraphActor's external outport.
Actor Trait Implementation
The SubgraphActor implements Actor:
get_behavior()— returns a no-op closure; all routing is handled increate_process()get_inports()/get_outports()— return the external port pairscreate_process()— starts the inner network, then runs an inbound routing loop usingtokio::select!with the shutdown signalshutdown()— signals the routing loop to stop and shuts down the inner network
Lifecycle
create_process()starts the inner network (initializing all inner actors, connectors, and OutportBridge actors) and begins the inbound routing loop- The inner network runs its actors concurrently
shutdown()sendstrueon the watch channel, breaking the routing loop, and callsnetwork.shutdown()
Next Steps
- Multi-Graph Composition - Workspace discovery and distributed composition
- Distributed Networks - Cross-network execution with PeerMesh
- Actor Model - Actor trait and port system