The previous article, App-to-app communication in io.Connect: practical guidance, looked at the main communication mechanisms in more detail and compared how they behave. This article is more decision oriented: when you are building a workflow, which mechanism should you actually choose?
The short answer is: start with the user interaction pattern, not the API. Once you know whether you need shared state, a direct service call, a workflow handoff, a live stream, or scoped launch state, the right io.Connect mechanism usually becomes much easier to pick.
io.Connect gives you several ways to move data, context, and actions between apps. They overlap intentionally, because different workflows need different levels of coupling, scope, persistence, and user control.
Here is the quick map before going into the decision rules.
The 8 Data Sharing Mechanisms
| # | Mechanism | Pattern | One-liner |
|---|---|---|---|
| 1 | Pub/Sub | Legacy | Topic-based broadcast. No persistence, no discovery - prefer higher-level APIs for new work |
| 2 | Shared Contexts | Broadcast | Named global key/value object - great for “selected client”, “current order”, “active instrument” |
| 3 | Color Channels | Broadcast + User-Driven | Shared Contexts plus a Channel Selector UI. Best when users decide which apps link together |
| 4 | Workspace Contexts | Broadcast | Apps inside a Workspace share state - scoped, portable, and persistable in layouts |
| 5 | Interop Methods | Service Call | Explicit, service-like capability. The caller knows exactly what it needs. Supports targeting |
| 6 | Intents | Workflow Handoff | Raise an action, not a target. Platform finds handlers, can launch an app, lets user choose |
| 7 | Streams | Streaming | Continuous real-time data push. Best for live alerts, telemetry, producer->many-subscribers |
| 8 | Window Context | Scoped | Per-window state and launch context. Best for restoring or initialising a specific window |
Think in Patterns Before APIs
The most common mistake is to start from the API name instead of the workflow shape. In practice, the better question is not “Should I use Interop or Contexts?”, but “What kind of interaction am I trying to create for the user?”
Use the pattern first, then select the lightest mechanism that supports it.
| Pattern | What it means | Mechanism(s) |
|---|---|---|
| Service Call | Call a known function in another app and get a result back | Interop Methods; Streams (for continuous updates) |
| Shared State | A named object many apps observe | Shared Contexts (developer-driven); Channels (user-driven grouping UI) |
| Workflow Handoff | Raise “handle this action” - loose coupling, user choice, app launch | Intents |
| Scoped Context | Local state containers | Window Context (per-window); Workspace Context (per-workspace) |
| Legacy Compatibility | Topic bus for porting existing integrations | Pub/Sub |
Rule of thumb: prefer the highest-level mechanism that matches the UX. Start with the narrowest scope that satisfies the workflow.
Practical tips
We can say these mechanisms fall into two families: Broadcast and Invocation & Streaming. The first is about sharing state across apps, the second is about triggering actions or delivering data flows between them. The tables below cover good fit use cases for each.
Broadcast Family
| Mechanism | Summary | Use when | Avoid when |
|---|---|---|---|
| Pub/Sub | One-shot topic messages, no persistence | Migrating existing pub/sub apps; fire-and-forget events | Building new integrations; late joiners need last value |
| Shared Contexts | Global named state, late joiners get current value | Syncing selected entity (developer-driven); late joiners must get state | User controls grouping → Channels; need workspace isolation |
| Color Channels | Shared Contexts + Channel Selector UI | User decides which apps link; FDC3 User Channels needed | Linking fixed in code → Shared Contexts; full global visibility needed |
| Workspace Contexts | Workspace-scoped state, persists with Layout | Parallel workflows must be isolated; context saves with layout | Apps outside Workspace need data; global visibility required |
Invocation & Streaming Family
| Mechanism | Summary | Use when | Avoid when |
|---|---|---|---|
| Interop Methods | Direct service call, target must be running | Need a direct result back; target app is already running | Target may not be running → Intents; need shared state |
| Intents | Workflow handoff, can launch handler | App handoffs, launch workflows; user picks handler via Resolver UI | Service contract well-known → Methods; streaming or broadcast needed |
| Streams | Continuous push, branches for targeted delivery | Continuous data feed; polling would be wasteful | Occasional updates → Shared Contexts; single request/response → Methods |
| Window Context | Launch-time parameters, window-scoped | Passing initial payload to a window; context persists with Global Layout | Dynamic messaging needed post-launch; multiple apps need the data |
For a detailed head-to-head on Methods vs. Intents (the most confused pair), see Interop Methods vs Intents.
Real-World Scenarios
Below are common app-to-app scenarios. These are not hard rules, but they should give you a good idea before you optimize for special cases.
| Scenario | Best Fit | Why | Alternative |
|---|---|---|---|
| User clicks a client - all open apps should update | Shared Contexts | Global; late joiners get current value | Channels if user controls grouping |
| Alert service pushes live notifications | Streams | Continuous push; branches for filtering | Methods if one-off lookup |
| “View Chart” should open the right charting app | Intents | Loose coupling; launches app; user picks | Methods if chart app always running |
| Two portfolios - each with independent context | Workspace Contexts | Isolated per Workspace | Channels if Workspaces aren’t in use |
| User wants to link two specific apps | Color Channels | User assigns apps to same colour | Shared Contexts for programmatic linking |
| Risk app exposes a calculation | Interop Methods | Precise service call; result returned | Intents if callers shouldn’t depend on a specific app |
| Open chart pre-loaded with a ticker | Window Context | Pass launch params at open time | Intents - raise “ViewChart” with context |
| Existing pub/sub app connecting | Pub/Sub | Compatibility bridge | Migrate to Shared Contexts long-term |
| Interop with third-party FDC3 app | Channels → FDC3 User Channels | Direct FDC3 mapping | Intents → FDC3 Intents if action-based |
Decision Flowchart
You can use this flow from top to bottom. It intentionally starts with the most specific cases first, such as legacy migration or continuous streaming, and leaves Shared Contexts as the default for general global state synchronization.
Answer each question top-to-bottom - first “Yes” wins.
Legacy pub/sub migration?
└─ YES → Pub/Sub
└─ NO ↓
Continuous real-time data push?
└─ YES → Streams
└─ NO ↓
Need a response or trigger an action?
└─ YES → Target known & running?
│ └─ YES → Interop Methods
│ └─ NO → Intents (can launch app)
└─ NO ↓
User controls which apps link together?
└─ YES → Color Channels
└─ NO ↓
Scoped to one Workspace only?
└─ YES → Workspace Contexts
└─ NO ↓
Launch payload or per-window state?
└─ YES → Window Context
└─ NO ↓
DEFAULT → Shared Contexts
Default path: All “No” → Shared Contexts - best global programmatic sync.
Mnemonic:
| Need | Mechanism |
|---|---|
| Action / handoff | -> Intents |
| Service call | -> Methods |
| Live feed | -> Streams |
| Shared state | -> Contexts / Channels |
| Legacy topic bus | -> Pub/Sub |
FDC3 Mappings: Channels → User Channels · Intents → FDC3 Intents · Streams → Private Channels
Key Takeaways
Most of the complexity comes from choosing the wrong level of abstraction. io.Connect gives you low-level and high-level mechanisms, but for new workflows you usually want the highest-level mechanism that matches the user experience.
These are the rules I would keep in mind when designing app-to-app communication.
-
Everything flows through the Gateway: All mechanisms ride through the io.Connect Gateway over WebSockets. No direct app-to-app connections.
-
Think in patterns first: Match the interaction pattern (service call, shared state, workflow handoff, scoped context, legacy bus) to the right mechanism.
-
Start narrow, go wider: Window → Workspace → Shared Context / Channels. Start with the narrowest scope that satisfies the requirement.
-
Prefer higher-level mechanisms: Reserve Pub/Sub for porting legacy systems. For new development, pick the mechanism that gives you persistence, discovery, and the best UX.
Related reading:
- Key io.Connect Integration Concepts Explained - conceptual overview with business use cases
- Data Sharing Comparison Cheatsheet - full feature matrix and per-mechanism summary cards
- Interop Methods vs Intents - deep-dive on the most confused pair
Documentation: