# Interaction Styles (/learn/concepts/interaction-styles)



Pick apart any messaging system and you find the same three conversation shapes underneath. A pattern's name and its bells and whistles vary, but the way a message travels from sender to receiver always reduces to one of three styles: **one-to-many** (pub/sub), **one-to-one-of-many** (point-to-point), or **there-and-back** (request/reply).

Think of how people communicate. A speaker at a conference addresses the whole room at once — everyone listening hears it (pub/sub). A help desk has a single ticket line feeding several agents — your ticket goes to whichever agent is free, and to exactly one of them (point-to-point). A phone call is a back-and-forth — you ask, you wait, you get an answer (request/reply). Learn these three shapes and every messaging pattern becomes a variation on a theme you already understand.

## Pub/Sub — one sender, many receivers [#pubsub--one-sender-many-receivers]

In **pub/sub** (publish/subscribe), a sender publishes a message to a named destination and **every** active receiver subscribed to that destination gets its own copy. The sender does not know or care who is listening — it could be zero receivers or a thousand. This shape is called **fan-out**: one message in, many copies out.

The sender and receivers are decoupled. You can add a new subscriber tomorrow without touching the publisher, and a slow or absent subscriber does not hold anyone else up.

<Mermaid
  chart="graph LR
  PUB[&#x22;Publisher&#x22;]
  CH{{&#x22;Channel<br/>order-events&#x22;}}
  S1[&#x22;Subscriber A&#x22;]
  S2[&#x22;Subscriber B&#x22;]
  S3[&#x22;Subscriber C&#x22;]

  PUB -- publish --> CH
  CH -- copy --> S1
  CH -- copy --> S2
  CH -- copy --> S3

  class CH events
  class PUB,S1,S2,S3 client"
/>

*One published message is copied to every active subscriber — fan-out, one-to-many.*

**Reach for pub/sub when** every interested party needs the same message: broadcasting state changes, notifying multiple services of an event, feeding live dashboards, or invalidating caches across a fleet.

## Point-to-Point — one sender, one-of-many receivers [#point-to-point--one-sender-one-of-many-receivers]

In **point-to-point**, a sender puts a message on a shared queue and **exactly one** receiver consumes it. When several receivers read from the same queue, they form a pool of **competing consumers** — the queue hands each message to whichever consumer is free, spreading the work across all of them. One message in, delivered once, to one worker.

This is how you scale a workload horizontally. Add more workers and throughput goes up; each message is still processed exactly once, and no two workers do the same job.

<Mermaid
  chart="graph LR
  P1[&#x22;Producer&#x22;]
  Q[[&#x22;Queue<br/>order-jobs · FIFO&#x22;]]
  W1[&#x22;Worker 1&#x22;]
  W2[&#x22;Worker 2&#x22;]
  W3[&#x22;Worker 3&#x22;]

  P1 -- enqueue --> Q
  Q -- &#x22;deliver (one consumer)&#x22; --> W1
  Q --> W2
  Q --> W3
  W1 -. ack .-> Q

  class Q queue
  class P1,W1,W2,W3 client"
/>

*Each queued message goes to exactly one of the competing workers — load-balanced, one-to-one-of-many. The dotted line is the acknowledgment that removes the message.*

**Reach for point-to-point when** each message represents a unit of work that should be done once: order processing, background jobs, task distribution, or anything where you want to add workers to handle more load.

<Callout type="info">
  **Pub/sub vs point-to-point** is the most consequential choice you make. Pub/sub *copies* a message to everyone; point-to-point *hands* a message to one worker. Same starting point, opposite outcomes.
</Callout>

## Request/Reply — there and back [#requestreply--there-and-back]

In **request/reply**, a sender issues a request and **waits** for a response from a receiver before continuing. It is the synchronous shape: the round-trip is part of the flow, and the sender blocks (up to a timeout) until the answer arrives or it gives up. One request out, one matching response back.

Unlike the other two styles, the sender expects a reply and is coupled to it in time — if the receiver is down or slow, the sender waits. That tight coupling is the point: you want the result *now*, before moving on.

<Mermaid
  chart="sequenceDiagram
  participant C as Sender
  participant K as Broker
  participant R as Responder

  C->>K: request (timeout 5s)
  K->>R: route request
  R->>R: process
  R-->>K: response
  K-->>C: response"
/>

*The sender blocks until the response returns through the broker — synchronous, there-and-back. A request that returns data is a query; one that only confirms an action is a command.*

**Reach for request/reply when** the sender needs an answer to proceed: looking up data, calling a service-to-service API, confirming a write succeeded, or any classic remote-procedure call.

## The three styles at a glance [#the-three-styles-at-a-glance]

|                           | Pub/Sub                          | Point-to-Point                 | Request/Reply                    |
| ------------------------- | -------------------------------- | ------------------------------ | -------------------------------- |
| **Direction**             | one → many                       | one → one-of-many              | one ↔ one                        |
| **Receivers per message** | every subscriber                 | exactly one consumer           | one responder                    |
| **Coupling**              | loose (sender ignores receivers) | loose (sender ignores workers) | tight (sender waits for reply)   |
| **Timing**                | asynchronous                     | asynchronous                   | synchronous                      |
| **Adds receivers to…**    | reach more listeners (fan-out)   | share more work (scale)        | distribute load behind one reply |
| **Typical use**           | broadcasts, notifications        | jobs, task queues              | lookups, RPC, confirmations      |

<Callout type="warn">
  **Pitfall:** don't force a synchronous request/reply where a one-way style fits. Blocking a sender on a slow downstream service is a common cause of cascading timeouts — if you only need to *tell* someone something, publish an event or enqueue a job and move on.
</Callout>

## In KubeMQ [#in-kubemq]

KubeMQ implements all three interaction styles natively, so you choose the conversation shape rather than wiring it together yourself:

| Interaction style                    | KubeMQ pattern                  | Why                                                                                                                                                                 |
| ------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Pub/Sub (fan-out)                    | **Events** and **Events Store** | A publish is copied to every active subscriber on the channel. Events is at-most-once and in-memory; Events Store persists messages so subscribers can also replay. |
| Point-to-Point (competing consumers) | **Queues**                      | Each queued message is delivered to one consumer and removed on acknowledgment; multiple consumers on a channel compete for messages and share the load.            |
| Request/Reply (round-trip)           | **RPC** (Commands & Queries)    | The sender blocks until a responder answers or the timeout expires. A Query returns a payload; a Command returns an execution acknowledgment.                       |

The snippets below show the *send* side of each style against `localhost:50000`, using the same e-commerce orders domain. They are deliberately minimal — see the pattern pages for full subscribe/receive/respond flows.

### Pub/Sub — publish an event [#pubsub--publish-an-event]

<Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
  <Tab value="Go">
    ```go title="publish.go"
    err = client.SendEvent(ctx, kubemq.NewEvent().
        SetChannel("order-events").
        SetMetadata("order.created").
        SetBody([]byte(`{"orderId":"ORD-1234","status":"created"}`)),
    )
    ```
  </Tab>

  <Tab value="Python">
    ```python title="publish.py"
    client.send_event(
        EventMessage(
            channel="order-events",
            metadata="order.created",
            body=b'{"orderId":"ORD-1234","status":"created"}',
        )
    )
    ```
  </Tab>

  <Tab value="Node.js">
    ```javascript title="publish.js"
    await client.sendEvent({
      channel: "order-events",
      metadata: "order.created",
      body: Buffer.from(JSON.stringify({ orderId: "ORD-1234", status: "created" })),
    });
    ```
  </Tab>

  <Tab value="Java">
    ```java title="Publish.java"
    client.sendEventsMessage(EventMessage.builder()
        .channel("order-events")
        .metadata("order.created")
        .body("{\"orderId\":\"ORD-1234\",\"status\":\"created\"}".getBytes())
        .build());
    ```
  </Tab>

  <Tab value="C#">
    ```csharp title="Publish.cs"
    await client.SendEventAsync(new EventMessage
    {
        Channel = "order-events",
        Metadata = "order.created",
        Body = Encoding.UTF8.GetBytes("{\"orderId\":\"ORD-1234\",\"status\":\"created\"}")
    });
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin title="Publish.kt"
    client.sendEvent(EventMessage(
        channel = "order-events",
        metadata = "order.created",
        body = """{"orderId":"ORD-1234","status":"created"}""".toByteArray()
    ))
    ```
  </Tab>

  <Tab value="C++">
    ```cpp title="publish.cpp"
    kubemq::EventMessage event;
    event.channel = "order-events";
    event.metadata = "order.created";
    event.body = R"({"orderId":"ORD-1234","status":"created"})";

    client.sendEvent(event);
    ```
  </Tab>

  <Tab value="Rust">
    ```rust title="publish.rs"
    let event = EventBuilder::new()
        .channel("order-events")
        .metadata("order.created")
        .body(br#"{"orderId":"ORD-1234","status":"created"}"#.to_vec())
        .build();

    client.send_event(event).await?;
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby title="publish.rb"
    msg = KubeMQ::PubSub::EventMessage.new(
      channel: "order-events",
      metadata: "order.created",
      body: '{"orderId":"ORD-1234","status":"created"}'
    )
    client.send_event(msg)
    ```
  </Tab>

  <Tab value="Elixir">
    ```elixir title="publish.exs"
    event = KubeMQ.Event.new(
      channel: "order-events",
      metadata: "order.created",
      body: ~s({"orderId":"ORD-1234","status":"created"})
    )

    KubeMQ.Client.send_event(client, event)
    ```
  </Tab>
</Tabs>

### Point-to-Point — enqueue a job [#point-to-point--enqueue-a-job]

<Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
  <Tab value="Go">
    ```go title="enqueue.go"
    msg := kubemq.NewQueueMessage().
        SetChannel("order-jobs").
        SetBody([]byte(`{"orderId":"ORD-1234","total":99.99}`))

    result, err := client.SendQueueMessage(ctx, msg)
    ```
  </Tab>

  <Tab value="Python">
    ```python title="enqueue.py"
    result = client.send_queue_message(
        QueueMessage(
            channel="order-jobs",
            body=b'{"orderId":"ORD-1234","total":99.99}',
        )
    )
    ```
  </Tab>

  <Tab value="Node.js">
    ```typescript title="enqueue.ts"
    const result = await client.sendQueueMessage(
      createQueueMessage({
        channel: "order-jobs",
        body: JSON.stringify({ orderId: "ORD-1234", total: 99.99 }),
      }),
    );
    ```
  </Tab>

  <Tab value="Java">
    ```java title="Enqueue.java"
    SendQueueMessageResult result = client.sendQueueMessage(
        QueueMessage.builder()
            .channel("order-jobs")
            .body("{\"orderId\":\"ORD-1234\",\"total\":99.99}".getBytes())
            .build());
    ```
  </Tab>

  <Tab value="C#">
    ```csharp title="Enqueue.cs"
    var result = await client.SendQueueMessageAsync(new QueueMessage
    {
        Channel = "order-jobs",
        Body = Encoding.UTF8.GetBytes("{\"orderId\":\"ORD-1234\",\"total\":99.99}")
    });
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin title="Enqueue.kt"
    val result = client.sendQueueMessage(QueueMessage(
        channel = "order-jobs",
        body = """{"orderId":"ORD-1234","total":99.99}""".toByteArray()
    ))
    ```
  </Tab>

  <Tab value="C++">
    ```cpp title="enqueue.cpp"
    kubemq::QueueMessage msg;
    msg.channel = "order-jobs";
    msg.body = R"({"orderId":"ORD-1234","total":99.99})";

    auto result = client.sendQueueMessage(msg);
    ```
  </Tab>

  <Tab value="Rust">
    ```rust title="enqueue.rs"
    let msg = QueueMessageBuilder::new()
        .channel("order-jobs")
        .body(br#"{"orderId":"ORD-1234","total":99.99}"#.to_vec())
        .build();

    let result = client.send_queue_message(msg).await?;
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby title="enqueue.rb"
    msg = KubeMQ::Queues::QueueMessage.new(
      channel: "order-jobs",
      body: '{"orderId":"ORD-1234","total":99.99}'
    )
    result = client.send_queue_message(msg)
    ```
  </Tab>

  <Tab value="Elixir">
    ```elixir title="enqueue.exs"
    msg = KubeMQ.QueueMessage.new(
      channel: "order-jobs",
      body: ~s({"orderId":"ORD-1234","total":99.99})
    )

    {:ok, result} = KubeMQ.Client.send_queue_message(client, msg)
    ```
  </Tab>
</Tabs>

### Request/Reply — send a command and wait [#requestreply--send-a-command-and-wait]

<Tabs groupId="language" items="['Go', 'Python', 'Node.js', 'Java', 'C#', 'Kotlin', 'C++', 'Rust', 'Ruby', 'Elixir']">
  <Tab value="Go">
    ```go title="send_command.go"
    resp, err := client.SendCommand(ctx, kubemq.NewCommand().
        SetChannel("orders.process").
        SetBody([]byte(`{"action":"create","orderId":"ORD-1234"}`)).
        SetTimeout(10 * time.Second))

    log.Printf("executed: %v", resp.Executed)
    ```
  </Tab>

  <Tab value="Python">
    ```python title="send_command.py"
    response = client.send_command(
        CommandMessage(
            channel="orders.process",
            body=b'{"action":"create","orderId":"ORD-1234"}',
            timeout_in_seconds=10,
        )
    )
    print(f"executed: {response.is_executed}")
    ```
  </Tab>

  <Tab value="Node.js">
    ```javascript title="send_command.js"
    const response = await client.sendCommand({
      channel: "orders.process",
      body: Buffer.from(JSON.stringify({ action: "create", orderId: "ORD-1234" })),
      timeoutInSeconds: 10,
    });
    console.log("executed:", response.isExecuted);
    ```
  </Tab>

  <Tab value="Java">
    ```java title="SendCommand.java"
    CommandResponseMessage response = client.sendCommandRequest(
        CommandMessage.builder()
            .channel("orders.process")
            .body("{\"action\":\"create\",\"orderId\":\"ORD-1234\"}".getBytes())
            .timeout(10000)
            .build());
    System.out.println("executed: " + response.isExecuted());
    ```
  </Tab>

  <Tab value="C#">
    ```csharp title="SendCommand.cs"
    var response = await client.SendCommandAsync(new CommandMessage
    {
        Channel = "orders.process",
        Body = Encoding.UTF8.GetBytes("{\"action\":\"create\",\"orderId\":\"ORD-1234\"}"),
        Timeout = TimeSpan.FromSeconds(10)
    });
    Console.WriteLine($"executed: {response.IsExecuted}");
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin title="SendCommand.kt"
    val response = client.sendCommand(CommandMessage(
        channel = "orders.process",
        body = """{"action":"create","orderId":"ORD-1234"}""".toByteArray(),
        timeout = 10000
    ))
    println("executed: ${response.isExecuted}")
    ```
  </Tab>

  <Tab value="C++">
    ```cpp title="send_command.cpp"
    kubemq::CommandMessage cmd;
    cmd.channel = "orders.process";
    cmd.body = R"({"action":"create","orderId":"ORD-1234"})";
    cmd.timeout = 10000;

    auto response = client.sendCommand(cmd);
    std::cout << "executed: " << response.isExecuted << std::endl;
    ```
  </Tab>

  <Tab value="Rust">
    ```rust title="send_command.rs"
    let command = CommandBuilder::new()
        .channel("orders.process")
        .body(br#"{"action":"create","orderId":"ORD-1234"}"#.to_vec())
        .timeout(Duration::from_secs(10))
        .build();

    let response = client.send_command(command).await?;
    println!("executed: {}", response.executed);
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby title="send_command.rb"
    msg = KubeMQ::CQ::CommandMessage.new(
      channel: "orders.process",
      timeout: 10,
      body: '{"action":"create","orderId":"ORD-1234"}'
    )
    result = client.send_command(msg)
    puts "executed: #{result.executed}"
    ```
  </Tab>

  <Tab value="Elixir">
    ```elixir title="send_command.exs"
    command = KubeMQ.Command.new(
      channel: "orders.process",
      body: ~s({"action":"create","orderId":"ORD-1234"}),
      timeout: 10_000
    )

    {:ok, response} = KubeMQ.Client.send_command(client, command)
    IO.puts("executed: #{response.executed}")
    ```
  </Tab>
</Tabs>

### How KubeMQ does this → [#how-kubemq-does-this-]

<Cards>
  <Card title="Events — Pub/Sub" href="/learn/events" description="Real-time fan-out: every active subscriber gets a copy, at-most-once." />

  <Card title="Events Store — Durable Pub/Sub" href="/learn/events-store" description="Fan-out with persistence, so subscribers can replay from any position." />

  <Card title="Queues — Point-to-Point" href="/learn/queues" description="Competing consumers: each message goes to exactly one worker, with acknowledgment." />

  <Card title="RPC — Request/Reply" href="/learn/rpc" description="Synchronous Commands and Queries: send a request, block for the response." />
</Cards>
