Skip to content

ZLink Framework C++ — User Guide

A C++ application framework for building server systems where real-time messaging matters out of several cooperating processes.

#include <zlink/framework.hpp>

int main (int argc, char **argv)
{
    auto app = zlink::framework::app_t::create ();
    app.add_zlink_framework ([] (zlink::framework::zlink_framework_options_t &options) {
        options.http ()
          .listen ("http://0.0.0.0:8080")
          .map_post<open_conversation_http_handler_t> ("/conversations");
    });
    return app.run (argc, argv);
}

Register one handler class and the framework handles message decoding, routing, and encoding.


What This Framework Builds

It's designed for systems where several server processes split responsibilities and cooperate, and where a state change must reach the client in real time.

Domain Core scenario
Real-time games Create room → player joins → game state updates → push to client
Customer support chat Open conversation → assign agent → relay messages → push conversation state
Order workflow Accept order → process in stages → change state → notify client
Delivery/dispatch Request dispatch → assign/accept a driver → track state → push in real time

There's one common shape — role-specific server processes talk in typed messages, and the client receives state changes over a real-time connection (stream).

%%{init: {'flowchart': {'nodeSpacing': 32, 'rankSpacing': 40, 'padding': 8, 'wrappingWidth': 180}, 'themeVariables': {'fontSize': '18px'}}}%%
flowchart LR
    Client["Client app"]
    subgraph Entry["Entry server"]
        HTTP["HTTP API"]:::infra
        ApiC["Channel client"]:::channel
    end
    subgraph Core["Domain server"]
        CoreS["Channel server"]:::channel
        SpotN["SPOT<br/>(state unit)"]:::spot
        StreamN["stream"]:::stream
        ActorG["actor gateway"]:::actor
    end
    Registry["Registry<br/>(discovery)"]:::infra

    Client -- "① HTTP request" --> HTTP
    HTTP --> ApiC
    ApiC -- "② server-to-server message" --> CoreS
    CoreS --> SpotN
    Client -- "③ real-time connection" --> StreamN
    StreamN --> ActorG --> SpotN
    ApiC & CoreS -.->|"address resolution"| Registry

    classDef channel fill:#e3f2fd,stroke:#1565c0,color:#000000
    classDef spot fill:#e8f5e9,stroke:#2e7d32,color:#000000
    classDef actor fill:#fff8e1,stroke:#f9a825,color:#000000
    classDef stream fill:#f3e5f5,stroke:#6a1b9a,color:#000000
    classDef infra fill:#eceff1,stroke:#546e7a,color:#000000

Each server process is an independent executable connected to the others over TCP. HTTP ingress, the communication path to other servers, client connections, and state-unit management all live inside one server. samples/TicTacToe (2 servers) and samples/Bingo (4 servers) are complete working examples.


Core Capabilities

Channel Messaging — typed request-reply between servers

A channel is a name given to a communication path between servers. One side sends a request by channel name, and the other side processes it and replies. structs are exchanged directly, and the framework handles serialization (JSON / MessagePack / Protobuf).

// Sending side (channel client)
auto result = co_await _client
    .request ("support.core", open_conversation_req_t{user_id})
    .async<open_conversation_res_t> ();

// Receiving side (channel server handler)
class open_conversation_handler_t {
  public:
    using request_type = open_conversation_req_t;
    using reply_type   = open_conversation_res_t;
    static constexpr const char *topic_name = "OpenConversation";
    open_conversation_res_t handle (const open_conversation_req_t &req) { ... }
};

Besides request-reply, it also provides fanout (pub/sub) and route mesh (address routing) patterns. Chapter 5 →


SPOT — managing a state unit without locks

A SPOT is an execution unit that binds "one state region" — a game room, a support conversation, an order-processing unit — together with its participants. Everything that happens inside one SPOT — participant packets, timers, join/leave — is processed serially. State can be accessed without std::mutex, and even with coroutine-based async processing, two requests never overlap within the same SPOT.

class conversation_spot_t : public zlink::framework::spot_t,
                             public conversation_t   // owns conversation state directly
{
  public:
    void configure (zlink::framework::spot_context_t &context)
    {
        context.handlers ().add_actor_packet<&conversation_spot_t::send_message> ();
    }

    send_message_res_t send_message (const user_actor_t &actor,
                                     const zlink::framework::message_context_t &,
                                     const send_message_req_t &request)
    {
        return append (actor.user_id, request.text);   // safe without std::mutex
    }
};

It splits into the entry spot (one per node), responsible for assignment/placement, and the room spot (one per unit), the state body itself. Periodic work is registered as a timer. Chapter 6 →


Stream + Actor — real-time client connections

A client's real-time bidirectional connection is called a stream, and the server-side object that represents one connection is an actor. When a client connects, the session creates an actor, and the actor joins a SPOT to participate in state processing.

class support_session_t : public zlink::framework::packet_stream_session_t {
  public:
    task_t<void> on_packet (stream_t &stream,
                            const stream_dispatch_context_t &dispatch,
                            const zlink::message_t &payload) override
    {
        auto actor = co_await _actors.find (actor_id);
        co_await actor.value ().relay (payload);   // forward the current dispatch's packet to the actor
    }
};

The client-side connection is handled by a separate deliverable, the stream connector. Chapter 8 → · Chapter 9 →


HTTP Hosting — a REST API embedded inside the server process

Host REST endpoints in the same process without a separate web server. You can implement path parameters and authentication logic in middleware/handlers, TLS is supported, and readiness / liveness / health-check endpoints register in one line.

options.http ()
  .listen ("https://0.0.0.0:8443")
  .configure_tls ([] (auto &tls) {
      tls.certificate_file (cert_path).private_key_file (key_path);
  })
  .map_post<create_game_http_handler_t> ("/games")
  .map_get<get_room_http_handler_t> ("/rooms/{room_id}")
  .map_readiness ("/ready");

Chapter 20 →


Configuration · DI · Logging · Monitoring

Built-in support for what a production server needs.

  • Configuration — composes CLI arguments, environment variables, and a JSON file in priority order. One call to bind<T>() maps a settings section onto a struct.
  • DI container — handler constructor parameters get services auto-injected. Supports singleton / scoped / transient lifetimes.
  • Logging — inject a logger_t<TOwner> via DI and get logs auto-tagged with the source name.
  • Monitoring / Health — receive socket, discovery, spot, and timer events as typed subscriptions. Wire health checks to /ready and /healthz endpoints.

Chapter 18 → · Chapter 19 → · 11. Monitoring chapter


Registry / Discovery — automatic server address wiring

When several Play server instances come up, you don't hardcode which server to connect to or its endpoint. A shared Redis-backed Location Store tracks addresses, and each server discovers them dynamically through add_location_store<redis::redis_location_store_t> ().

options.add_location_store<redis::redis_location_store_t> ()
  .set_connection_string (topology.redis_endpoint)
  .set_key_prefix (topology.redis_key_prefix + "location:");

auto room_mesh = options.add_route_mesh (sample_names_t::room_spot_mesh);
room_mesh.set_routing_id (zlink::routing_id_t::from ("bingo-play-" + topology.play_node))
  .listen (topology.selected_play_spot_router_endpoint ());
room_mesh.objects ()
  .server ()
  .add_entry_spot<bingo_entry_spot_t> ()
  .add_spot_factory<bingo_room_spot_t> (sample_names_t::room_spot);

Chapter 10 →


Table Of Contents

Order Document Content
1 1. Overview A map of the full feature set, the four integration axes, and the overall topology
2 2. Getting Started CMake integration, the first app, writing a handler, running and checking it
3 3. Core Concepts Channel · Spot · Actor · stream · relocation
4 4. Backpressure How the system behaves when arrival outpaces processing, and the options that affect it
5 5. Channel Messaging Request-reply, fanout, route mesh, channel client
6 6. SPOT room/stage/zone, serial execution, timer
7 7. Actor And Spot Actor hosting, membership, relocation
8 8. Actor · Session Actor manager, session actor, gateway relay
9 9. Stream Stream session, stream connector
10 10. Location Location store, auto-connect, operational queries
11 11. Monitoring State observation, message flow, health
12 12. Operations Runtime metrics, graceful drain, readiness
13 13. Key Type Usage Index An index of public types by feature and how to obtain them
14 14. Picking A Sample TicTacToe · Bingo samples mapped to features
15 15. E2E Testing How to verify the whole system with the client
16 16. Options The option list, defaults, and when they can change
17 17. Where ZLink Fits Internal service communication/real-time state patterns, comparison to gRPC/mesh
18 18. DI Container The three lifetimes, how to register, handler auto-injection, captive dependency
19 19. Configuration Config sources (cli/env/json), priority, section/bind
20 20. HTTP Hosting Embedded HTTP server, route handler
21 21. Execution/Composition Model The handler model, task_t/co_await, app lifecycle, module

The file number identifies the same chapter regardless of language. Chapters 1–17 are shared across all five languages, and chapters 18–21 are C++-only — DI, configuration, and HTTP hosting, which .NET gets from its runtime, are provided directly by the framework in C++, as is the execution model.

These four are foundational, so after reading chapters 2 and 3 you can jump straight to 18–21 and come back to chapter 4.


How To Read The Diagrams

Every diagram in this guide uses the same visual language — color maps to concept.

%%{init: {'flowchart': {'nodeSpacing': 32, 'rankSpacing': 40, 'padding': 8, 'wrappingWidth': 180}, 'themeVariables': {'fontSize': '18px'}}}%%
flowchart LR
    CH["Channel<br/>(messaging path)"]:::channel
    SP["SPOT<br/>(serial execution region)"]:::spot
    AC["actor / session"]:::actor
    ST["stream<br/>(external boundary)"]:::stream
    RG["registry / infrastructure"]:::infra
    classDef channel fill:#e3f2fd,stroke:#1565c0,color:#000000
    classDef spot fill:#e8f5e9,stroke:#2e7d32,color:#000000
    classDef actor fill:#fff8e1,stroke:#f9a825,color:#000000
    classDef stream fill:#f3e5f5,stroke:#6a1b9a,color:#000000
    classDef infra fill:#eceff1,stroke:#546e7a,color:#000000

Several chapters draw the same TicTacToe/Bingo topology, and only the zoomed-in location changes per chapter.

  • The HTTP client (the side that sends requests) is a separate deliverable — zlink::http_client user guide
  • The design contract (draft) lives in doc/spec/. When it conflicts with the guide, the code and the spec are authoritative.