diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 6331d00b..f9647189 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -19,7 +19,8 @@
 				"xaver.clang-format",
 				"BazelBuild.vscode-bazel",
 				"llvm-vs-code-extensions.vscode-clangd",
-				"ms-azuretools.vscode-docker"
+				"ms-azuretools.vscode-docker",
+				"cschlosser.doxdocgen"
 			],
 			"settings": {
 				"remote.autoForwardPorts": false
diff --git a/com-middleware/BUILD b/com-middleware/BUILD
index d0f61154..8106915e 100644
--- a/com-middleware/BUILD
+++ b/com-middleware/BUILD
@@ -1,13 +1,14 @@
 cc_binary(
     name = "bin",
-    srcs = ["main.cpp"],
+    srcs = ["src/main.cpp"],
     features = ["warnings_critical_code_gcc"],
     linkopts = [
         "-lzmq",
     ],
     deps = [
-        "//com-middleware/can",
-        "//com-middleware/exceptions",
+        "//com-middleware/src/can",
+        "//com-middleware/src/exceptions",
+        "//com-middleware/src/mq",
         "@cppzmq",
     ],
 )
diff --git a/com-middleware/can/BUILD b/com-middleware/src/can/BUILD
similarity index 53%
rename from com-middleware/can/BUILD
rename to com-middleware/src/can/BUILD
index e80a6630..e542ac06 100644
--- a/com-middleware/can/BUILD
+++ b/com-middleware/src/can/BUILD
@@ -1,8 +1,8 @@
 cc_library(
     name = "can",
-    srcs = ["CanDriver.cpp"],
-    hdrs = ["CanDriver.hpp"],
+    srcs = glob(["*.cpp"]),
+    hdrs = glob(["*.hpp"]),
     features = ["warnings_critical_code_gcc"],
     visibility = ["//visibility:public"],
-    deps = ["//com-middleware/exceptions"],
+    deps = ["//com-middleware/src/exceptions"],
 )
diff --git a/com-middleware/can/CanDriver.cpp b/com-middleware/src/can/CanDriver.cpp
similarity index 79%
rename from com-middleware/can/CanDriver.cpp
rename to com-middleware/src/can/CanDriver.cpp
index 3404f5bc..60726ba2 100644
--- a/com-middleware/can/CanDriver.cpp
+++ b/com-middleware/src/can/CanDriver.cpp
@@ -9,8 +9,8 @@
 #include <cstring>
 #include <iostream>
 
-#include "com-middleware/exceptions/CanCloseException.hpp"
-#include "com-middleware/exceptions/CanInitException.hpp"
+#include "com-middleware/src/exceptions/CanCloseException.hpp"
+#include "com-middleware/src/exceptions/CanInitException.hpp"
 
 namespace candriver {
 
@@ -19,6 +19,14 @@ CanDriver::CanDriver(std::string can_interface, int32_t timeout_s)
     initializeCan();
 };
 
+CanDriver::~CanDriver() {
+    try {
+        uninitializeCan();
+    } catch (const std::exception &exception) {
+        std::cout << exception.what() << "\n";
+    }
+};
+
 void CanDriver::initializeCan() {
     if (m_canInterface.empty()) {
         throw exceptions::CanInitException("Can interface is empty");
@@ -38,6 +46,7 @@ void CanDriver::initializeCan() {
     // Setting the can interface
     strcpy(ifr.ifr_name, m_canInterface.c_str());
     // To determine the interface index an appropriate ioctl() has to be used (0 for all)
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
     ioctl(m_can_socket_fd, SIOCGIFINDEX, &ifr);
 
     memset(&addr, 0, sizeof(addr));
@@ -63,7 +72,7 @@ void CanDriver::uninitializeCan() const {
     }
 }
 
-auto CanDriver::readMessage(void *buffer) const -> int32_t {
-    return static_cast<int32_t>(read(m_can_socket_fd, buffer, sizeof(struct can_frame)));
+auto CanDriver::receive(can_frame *frame) const -> int32_t {
+    return static_cast<int32_t>(::read(m_can_socket_fd, frame, sizeof(can_frame)));
 }
-}  // namespace candriver
\ No newline at end of file
+}  // namespace candriver
diff --git a/com-middleware/can/CanDriver.hpp b/com-middleware/src/can/CanDriver.hpp
similarity index 59%
rename from com-middleware/can/CanDriver.hpp
rename to com-middleware/src/can/CanDriver.hpp
index a4931314..0380a236 100644
--- a/com-middleware/can/CanDriver.hpp
+++ b/com-middleware/src/can/CanDriver.hpp
@@ -1,11 +1,15 @@
 #ifndef CAN_DRIVER_HPP
 #define CAN_DRIVER_HPP
 
+#include <linux/can.h>
+
 #include <string>
 
+#include "ICanDriver.hpp"
+
 namespace candriver {
 
-class CanDriver {
+class CanDriver : public ICanDriver {
    private:
     std::string m_canInterface;
     int32_t m_timeout_s;
@@ -13,7 +17,12 @@ class CanDriver {
 
    public:
     CanDriver(std::string can_interface, int32_t timeout_s);
-    virtual ~CanDriver() noexcept(false) { uninitializeCan(); }
+    ~CanDriver() override;
+    CanDriver() = delete;
+    CanDriver(const CanDriver&) = default;
+    auto operator=(const CanDriver&) -> CanDriver& = default;
+    CanDriver(CanDriver&&) = default;
+    auto operator=(CanDriver&&) -> CanDriver& = default;
 
    private:
     /**
@@ -28,8 +37,8 @@ class CanDriver {
     void uninitializeCan() const;
 
    public:
-    auto readMessage(void* buffer) const -> int32_t;
+    auto receive(can_frame* frame) const -> int32_t override;
 };
 }  // namespace candriver
 
-#endif
\ No newline at end of file
+#endif
diff --git a/com-middleware/src/can/ICanDriver.hpp b/com-middleware/src/can/ICanDriver.hpp
new file mode 100644
index 00000000..6a286ddf
--- /dev/null
+++ b/com-middleware/src/can/ICanDriver.hpp
@@ -0,0 +1,25 @@
+#include <linux/can.h>
+
+#include <cstdint>
+
+/**
+ * @brief Asbtracts the Linux CAN Socket logic
+ *
+ */
+class ICanDriver {
+   public:
+    ICanDriver() = default;
+    ICanDriver(const ICanDriver&) = default;
+    auto operator=(const ICanDriver&) -> ICanDriver& = default;
+    ICanDriver(ICanDriver&&) = default;
+    auto operator=(ICanDriver&&) -> ICanDriver& = default;
+    virtual ~ICanDriver() = default;
+
+    /**
+     * @brief Receives CAN message
+     *
+     * @param frame CAN frame
+     * @return int32_t number of bytes received
+     */
+    virtual auto receive(can_frame* frame) const -> int32_t = 0;
+};
diff --git a/com-middleware/exceptions/BUILD b/com-middleware/src/exceptions/BUILD
similarity index 100%
rename from com-middleware/exceptions/BUILD
rename to com-middleware/src/exceptions/BUILD
diff --git a/com-middleware/exceptions/CanCloseException.hpp b/com-middleware/src/exceptions/CanCloseException.hpp
similarity index 92%
rename from com-middleware/exceptions/CanCloseException.hpp
rename to com-middleware/src/exceptions/CanCloseException.hpp
index 432d5b8a..74b80dd8 100644
--- a/com-middleware/exceptions/CanCloseException.hpp
+++ b/com-middleware/src/exceptions/CanCloseException.hpp
@@ -25,4 +25,4 @@ class CanCloseException : public exception {
 
 }  // namespace candriver::exceptions
 
-#endif  // CAN_DRIVER_EXCEPTIONS_CANCLOSEEXCEPTION_HPP
\ No newline at end of file
+#endif  // CAN_DRIVER_EXCEPTIONS_CANCLOSEEXCEPTION_HPP
diff --git a/com-middleware/exceptions/CanInitException.hpp b/com-middleware/src/exceptions/CanInitException.hpp
similarity index 93%
rename from com-middleware/exceptions/CanInitException.hpp
rename to com-middleware/src/exceptions/CanInitException.hpp
index 04025a96..52d67edc 100644
--- a/com-middleware/exceptions/CanInitException.hpp
+++ b/com-middleware/src/exceptions/CanInitException.hpp
@@ -25,4 +25,4 @@ class CanInitException : public exception {
 
 }  // namespace candriver::exceptions
 
-#endif  // CAN_DRIVER_EXCEPTIONS_CANINITEXCEPTION_HPP
\ No newline at end of file
+#endif  // CAN_DRIVER_EXCEPTIONS_CANINITEXCEPTION_HPP
diff --git a/com-middleware/main.cpp b/com-middleware/src/main.cpp
similarity index 65%
rename from com-middleware/main.cpp
rename to com-middleware/src/main.cpp
index 2c8dfe7a..abc17a18 100644
--- a/com-middleware/main.cpp
+++ b/com-middleware/src/main.cpp
@@ -14,28 +14,30 @@
 #include <zmq.hpp>
 
 #include "can/CanDriver.hpp"
+#include "mq/ZeroMQPublisher.hpp"
 
 namespace {
 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 volatile std::sig_atomic_t interrupted{0};
 }  // namespace
 
-void signal_handler(int signum) {
+void signalHandler(int signum) {
     (void)signum;  // ignore unused variable
     interrupted = 1;
 }
 
-void catch_signals() {
-    (void)std::signal(SIGINT, signal_handler);
-    (void)std::signal(SIGTERM, signal_handler);
-    (void)std::signal(SIGSEGV, signal_handler);
-    (void)std::signal(SIGABRT, signal_handler);
+void catchSignals() {
+    (void)std::signal(SIGINT, signalHandler);
+    (void)std::signal(SIGTERM, signalHandler);
+    (void)std::signal(SIGSEGV, signalHandler);
+    (void)std::signal(SIGABRT, signalHandler);
 }
 
 auto main(int argc, char **argv) -> int {
     int opt{};
     std::string interface = "can0";
 
+    // NOLINTNEXTLINE(concurrency-mt-unsafe)
     while ((opt = getopt(argc, argv, "i:")) != -1) {  // "i:" means -i takes an argument
         switch (opt) {
             case 'i':
@@ -52,25 +54,28 @@ auto main(int argc, char **argv) -> int {
     std::unique_ptr<candriver::CanDriver> can;
     try {
         can = std::make_unique<candriver::CanDriver>(interface, 5);
-    } catch (const std::exception &myCustomException) {
-        std::cout << myCustomException.what() << std::endl;
+    } catch (const std::exception &exception) {
+        std::cout << exception.what() << "\n";
         return 1;
     }
 
     struct can_frame frame {};  // Classical can frame
-    int32_t numBytes{};
+    int32_t num_bytes{};
+
     // Connect to zmq
-    zmq::context_t ctx(1);
-    zmq::socket_t publisher(ctx, zmq::socket_type::pub);
-    publisher.bind("ipc:///tmp/speed.ipc");
+    zmq::context_t context(1);
+    std::unique_ptr<zmq::socket_t> pub_socket =
+        std::make_unique<zmq::socket_t>(context, zmq::socket_type::pub);
+    mq::ZeroMQPublisher publisher{std::move(pub_socket)};
+    publisher.subscribe("ipc:///tmp/speed.ipc");
 
-    catch_signals();
+    catchSignals();
     while (true) {
-        numBytes = can->readMessage(&frame);
+        num_bytes = can->receive(&frame);
 
-        if (numBytes < 0) {
+        if (num_bytes < 0) {
             if (errno == EINTR) {
-                std::cout << "interrupt received, exiting gracefully..." << std::endl;
+                std::cout << "interrupt received, exiting gracefully..." << "\n";
                 break;
             }
             if (errno != EAGAIN) {
@@ -81,19 +86,21 @@ auto main(int argc, char **argv) -> int {
 
         // The len element contains the payload length in bytes and should be used instead of
         // can_dlc
+        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
         printf("0x%03X [%d] ", frame.can_id, frame.len);
 
         for (int i = 0; i < frame.len; i++) {
+            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
             std::cout << printf("%02X ", frame.data[i]);
         }
 
         std::cout << "\n";
 
-        zmq::message_t msg{static_cast<size_t>(numBytes)};
-        memcpy(msg.data(), frame.data, static_cast<size_t>(numBytes));
+        std::vector<uint8_t> message(frame.len);
+        memcpy(message.data(), frame.data, static_cast<size_t>(num_bytes));
 
         try {
-            publisher.send(msg, zmq::send_flags::none);
+            publisher.publish(message);
         } catch (zmq::error_t &e) {
             std::cout << "interrupt received, proceeding...\n";
         }
@@ -104,11 +111,5 @@ auto main(int argc, char **argv) -> int {
         }
     }
 
-    // Closing ZeroMQ
-    std::cout << "Closing publisher\n";
-    publisher.close();
-    std::cout << "Closing context\n";
-    ctx.close();
-
     return 0;
 }
diff --git a/com-middleware/src/mq/BUILD b/com-middleware/src/mq/BUILD
new file mode 100644
index 00000000..1bc3a734
--- /dev/null
+++ b/com-middleware/src/mq/BUILD
@@ -0,0 +1,10 @@
+cc_library(
+    name = "mq",
+    srcs = glob(["*.cpp"]),
+    hdrs = glob(["*.hpp"]),
+    features = ["warnings_critical_code_gcc"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "@cppzmq",
+    ],
+)
diff --git a/com-middleware/src/mq/IMQPublisher.hpp b/com-middleware/src/mq/IMQPublisher.hpp
new file mode 100644
index 00000000..d01fe15d
--- /dev/null
+++ b/com-middleware/src/mq/IMQPublisher.hpp
@@ -0,0 +1,28 @@
+#ifndef MQ_PUBLISHER_HPP
+#define MQ_PUBLISHER_HPP
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace mq {
+
+class IMQPublisher {
+   protected:
+    // Constructors
+    IMQPublisher() = default;
+    IMQPublisher(const IMQPublisher&) = default;
+    IMQPublisher(IMQPublisher&&) = default;
+
+    // Asignment operators
+    auto operator=(const IMQPublisher&) -> IMQPublisher& = default;
+    auto operator=(IMQPublisher&&) -> IMQPublisher& = default;
+
+   public:
+    virtual ~IMQPublisher() = default;
+    virtual void subscribe(const std::string& topic) = 0;
+    virtual auto publish(const std::vector<uint8_t>& data) -> bool = 0;
+};
+}  // namespace mq
+
+#endif
diff --git a/com-middleware/src/mq/IMQSubscriber.hpp b/com-middleware/src/mq/IMQSubscriber.hpp
new file mode 100644
index 00000000..37bf3508
--- /dev/null
+++ b/com-middleware/src/mq/IMQSubscriber.hpp
@@ -0,0 +1,27 @@
+#ifndef MQ_SUBSCRIBER_HPP
+#define MQ_SUBSCRIBER_HPP
+
+#include <string>
+#include <vector>
+
+namespace mq {
+
+class IMQSubscriber {
+   protected:
+    // Constructors
+    IMQSubscriber() = default;
+    IMQSubscriber(const IMQSubscriber&) = default;
+    IMQSubscriber(IMQSubscriber&&) = default;
+
+    // Asignment operators
+    auto operator=(const IMQSubscriber&) -> IMQSubscriber& = default;
+    auto operator=(IMQSubscriber&&) -> IMQSubscriber& = default;
+
+   public:
+    virtual ~IMQSubscriber() = 0;
+    virtual void subscribe(const std::string& topic) = 0;
+    virtual auto receive() -> std::vector<uint8_t> = 0;
+};
+}  // namespace mq
+
+#endif
diff --git a/com-middleware/src/mq/ZeroMQPublisher.cpp b/com-middleware/src/mq/ZeroMQPublisher.cpp
new file mode 100644
index 00000000..83b215a0
--- /dev/null
+++ b/com-middleware/src/mq/ZeroMQPublisher.cpp
@@ -0,0 +1,29 @@
+#include "ZeroMQPublisher.hpp"
+
+#include <iostream>
+
+namespace mq {
+ZeroMQPublisher::ZeroMQPublisher(std::unique_ptr<zmq::socket_t> socket)
+    : m_socket(std::move(socket)) {};
+
+ZeroMQPublisher::~ZeroMQPublisher() {
+    if (m_socket) {
+        std::cout << "Closing publisher\n";
+        m_socket->close();
+    }
+}
+
+void ZeroMQPublisher::subscribe(const std::string& topic) { m_socket->bind(topic); }
+
+auto ZeroMQPublisher::publish(const std::vector<uint8_t>& data) -> bool {
+    zmq::message_t msg(data.size());
+    memcpy(msg.data(), data.data(), data.size());
+    std::cout << m_socket.get() << "\n";
+    zmq::send_result_t res = m_socket->send(msg, zmq::send_flags::none);
+    if (res.has_value()) {
+        std::cout << "!sent\n";
+        return true;
+    }
+    return false;
+}
+}  // namespace mq
diff --git a/com-middleware/src/mq/ZeroMQPublisher.hpp b/com-middleware/src/mq/ZeroMQPublisher.hpp
new file mode 100644
index 00000000..455eb3bc
--- /dev/null
+++ b/com-middleware/src/mq/ZeroMQPublisher.hpp
@@ -0,0 +1,34 @@
+#ifndef ZERO_MQ_PUBLISHER_HPP
+#define ZERO_MQ_PUBLISHER_HPP
+
+#include <memory>
+#include <string>
+#include <zmq.hpp>
+
+#include "IMQPublisher.hpp"
+
+namespace mq {
+
+class ZeroMQPublisher : public IMQPublisher {
+   protected:
+    // Constructors
+    ZeroMQPublisher() = default;
+    ZeroMQPublisher(ZeroMQPublisher&&) = default;
+
+    // Asignment operators
+    auto operator=(ZeroMQPublisher&&) -> ZeroMQPublisher& = default;
+
+   public:
+    virtual ~ZeroMQPublisher() override;
+    explicit ZeroMQPublisher(std::unique_ptr<zmq::socket_t> socket);
+    ZeroMQPublisher(const ZeroMQPublisher&) = delete;
+    auto operator=(const ZeroMQPublisher&) -> ZeroMQPublisher& = delete;
+    void subscribe(const std::string& topic) override;
+    auto publish(const std::vector<uint8_t>& data) -> bool override;
+
+   private:
+    std::unique_ptr<zmq::socket_t> m_socket;
+};
+}  // namespace mq
+
+#endif
diff --git a/com-middleware/src/mq/ZeroMQSubscriber.cpp b/com-middleware/src/mq/ZeroMQSubscriber.cpp
new file mode 100644
index 00000000..457ee2a9
--- /dev/null
+++ b/com-middleware/src/mq/ZeroMQSubscriber.cpp
@@ -0,0 +1,19 @@
+#include "ZeroMQSubscriber.hpp"
+
+namespace mq {
+ZeroMQSubscriber::ZeroMQSubscriber(const std::shared_ptr<zmq::context_t>& context)
+    : m_context(context), m_socket(*context, zmq::socket_type::sub) {};
+
+void ZeroMQSubscriber::subscribe(const std::string& topic) { m_socket.bind(topic); }
+
+auto ZeroMQSubscriber::receive() -> std::vector<uint8_t> {
+    zmq::message_t msg;
+    if (!m_socket.recv(msg, zmq::recv_flags::none)) {
+        return {};
+    }
+
+    std::vector<uint8_t> data(msg.size());
+    memcpy(data.data(), msg.data(), msg.size());
+    return data;
+}
+}  // namespace mq
diff --git a/com-middleware/src/mq/ZeroMQSubscriber.hpp b/com-middleware/src/mq/ZeroMQSubscriber.hpp
new file mode 100644
index 00000000..6c28bc9a
--- /dev/null
+++ b/com-middleware/src/mq/ZeroMQSubscriber.hpp
@@ -0,0 +1,35 @@
+#ifndef ZERO_MQ_SUBSCRIBER_HPP
+#define ZERO_MQ_SUBSCRIBER_HPP
+
+#include <memory>
+#include <string>
+#include <zmq.hpp>
+
+#include "IMQSubscriber.hpp"
+
+namespace mq {
+
+class ZeroMQSubscriber : public IMQSubscriber {
+   protected:
+    // Constructors
+    ZeroMQSubscriber() = default;
+    ZeroMQSubscriber(ZeroMQSubscriber&&) = default;
+
+    // Asignment operators
+    auto operator=(ZeroMQSubscriber&&) -> ZeroMQSubscriber& = default;
+
+   public:
+    ~ZeroMQSubscriber();
+    explicit ZeroMQSubscriber(const std::shared_ptr<zmq::context_t>& context);
+    ZeroMQSubscriber(const ZeroMQSubscriber&) = delete;
+    auto operator=(const ZeroMQSubscriber&) -> ZeroMQSubscriber& = delete;
+    void subscribe(const std::string& topic) override;
+    auto receive() -> std::vector<uint8_t> override;
+
+   private:
+    std::shared_ptr<zmq::context_t> m_context;
+    zmq::socket_t m_socket;
+};
+}  // namespace mq
+
+#endif