diff --git a/include/reactor-cpp/environment.hh b/include/reactor-cpp/environment.hh
index 693fa51e..14a49715 100644
--- a/include/reactor-cpp/environment.hh
+++ b/include/reactor-cpp/environment.hh
@@ -59,14 +59,14 @@ private:
   /// Timeout as given in the constructor
   const Duration timeout_{};
 
+  Graph<BasePort> graph_{};
+  Graph<BasePort> optimized_graph_{};
+
   /// The start tag as determined during startup()
   Tag start_tag_{};
   /// The timeout tag as determined during startup()
   Tag timeout_tag_{};
 
-  Graph<BasePort*, ConnectionProperties> graph_{};
-  Graph<BasePort*, ConnectionProperties> optimized_graph_{};
-
   void build_dependency_graph(Reactor* reactor);
   void calculate_indexes();
 
@@ -98,7 +98,6 @@ public:
   void optimize();
 
   void register_reactor(Reactor* reactor);
-  void register_port(BasePort* port) noexcept;
   void register_input_action(BaseAction* action);
   void assemble();
   auto startup() -> std::thread;
diff --git a/include/reactor-cpp/graph.hh b/include/reactor-cpp/graph.hh
old mode 100644
new mode 100755
index 4009e169..7786dfed
--- a/include/reactor-cpp/graph.hh
+++ b/include/reactor-cpp/graph.hh
@@ -9,16 +9,39 @@
 #ifndef REACTOR_CPP_GRAPH_HH
 #define REACTOR_CPP_GRAPH_HH
 
+#include <algorithm>
 #include <iostream>
 #include <map>
 #include <optional>
+#include <type_traits>
 #include <vector>
 
 namespace reactor {
+class GraphElement {
+public:
+  GraphElement() noexcept = default;
+  GraphElement(const GraphElement& graph) noexcept = default;
+  GraphElement(GraphElement&& graph) noexcept = default;
+
+  virtual ~GraphElement() noexcept = default;
+  [[nodiscard]] virtual auto connected_to_downstream_actions() const noexcept -> bool = 0;
+  [[nodiscard]] virtual auto connected_to_upstream_actions() const noexcept -> bool = 0;
+  [[nodiscard]] virtual auto rating() const noexcept -> std::size_t = 0;
+
+  auto operator=([[maybe_unused]] const GraphElement& other) noexcept -> GraphElement& = default;
+  auto operator=([[maybe_unused]] GraphElement&& other) noexcept -> GraphElement& = default;
+};
+
 // this graph is special, because to every edge properties are annotated
-template <class E, class P> class Graph {
+template <class X> class Graph {
+  // static_assert(std::is_base_of_v<GraphElement, X>);
+  using E = X*;
+  using P = ConnectionProperties;
+
 private:
-  std::map<E, std::vector<std::pair<P, E>>> graph_;
+  using Path = std::vector<std::tuple<E, P, E>>;
+  std::map<E, std::vector<std::pair<P, E>>> graph_{};
+  std::set<E> nodes_{};
 
   // custom compare operator this is required if u want special key values in std::map
   // this is required for the Graph::get_edges() method
@@ -50,14 +73,20 @@ public:
 
   // adds a single edge to the graph structure
   void add_edge(E source, E destination, P properties) noexcept {
+    nodes_.insert(source);
+    nodes_.insert(destination);
+
     if (graph_.find(source) == std::end(graph_)) {
-      std::vector<std::pair<P, E>> edges{std::make_pair(properties, destination)};
+      std::vector<std::pair<P, E>> edges;
+      edges.emplace_back(properties, destination);
       graph_[source] = edges;
     } else {
       graph_[source].emplace_back(properties, destination);
     }
   }
 
+  auto get_nodes() -> std::set<E> { return nodes_; }
+
   // this groups connections by same source and properties
   [[nodiscard]] auto get_edges() const noexcept -> std::map<map_key, std::vector<E>, map_key_compare> {
     std::map<map_key, std::vector<E>, map_key_compare> all_edges{};
@@ -85,43 +114,188 @@ public:
     return keys;
   }
 
-  // returns the spanning tree of a given source including properties
-  [[nodiscard]] auto spanning_tree(E source) noexcept -> std::map<E, std::vector<std::pair<P, E>>> {
-    std::map<E, std::vector<std::pair<P, E>>> tree{};
-    std::vector<E> work_nodes{source};
-
-    while (!work_nodes.empty()) {
-      auto parent = *work_nodes.begin();
-
-      for (auto child : graph_[parent]) {
-        // figuring out the properties until this node
-        std::vector<std::pair<P, E>> parent_properties{};
-        if (tree.find(parent) != std::end(tree)) {
-          // this if should always be the case except the first time when tree is empty
-          parent_properties = tree[parent]; // TODO: make sure this is a copy otherwise we change this properties as
-                                            // well
-        }
+  // the return type looks a little bit cursed what is happening here ?
+  // we have a map from the destination as a key to a list of paths through the graph.
+  // A path here is modelled by a list of edges (with properties and the next vertex).
+  auto naive_spanning_tree(E source) noexcept -> std::vector<std::vector<std::tuple<E, P, E>>> {
+    return recursive_spanning_tree(source, std::vector<E>{});
+  }
 
-        // appending the new property and inserting into the tree
-        parent_properties.push_back(child);
-        work_nodes.push_back(child.second);
-        tree[child.second] = parent_properties;
-      }
+  // this function goes recursively though the graph and tries to find every possible path
+  auto recursive_spanning_tree(E source_node, std::vector<E> visited_nodes)
+      -> std::vector<std::vector<std::tuple<E, P, E>>> {
+    std::vector<Path> paths{};
 
-      work_nodes.erase(std::begin(work_nodes));
+    if (graph_[source_node].empty()) {
+      return std::vector<Path>{Path{}};
     }
 
-    return tree;
+    // if this node has an action we need to append the path
+    if (source_node->connected_to_downstream_actions()) {
+      paths.push_back(Path{});
+    }
+
+    for (auto child : graph_[source_node]) {
+      E current_node = child.second;
+
+      // we dont need to check for cycles because lf semantics assure that there wont be any cycles
+      for (auto path : recursive_spanning_tree(current_node, visited_nodes)) {
+        path.push_back(std::make_tuple(source_node, child.first, current_node));
+        paths.push_back(path);
+      }
+    }
+
+    return paths;
   }
 
   [[nodiscard]] auto get_destinations(E source) const noexcept -> std::vector<std::pair<P, E>> {
-    return graph_[source];
+    return this->graph_.at(source);
   }
 
-  [[nodiscard]] auto get_upstream(E vertex) const noexcept -> std::optional<E> {
-    for (const auto& [source, sinks] : graph_) {
-      if (sinks.second.contains(vertex)) {
-        return source;
+  [[nodiscard]] auto to_mermaid() const noexcept -> std::string {
+    std::string mermaid_string = "graph TD;\n";
+    std::size_t index{0};
+    std::map<E, std::string> name_map{};
+
+    auto name_resolver = [&](E object) -> std::string {
+      char names[] = "ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz"; // NOLINT
+      if (name_map.find(object) == std::end(name_map)) {
+        name_map[object] = names[index];
+        index++;
+        return std::string{names[index - 1], 1};
+      }
+      return name_map[object];
+    };
+
+    for (const auto& [source, destinations] : graph_) {
+      for (auto dest : destinations) {
+        mermaid_string += std::string("    ") + name_resolver(source) + std::string("-->") +
+                          name_resolver(dest.second) + std::string(";\n");
+      }
+    }
+    return mermaid_string;
+  }
+
+  void optimize(Graph<X>& optimized_graph) {
+    optimized_graph.clear();
+
+    static std::map<std::pair<ConnectionType, ConnectionType>, ConnectionType> construction_table = {
+        // Normal + x
+        {std::make_pair<ConnectionType, ConnectionType>(Normal, Normal), Normal},
+        {std::make_pair<ConnectionType, ConnectionType>(Normal, Delayed), Delayed},
+        {std::make_pair<ConnectionType, ConnectionType>(Normal, Enclaved), Enclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Normal, Physical), Physical},
+        {std::make_pair<ConnectionType, ConnectionType>(Normal, DelayedEnclaved), DelayedEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Normal, PhysicalEnclaved), PhysicalEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Normal, Plugin), Plugin},
+        // Delayed + x
+        {std::make_pair<ConnectionType, ConnectionType>(Delayed, Normal), Delayed},
+        {std::make_pair<ConnectionType, ConnectionType>(Delayed, Delayed), Delayed},
+        {std::make_pair<ConnectionType, ConnectionType>(Delayed, Enclaved), DelayedEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Delayed, Physical), Invalid}, //!!!
+        {std::make_pair<ConnectionType, ConnectionType>(Delayed, DelayedEnclaved), DelayedEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Delayed, PhysicalEnclaved), Invalid}, //!!!
+        {std::make_pair<ConnectionType, ConnectionType>(Delayed, Plugin), Invalid},
+        // Enclaved + x
+        {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Normal), Enclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Delayed), DelayedEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Enclaved), Enclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Physical), PhysicalEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Enclaved, DelayedEnclaved), DelayedEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Enclaved, PhysicalEnclaved), PhysicalEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Plugin), Invalid},
+        // Physical + x
+        {std::make_pair<ConnectionType, ConnectionType>(Physical, Normal), Physical},
+        {std::make_pair<ConnectionType, ConnectionType>(Physical, Delayed), Invalid}, // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(Physical, Enclaved), PhysicalEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Physical, Physical), Physical},
+        {std::make_pair<ConnectionType, ConnectionType>(Physical, DelayedEnclaved), Invalid}, // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(Physical, PhysicalEnclaved), PhysicalEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(Physical, Plugin), Invalid},
+        // DelayedEnclaved + x
+        {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Normal), DelayedEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Delayed), DelayedEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Enclaved), DelayedEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Physical), Invalid}, // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, DelayedEnclaved), DelayedEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, PhysicalEnclaved), Invalid}, // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Plugin), Invalid},
+        // PhysicalEnclaved + x
+        {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Normal), PhysicalEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Delayed), Invalid}, // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Enclaved), PhysicalEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Physical), PhysicalEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, DelayedEnclaved), Invalid}, // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, PhysicalEnclaved), PhysicalEnclaved},
+        {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Plugin), Invalid},
+        // Plugin + x = Invalid
+        {std::make_pair<ConnectionType, ConnectionType>(Plugin, Normal), Invalid},           // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(Plugin, Delayed), Invalid},          // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(Plugin, Enclaved), Invalid},         // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(Plugin, Physical), Invalid},         // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(Plugin, DelayedEnclaved), Invalid},  // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(Plugin, PhysicalEnclaved), Invalid}, // !!!
+        {std::make_pair<ConnectionType, ConnectionType>(Plugin, Plugin), Invalid},           // !!!
+    };
+
+    // getting all the sources from the graph
+    auto keys = this->keys();
+
+    std::vector<E> has_downstreams{};
+    std::copy_if(keys.begin(), keys.end(), std::back_inserter(has_downstreams),
+                 [](auto element) { return element->connected_to_downstream_actions(); });
+
+    std::vector<E> has_upstreams{};
+    std::copy_if(keys.begin(), keys.end(), std::back_inserter(has_upstreams),
+                 [](auto element) { return element->connected_to_upstream_actions(); });
+
+    // generating all the possible destinations for all sources
+    for (auto* source : has_upstreams) {
+      auto spanning_tree = naive_spanning_tree(source);
+
+      for (auto& path : spanning_tree) {
+        if (path.empty()) {
+          continue;
+        }
+
+        ConnectionProperties merged_properties{};
+        auto* final_destination = std::get<2>(*std::begin(path));
+        std::size_t current_rating = 0;
+
+        for (auto edge : path) {
+          auto property = std::get<1>(edge);
+          // auto source_port = std::get<0>(edge);
+          auto* destination_port = std::get<2>(edge);
+
+          current_rating += destination_port->rating();
+
+          if (current_rating > 0) {
+            auto return_type =
+                construction_table[std::pair<ConnectionType, ConnectionType>(merged_properties.type_, property.type_)];
+            // invalid will split the connections
+            if (return_type == Invalid) {
+              // first add connection until this point
+              optimized_graph.add_edge(destination_port, final_destination, merged_properties); // NOLINT
+
+              // resetting the properties and destination_port
+              final_destination = destination_port;
+              merged_properties = property;
+
+            } else {
+
+              // merging the connections
+              merged_properties.type_ = return_type;
+
+              // adding up delays
+              merged_properties.delay_ += property.delay_;
+
+              // updating target enclave if not nullptr
+              merged_properties.enclave_ =
+                  (property.enclave_ != nullptr) ? property.enclave_ : merged_properties.enclave_;
+            }
+          }
+        }
+        optimized_graph.add_edge(std::get<0>(*(std::end(path) - 1)), final_destination, merged_properties);
       }
     }
   }
@@ -136,4 +310,4 @@ public:
   }
 };
 } // namespace reactor
-#endif // REACTOR_CPP_GRAPH_HH
\ No newline at end of file
+#endif // REACTOR_CPP_GRAPH_HH
diff --git a/include/reactor-cpp/port.hh b/include/reactor-cpp/port.hh
index 6fe01494..8999a876 100644
--- a/include/reactor-cpp/port.hh
+++ b/include/reactor-cpp/port.hh
@@ -15,6 +15,7 @@
 #include "assert.hh"
 #include "connection_properties.hh"
 #include "fwd.hh"
+#include "graph.hh"
 #include "multiport.hh"
 #include "reactor_element.hh"
 #include "value_ptr.hh"
@@ -23,7 +24,7 @@ namespace reactor {
 
 enum class PortType { Input, Output, Delay };
 
-class BasePort : public ReactorElement {
+class BasePort : public GraphElement, public ReactorElement { // NOLINT
 private:
   BasePort* inward_binding_{nullptr};
   std::set<BasePort*> outward_bindings_{};
@@ -71,6 +72,8 @@ protected:
   }
 
 public:
+  ~BasePort() noexcept override = default;
+
   void set_inward_binding(BasePort* port) noexcept { inward_binding_ = port; }
   void add_outward_binding(BasePort* port) noexcept {
     outward_bindings_.insert(port); // NOLINT
@@ -92,6 +95,7 @@ public:
   [[nodiscard]] inline auto has_outward_bindings() const noexcept -> bool { return !outward_bindings_.empty(); }
   [[nodiscard]] inline auto has_dependencies() const noexcept -> bool { return !dependencies_.empty(); }
   [[nodiscard]] inline auto has_anti_dependencies() const noexcept -> bool { return !anti_dependencies_.empty(); }
+  [[nodiscard]] inline auto has_triggers() const noexcept -> bool { return !triggers_.empty(); }
 
   [[nodiscard]] inline auto inward_binding() const noexcept -> BasePort* { return inward_binding_; }
   [[nodiscard]] inline auto outward_bindings() const noexcept -> const auto& { return outward_bindings_; }
@@ -101,6 +105,14 @@ public:
   [[nodiscard]] inline auto anti_dependencies() const noexcept -> const auto& { return anti_dependencies_; }
   [[nodiscard]] inline auto port_type() const noexcept -> PortType { return type_; }
 
+  [[nodiscard]] auto connected_to_downstream_actions() const noexcept -> bool final {
+    return has_dependencies() || has_triggers();
+  };
+  [[nodiscard]] auto connected_to_upstream_actions() const noexcept -> bool final { return has_anti_dependencies(); };
+  [[nodiscard]] auto rating() const noexcept -> std::size_t final {
+    return dependencies_.size() + triggers_.size() + ((set_callback_ != nullptr) ? 1 : 0);
+  }
+
   void register_set_callback(const PortCallback& callback);
   void register_clean_callback(const PortCallback& callback);
 
@@ -170,7 +182,7 @@ public:
   Input(const std::string& name, Reactor* container)
       : Port<T>(name, PortType::Input, container) {}
 
-  Input(Input&&) = default; // NOLINT(performance-noexcept-move-constructor)
+  Input(Input&&) noexcept = default; // NOLINT(performance-noexcept-move-constructor)
 };
 
 template <class T> class Output : public Port<T> { // NOLINT
@@ -178,7 +190,7 @@ public:
   Output(const std::string& name, Reactor* container)
       : Port<T>(name, PortType::Output, container) {}
 
-  Output(Output&&) = default; // NOLINT(performance-noexcept-move-constructor)
+  Output(Output&&) noexcept = default; // NOLINT(performance-noexcept-move-constructor)
 };
 
 } // namespace reactor
diff --git a/include/reactor-cpp/reactor_element.hh b/include/reactor-cpp/reactor_element.hh
index f6322058..94bbbbaa 100644
--- a/include/reactor-cpp/reactor_element.hh
+++ b/include/reactor-cpp/reactor_element.hh
@@ -37,7 +37,7 @@ public:
   virtual ~ReactorElement() = default;
 
   // not copyable, but movable
-  ReactorElement(const ReactorElement&) = delete;
+  ReactorElement(const ReactorElement&) = default;
   ReactorElement(ReactorElement&&) = default;
 
   [[nodiscard]] auto container() const noexcept -> Reactor* { return container_; }
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index fd47bcfb..8b6614dd 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -48,6 +48,13 @@ set_target_properties(${LIB_TARGET} PROPERTIES
     VERSION ${PROJECT_VERSION}
     SOVERSION 1)
 
+option(GRAPH_OPTIMIZATIONS "Graph optimizations" ON)
+if(GRAPH_OPTIMIZATIONS)
+  target_compile_definitions(${LIB_TARGET} PRIVATE GRAPH_OPTIMIZATIONS=1 )
+else (GRAPH_OPTIMIZATIONS)
+  target_compile_definitions(${LIB_TARGET} PRIVATE GRAPH_OPTIMIZATIONS=0 )
+endif(GRAPH_OPTIMIZATIONS)
+
 if(DEFINED LF_REACTOR_CPP_SUFFIX)
   install(FILES "${PROJECT_BINARY_DIR}/include/reactor-cpp/config.hh" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIB_TARGET}/reactor-cpp")
 else()
diff --git a/lib/environment.cc b/lib/environment.cc
index d753a877..6b6f17bb 100644
--- a/lib/environment.cc
+++ b/lib/environment.cc
@@ -63,8 +63,20 @@ void Environment::register_input_action(BaseAction* action) {
 }
 
 void Environment::optimize() {
-  // no optimizations
-  optimized_graph_ = graph_;
+#if GRAPH_OPTIMIZATIONS
+  constexpr bool enable_optimizations = true;
+#else
+  constexpr bool enable_optimizations = false;
+#endif
+  log::Debug() << "Opimizations:" << enable_optimizations;
+  if constexpr (enable_optimizations) {
+    log::Debug() << graph_.to_mermaid();
+    graph_.optimize(optimized_graph_);
+    log::Debug() << optimized_graph_.to_mermaid();
+  } else {
+    // no optimizations
+    optimized_graph_ = graph_;
+  }
 }
 
 void recursive_assemble(Reactor* container) { // NOLINT
@@ -96,6 +108,7 @@ void Environment::assemble() { // NOLINT
     this->optimize();
 
     log::Debug() << "instantiating port graph declaration";
+
     log::Debug() << "graph: ";
     log::Debug() << optimized_graph_;
 
@@ -142,6 +155,13 @@ void Environment::assemble() { // NOLINT
       }
     }
   }
+
+  log::Debug() << "Building the Dependency-Graph";
+  for (auto* reactor : top_level_reactors_) {
+    build_dependency_graph(reactor);
+  }
+
+  calculate_indexes();
 }
 
 void Environment::build_dependency_graph(Reactor* reactor) { // NOLINT
diff --git a/lib/port.cc b/lib/port.cc
index 8525df66..401f9828 100644
--- a/lib/port.cc
+++ b/lib/port.cc
@@ -30,6 +30,8 @@ void BasePort::register_dependency(Reaction* reaction, bool is_trigger) noexcept
              "Dependent output ports must belong to a contained reactor");
   }
 
+  log::Debug() << "registering dependency for : " << this->fqn() << " with reaction: " << reaction->fqn()
+               << " is trigger: " << is_trigger;
   [[maybe_unused]] bool result = dependencies_.insert(reaction).second;
   reactor_assert(result);
   if (is_trigger) {
diff --git a/lib/reactor_element.cc b/lib/reactor_element.cc
index db111566..9fd1d5f0 100644
--- a/lib/reactor_element.cc
+++ b/lib/reactor_element.cc
@@ -39,10 +39,10 @@ ReactorElement::ReactorElement(const std::string& name, ReactorElement::Type typ
     container->register_action(reinterpret_cast<BaseAction*>(this)); // NOLINT
     break;
   case Type::Input:
-    container->register_input(reinterpret_cast<BasePort*>(this)); // NOLINT
+    container->register_input(static_cast<BasePort*>(this)); // NOLINT
     break;
   case Type::Output:
-    container->register_output(reinterpret_cast<BasePort*>(this)); // NOLINT
+    container->register_output(static_cast<BasePort*>(this)); // NOLINT
     break;
   case Type::Reaction:
     container->register_reaction(reinterpret_cast<Reaction*>(this)); // NOLINT