{"id":34837079,"url":"https://github.com/modelingevolution/cppplumberd","last_synced_at":"2026-05-25T10:03:30.994Z","repository":{"id":286268738,"uuid":"960904698","full_name":"modelingevolution/cppplumberd","owner":"modelingevolution","description":null,"archived":false,"fork":false,"pushed_at":"2025-09-22T09:46:01.000Z","size":236,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-22T11:33:00.223Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/modelingevolution.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-04-05T10:18:02.000Z","updated_at":"2025-09-22T09:46:05.000Z","dependencies_parsed_at":"2025-07-04T21:27:24.522Z","dependency_job_id":"a06486fe-0ca8-4b08-928b-6185c0fec3fc","html_url":"https://github.com/modelingevolution/cppplumberd","commit_stats":null,"previous_names":["modelingevolution/cppplumberd"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/modelingevolution/cppplumberd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modelingevolution%2Fcppplumberd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modelingevolution%2Fcppplumberd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modelingevolution%2Fcppplumberd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modelingevolution%2Fcppplumberd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/modelingevolution","download_url":"https://codeload.github.com/modelingevolution/cppplumberd/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modelingevolution%2Fcppplumberd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28032374,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-25T02:00:05.988Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-25T16:07:03.656Z","updated_at":"2025-12-25T16:08:49.406Z","avatar_url":"https://github.com/modelingevolution.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cppplumberd\n\nA modern C++ library implementing **CQRS (Command Query Responsibility Segregation)** pattern with event-driven architecture, built on top of [NNG (nanomsg-next-gen)](https://github.com/nanomsg/nng) for high-performance messaging.\n\n[![Version](https://img.shields.io/badge/version-0.1.0-blue.svg)](https://github.com/modelingevolution/streamer)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n[![C++](https://img.shields.io/badge/C%2B%2B-20-blue.svg)](https://isocpp.org/)\n\n## 🎯 Overview\n\n**cppplumberd** provides a robust foundation for building distributed, event-driven applications using the CQRS pattern. It separates command handling (write operations) from event subscriptions (read operations), enabling scalable, maintainable architectures with clear separation of concerns.\n\n### Key Features\n\n- **🏗️ CQRS Architecture**: Clean separation between commands and events\n- **📡 Real-time Messaging**: Built on NNG for high-performance, low-latency communication  \n- **🔄 Event Sourcing**: Built-in event store for reliable event persistence and replay\n- **🌐 Network Transparency**: Client-server communication over TCP/IPC\n- **🧵 Thread-Safe**: Concurrent command processing and event handling\n- **📋 Protocol Buffers**: Efficient binary serialization for messages\n- **🎭 Type-Safe**: Template-based APIs for compile-time type checking\n- **🔌 Reactive Subscriptions**: Subscribe to specific event streams with filtering\n\n## 🏛️ Architecture\n\n```\n┌─────────────────┐    Commands     ┌─────────────────┐\n│                 │ ──────────────→ │                 │\n│  PlumberClient  │                 │    Plumber      │\n│                 │ ←────────────── │   (Server)      │\n└─────────────────┘    Events       └─────────────────┘\n         │                                   │\n         │                                   │\n    ┌────▼────┐                         ┌────▼────┐\n    │ Command │                         │ Command │\n    │   Bus   │                         │Handlers │\n    └─────────┘                         └─────────┘\n         │                                   │\n    ┌────▼────┐                         ┌────▼────┐\n    │Event    │                         │Event    │\n    │Subscr.  │                         │ Store   │\n    └─────────┘                         └─────────┘\n```\n\n### Core Components\n\n#### 🎯 **Command Side (Write)**\n- **Command Bus**: Routes commands to appropriate handlers\n- **Command Handlers**: Process business logic and generate events\n- **Command Validation**: Type-safe command processing\n\n#### 📊 **Query Side (Read)** \n- **Event Store**: Persists and replays events\n- **Event Subscriptions**: Real-time event streaming\n- **Event Handlers**: Process events for read model updates\n\n#### 🌐 **Transport Layer**\n- **NNG Sockets**: REQ/REP for commands, PUB/SUB for events\n- **Protocol Buffers**: Efficient message serialization\n- **Socket Factory**: Abstract socket creation for testability\n\n## 🚀 Quick Start\n\n### Prerequisites\n\n- **C++20** compatible compiler (GCC 11+, Clang 12+, MSVC 2022+)\n- **CMake 3.20+**\n- **vcpkg** package manager\n- **NNG** and **nngpp** libraries\n- **Protocol Buffers**\n- **Boost.Signals2**\n\n### Installation\n\n#### Using vcpkg\n\n```bash\n# Install dependencies\nvcpkg install nng:x64-linux nngpp:x64-linux protobuf:x64-linux boost-signals2:x64-linux\n\n# Build cppplumberd\nmkdir build \u0026\u0026 cd build\ncmake -DCMAKE_TOOLCHAIN_FILE=[vcpkg-root]/scripts/buildsystems/vcpkg.cmake ..\nmake -j$(nproc)\n```\n\n#### CMake Integration\n\n```cmake\nfind_package(cppplumberd REQUIRED)\ntarget_link_libraries(your_target PRIVATE cppplumberd::cppplumberd)\n```\n\n### Basic Usage\n\n#### 1. Define Your Messages (Protocol Buffers)\n\n```protobuf\n// messages.proto\nsyntax = \"proto3\";\n\nmessage SetPropertyCommand {\n    string element_name = 1;\n    string property_name = 2;\n    bytes value_data = 3;\n}\n\nmessage PropertyChangedEvent {\n    string element_name = 1;\n    string property_name = 2;\n    bytes value_data = 3;\n    uint64 timestamp = 4;\n}\n```\n\n#### 2. Server Setup\n\n```cpp\n#include \u003cplumberd.hpp\u003e\n#include \"messages.pb.h\"\n\nusing namespace cppplumberd;\n\n// Command Handler\nclass PropertyCommandHandler : public ICommandHandler\u003cSetPropertyCommand\u003e {\npublic:\n    void Handle(const std::string\u0026 stream_id, const SetPropertyCommand\u0026 cmd) override {\n        // Process the command\n        std::cout \u003c\u003c \"Setting property: \" \u003c\u003c cmd.property_name() \n                  \u003c\u003c \" on element: \" \u003c\u003c cmd.element_name() \u003c\u003c std::endl;\n        \n        // Publish resulting event\n        PropertyChangedEvent event;\n        event.set_element_name(cmd.element_name());\n        event.set_property_name(cmd.property_name());\n        event.set_value_data(cmd.value_data());\n        event.set_timestamp(std::chrono::system_clock::now().time_since_epoch().count());\n        \n        // Event store will handle publishing\n        eventStore-\u003eAppendEvent(stream_id, event);\n    }\n    \nprivate:\n    std::shared_ptr\u003cEventStore\u003e eventStore;\n};\n\nint main() {\n    // Create server\n    auto socketFactory = std::make_shared\u003cNggSocketFactory\u003e();\n    auto server = Plumber::CreateServer(socketFactory, \"tcp://localhost:5555\");\n    \n    // Register message types\n    server-\u003eRegisterMessage\u003cSetPropertyCommand, 100\u003e();\n    server-\u003eRegisterMessage\u003cPropertyChangedEvent, 200\u003e();\n    \n    // Register command handler\n    server-\u003eAddCommandHandler\u003cPropertyCommandHandler, SetPropertyCommand, 100\u003e();\n    \n    // Start server\n    server-\u003eStart();\n    \n    std::cout \u003c\u003c \"Server running on tcp://localhost:5555\" \u003c\u003c std::endl;\n    std::cin.get(); // Wait for input\n    \n    server-\u003eStop();\n    return 0;\n}\n```\n\n#### 3. Client Setup\n\n```cpp\n#include \u003cplumberd.hpp\u003e\n#include \"messages.pb.h\"\n\nusing namespace cppplumberd;\n\n// Event Handler\nclass PropertyEventHandler : public EventHandlerBase,\n                            public IEventHandler\u003cPropertyChangedEvent\u003e {\npublic:\n    PropertyEventHandler() {\n        this-\u003eMap\u003cPropertyChangedEvent, 200\u003e();\n    }\n    \nprivate:\n    void Handle(const Metadata\u0026 metadata, const PropertyChangedEvent\u0026 evt) override {\n        std::cout \u003c\u003c \"Property changed event received: \" \n                  \u003c\u003c evt.property_name() \u003c\u003c \" = \" \u003c\u003c evt.value_data().size() \n                  \u003c\u003c \" bytes\" \u003c\u003c std::endl;\n    }\n};\n\nint main() {\n    // Create client\n    auto socketFactory = std::make_shared\u003cNggSocketFactory\u003e();\n    auto client = PlumberClient::CreateClient(socketFactory, \"tcp://localhost:5555\");\n    \n    // Register message types\n    client-\u003eRegisterMessage\u003cSetPropertyCommand, 100\u003e();\n    client-\u003eRegisterMessage\u003cPropertyChangedEvent, 200\u003e();\n    \n    // Start client\n    client-\u003eStart();\n    \n    // Send commands\n    SetPropertyCommand cmd;\n    cmd.set_element_name(\"videosrc\");\n    cmd.set_property_name(\"bitrate\");\n    cmd.set_value_data(\"1000000\");\n    \n    client-\u003eCommandBus()-\u003eSend(\"element-stream\", cmd);\n    \n    // Subscribe to events\n    auto eventHandler = std::make_shared\u003cPropertyEventHandler\u003e();\n    auto subscription = client-\u003eSubscriptionManager()-\u003eSubscribe(\"element-stream\", eventHandler);\n    \n    std::cout \u003c\u003c \"Client running. Press Enter to exit...\" \u003c\u003c std::endl;\n    std::cin.get();\n    \n    client-\u003eStop();\n    return 0;\n}\n```\n\n## 📚 Advanced Features\n\n### Stream-Based Event Organization\n\nEvents are organized into **streams** - logical groupings of related events:\n\n```cpp\n// Different streams for different contexts\nclient-\u003eCommandBus()-\u003eSend(\"user-123\", userCommand);      // User domain\nclient-\u003eCommandBus()-\u003eSend(\"order-456\", orderCommand);    // Order domain\nclient-\u003eCommandBus()-\u003eSend(\"inventory\", stockCommand);    // Inventory domain\n```\n\n### Reactive Subscriptions\n\nSubscribe to specific event streams with filtering:\n\n```cpp\n// Subscribe to specific stream\nauto subscription = client-\u003eSubscriptionManager()-\u003eSubscribe(\"user-events\", handler);\n\n// Multiple subscriptions\nauto userSub = client-\u003eSubscriptionManager()-\u003eSubscribe(\"user-events\", userHandler);\nauto orderSub = client-\u003eSubscriptionManager()-\u003eSubscribe(\"order-events\", orderHandler);\n\n// Unsubscribe when done\nsubscription-\u003eUnsubscribe();\n```\n\n### Event Store Operations\n\n```cpp\nauto eventStore = server-\u003eGetEventStore();\n\n// Append events to stream\neventStore-\u003eAppendEvent(\"stream-1\", myEvent);\n\n// Read events from stream\nauto events = eventStore-\u003eReadEvents(\"stream-1\", 0, 100);\n\n// Stream management\neventStore-\u003eEnsureStreamCreated(\"new-stream\");\n```\n\n### Error Handling\n\n```cpp\nclass MyCommandHandler : public ICommandHandler\u003cMyCommand\u003e {\npublic:\n    void Handle(const std::string\u0026 stream_id, const MyCommand\u0026 cmd) override {\n        try {\n            // Business logic\n            processCommand(cmd);\n        }\n        catch (const BusinessLogicException\u0026 e) {\n            // Handle business rule violations\n            throw FaultException(400, e.what());\n        }\n        catch (const std::exception\u0026 e) {\n            // Handle unexpected errors\n            throw FaultException(500, \"Internal server error\");\n        }\n    }\n};\n```\n\n## 🔧 Configuration\n\n### Socket Factory Configuration\n\n```cpp\nclass CustomSocketFactory : public ISocketFactory {\npublic:\n    std::unique_ptr\u003cITransportReqRspSrvSocket\u003e CreateReqRspSrvSocket(const std::string\u0026 endpoint) override {\n        auto socket = std::make_unique\u003cNngReqRspSrvSocket\u003e();\n        socket-\u003eSetTimeout(5000);  // 5 second timeout\n        socket-\u003eSetBufferSize(1024 * 1024);  // 1MB buffer\n        return socket;\n    }\n    // ... implement other methods\n};\n\nauto customFactory = std::make_shared\u003cCustomSocketFactory\u003e();\nauto server = Plumber::CreateServer(customFactory, \"tcp://0.0.0.0:5555\");\n```\n\n### Message Registration\n\n```cpp\n// Register messages with unique IDs\nconstexpr uint32_t SET_PROPERTY_CMD = 100;\nconstexpr uint32_t PROPERTY_CHANGED_EVT = 200;\nconstexpr uint32_t CREATE_STREAM_CMD = 300;\n\nserver-\u003eRegisterMessage\u003cSetPropertyCommand, SET_PROPERTY_CMD\u003e();\nserver-\u003eRegisterMessage\u003cPropertyChangedEvent, PROPERTY_CHANGED_EVT\u003e();\nserver-\u003eRegisterMessage\u003cCreateStreamCommand, CREATE_STREAM_CMD\u003e();\n```\n\n## 🧪 Testing\n\n### Unit Testing Support\n\n```cpp\n#include \u003cgtest/gtest.h\u003e\n#include \u003cgmock/gmock.h\u003e\n\nclass MockSocketFactory : public ISocketFactory {\n    MOCK_METHOD(std::unique_ptr\u003cITransportReqRspSrvSocket\u003e, CreateReqRspSrvSocket, \n                (const std::string\u0026), (override));\n    // ... other mock methods\n};\n\nTEST(PlumberTest, CommandHandling) {\n    auto mockFactory = std::make_shared\u003cMockSocketFactory\u003e();\n    auto server = Plumber::CreateServer(mockFactory);\n    \n    // Set up expectations\n    EXPECT_CALL(*mockFactory, CreateReqRspSrvSocket(_))\n        .WillOnce(Return(std::make_unique\u003cMockReqRspSocket\u003e()));\n    \n    // Test command handling\n    // ...\n}\n```\n\n### Integration Testing\n\n```cpp\nTEST(PlumberIntegrationTest, EndToEndCommandFlow) {\n    // Start server\n    auto server = CreateTestServer();\n    server-\u003eStart();\n    \n    // Connect client\n    auto client = CreateTestClient();\n    client-\u003eStart();\n    \n    // Send command and verify response\n    TestCommand cmd;\n    cmd.set_data(\"test-data\");\n    \n    auto response = client-\u003eCommandBus()-\u003eSend(\"test-stream\", cmd);\n    EXPECT_EQ(response.status_code(), 0);\n    \n    // Verify event was published\n    auto eventHandler = std::make_shared\u003cTestEventHandler\u003e();\n    auto subscription = client-\u003eSubscriptionManager()-\u003eSubscribe(\"test-stream\", eventHandler);\n    \n    EXPECT_TRUE(eventHandler-\u003eWaitForEvent(1000)); // Wait 1 second\n    EXPECT_EQ(eventHandler-\u003eGetReceivedEvent().data(), \"test-data\");\n}\n```\n\n## 🚀 Performance Considerations\n\n### Throughput Optimization\n\n- **Connection Pooling**: Reuse connections for multiple commands\n- **Batch Processing**: Send multiple commands in batches\n- **Async Operations**: Use non-blocking I/O where possible\n\n```cpp\n// Batch command sending\nstd::vector\u003cSetPropertyCommand\u003e commands = {cmd1, cmd2, cmd3};\nfor (const auto\u0026 cmd : commands) {\n    client-\u003eCommandBus()-\u003eSend(\"batch-stream\", cmd);\n}\n```\n\n### Memory Management\n\n- **Smart Pointers**: Automatic memory management\n- **Buffer Pooling**: Reuse message buffers\n- **Stream Cleanup**: Properly close subscriptions\n\n```cpp\n// RAII pattern for subscriptions\nclass SubscriptionManager {\n    std::vector\u003cstd::unique_ptr\u003cISubscription\u003e\u003e subscriptions_;\n    \npublic:\n    ~SubscriptionManager() {\n        for (auto\u0026 sub : subscriptions_) {\n            sub-\u003eUnsubscribe();  // Automatic cleanup\n        }\n    }\n};\n```\n\n## 🛠️ Integration Examples\n\n### GStreamer Integration\n\nThis library is designed to work seamlessly with GStreamer pipelines:\n\n```cpp\n// GStreamer element property control\nclass GStreamerPropertyHandler : public ICommandHandler\u003cSetPropertyCommand\u003e {\npublic:\n    void Handle(const std::string\u0026 stream_id, const SetPropertyCommand\u0026 cmd) override {\n        // Find GStreamer element\n        GstElement* element = gst_bin_get_by_name(GST_BIN(pipeline), \n                                                  cmd.element_name().c_str());\n        if (!element) {\n            throw FaultException(404, \"Element not found\");\n        }\n        \n        // Set property\n        g_object_set_property(G_OBJECT(element), \n                            cmd.property_name().c_str(), \n                            \u0026gvalue);\n        \n        // Publish property changed event\n        PropertyChangedEvent event;\n        event.set_element_name(cmd.element_name());\n        event.set_property_name(cmd.property_name());\n        eventStore-\u003eAppendEvent(stream_id, event);\n        \n        gst_object_unref(element);\n    }\n};\n```\n\n## 📈 Monitoring \u0026 Debugging\n\n### Built-in Diagnostics\n\n```cpp\n// Enable debug logging\n#define CPPPLUMBERD_DEBUG 1\n\n// Performance metrics\nauto stats = client-\u003eGetStatistics();\nstd::cout \u003c\u003c \"Commands sent: \" \u003c\u003c stats.commands_sent \u003c\u003c std::endl;\nstd::cout \u003c\u003c \"Events received: \" \u003c\u003c stats.events_received \u003c\u003c std::endl;\nstd::cout \u003c\u003c \"Average latency: \" \u003c\u003c stats.avg_latency_ms \u003c\u003c \"ms\" \u003c\u003c std::endl;\n```\n\n### Tracing Support\n\n```cpp\nclass TracingCommandHandler : public ICommandHandler\u003cMyCommand\u003e {\npublic:\n    void Handle(const std::string\u0026 stream_id, const MyCommand\u0026 cmd) override {\n        auto start = std::chrono::high_resolution_clock::now();\n        \n        try {\n            // Process command\n            processCommand(cmd);\n            \n            auto end = std::chrono::high_resolution_clock::now();\n            auto duration = std::chrono::duration_cast\u003cstd::chrono::microseconds\u003e(end - start);\n            \n            logger-\u003einfo(\"Command processed in {}μs\", duration.count());\n        }\n        catch (const std::exception\u0026 e) {\n            logger-\u003eerror(\"Command failed: {}\", e.what());\n            throw;\n        }\n    }\n};\n```\n\n## 🤝 Contributing\n\nWe welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details.\n\n### Development Setup\n\n```bash\n# Clone with submodules\ngit clone --recursive https://github.com/modelingevolution/streamer.git\ncd streamer/src/cppplumberd\n\n# Install dependencies via vcpkg\nvcpkg install --triplet=x64-linux nng nngpp protobuf boost-signals2\n\n# Build with tests\nmkdir build \u0026\u0026 cd build\ncmake -DCMAKE_TOOLCHAIN_FILE=[vcpkg-root]/scripts/buildsystems/vcpkg.cmake \\\n      -DCPPPLUMBERD_BUILD_TESTS=ON ..\nmake -j$(nproc)\n\n# Run tests\nctest -V\n```\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🙏 Acknowledgments\n\n- **NNG**: High-performance messaging library\n- **Protocol Buffers**: Efficient serialization\n- **Boost.Signals2**: Type-safe callbacks\n- **GoogleTest**: Testing framework\n\n---\n\n**Built with ❤️ by [ModelingEvolution](https://github.com/modelingevolution)**","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodelingevolution%2Fcppplumberd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmodelingevolution%2Fcppplumberd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodelingevolution%2Fcppplumberd/lists"}