diff --git a/src/pathcomp/backend/pathComp_tools.h b/src/pathcomp/backend/pathComp_tools.h
index 393274e161ee131c4e9d35922cc579faa088ed6e..8fe704c3932c219e0f04046fcc62d6f1da5f9b66 100644
--- a/src/pathcomp/backend/pathComp_tools.h
+++ b/src/pathcomp/backend/pathComp_tools.h
@@ -120,7 +120,7 @@ struct map_nodes_t {
     gint numMapNodes;
 };
 
-#define MAX_NUM_VERTICES				10 // 100 # LGR: reduced from 100 to 10 to divide by 10 the memory used
+#define MAX_NUM_VERTICES				20 // 100 # LGR: reduced from 100 to 20 to divide by 5 the memory used
 #define MAX_NUM_EDGES					10 // 100 # LGR: reduced from 100 to 10 to divide by 10 the memory used
 // Structures for the graph composition
 struct targetNodes_t {
diff --git a/src/pathcomp/frontend/service/__main__.py b/src/pathcomp/frontend/service/__main__.py
index a41b9e994f02db725c4adf371d9638fd5135693e..24a5b77418839153839a4fcf92f1a2b42d9cf91d 100644
--- a/src/pathcomp/frontend/service/__main__.py
+++ b/src/pathcomp/frontend/service/__main__.py
@@ -31,7 +31,7 @@ def main():
     global LOGGER # pylint: disable=global-statement
 
     log_level = get_log_level()
-    logging.basicConfig(level=log_level)
+    logging.basicConfig(level=log_level, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s")
     LOGGER = logging.getLogger(__name__)
 
     wait_for_environment_variables([
diff --git a/src/pathcomp/frontend/service/algorithms/KDisjointPathAlgorithm.py b/src/pathcomp/frontend/service/algorithms/KDisjointPathAlgorithm.py
index 48e951b1dbe01790e1ec44d2759c6079d52d0aab..70b50dae53ba22eb6c8df018fb5663cce0bc125e 100644
--- a/src/pathcomp/frontend/service/algorithms/KDisjointPathAlgorithm.py
+++ b/src/pathcomp/frontend/service/algorithms/KDisjointPathAlgorithm.py
@@ -14,9 +14,11 @@
 
 import operator
 from typing import Dict, List, Optional, Set, Tuple
-from common.proto.context_pb2 import Link
+from common.proto.context_pb2 import Connection, Link, Service
 from common.proto.pathcomp_pb2 import Algorithm_KDisjointPath, Algorithm_KShortestPath, PathCompReply, PathCompRequest
 from common.tools.grpc.Tools import grpc_message_to_json_string
+from pathcomp.frontend.service.algorithms.tools.ComputeSubServices import convert_explicit_path_hops_to_connections
+from pathcomp.frontend.service.algorithms.tools.EroPathToHops import eropath_to_hops
 from ._Algorithm import _Algorithm
 from .KShortestPathAlgorithm import KShortestPathAlgorithm
 
@@ -184,10 +186,47 @@ class KDisjointPathAlgorithm(_Algorithm):
 
     def get_reply(self) -> PathCompReply:
         reply = PathCompReply()
+        grpc_services : Dict[Tuple[str, str], Service] = {}
+        grpc_connections : Dict[Tuple[int, str], Connection] = {}
         for service_key,paths in self.json_reply.items():
-            grpc_service = self.add_service_to_reply(reply, service_key[0], service_key[1])
-            for path_endpoints in paths:
-                if path_endpoints is None: continue
-                grpc_connection = self.add_connection_to_reply(reply, grpc_service)
-                self.add_path_to_connection(grpc_connection, path_endpoints)
+            context_uuid, service_uuid = service_key
+
+            grpc_services[service_key] = self.add_service_to_reply(reply, context_uuid, service_uuid)
+
+            for num_path,service_path_ero in enumerate(paths):
+                if service_path_ero is None: continue
+                path_hops = eropath_to_hops(service_path_ero, self.endpoint_to_link_dict)
+                connections = convert_explicit_path_hops_to_connections(path_hops, self.device_dict, service_uuid)
+
+                for connection in connections:
+                    connection_uuid,device_layer,path_hops,_ = connection
+
+                    service_key = (context_uuid, connection_uuid)
+                    grpc_service = grpc_services.get(service_key)
+                    if grpc_service is not None: continue
+                    grpc_service = self.add_service_to_reply(
+                        reply, context_uuid, connection_uuid, device_layer=device_layer, path_hops=path_hops)
+                    grpc_services[service_key] = grpc_service
+
+                for connection in connections:
+                    connection_uuid,device_layer,path_hops,dependencies = connection
+
+                    service_key = (context_uuid, connection_uuid)
+                    grpc_service = grpc_services.get(service_key)
+                    if grpc_service is None: raise Exception('Service({:s}) not found'.format(str(service_key)))
+
+                    connection_uuid = '{:s}:{:d}'.format(connection_uuid, num_path)
+                    grpc_connection = grpc_connections.get(connection_uuid)
+                    if grpc_connection is not None: continue
+                    grpc_connection = self.add_connection_to_reply(reply, connection_uuid, grpc_service, path_hops)
+                    grpc_connections[connection_uuid] = grpc_connection
+
+                    for service_uuid in dependencies:
+                        sub_service_key = (context_uuid, service_uuid)
+                        grpc_sub_service = grpc_services.get(sub_service_key)
+                        if grpc_sub_service is None:
+                            raise Exception('Service({:s}) not found'.format(str(sub_service_key)))
+                        grpc_sub_service_id = grpc_connection.sub_service_ids.add()
+                        grpc_sub_service_id.CopyFrom(grpc_sub_service.service_id)
+
         return reply
diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py
index f90c1f18bae5e5d63cba5d8dd0785d8ba5f12d71..bb96ff354ef32cb0a269d2b678fdb9552d86939d 100644
--- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py
+++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py
@@ -14,11 +14,14 @@
 
 import json, logging, requests, uuid
 from typing import Dict, List, Optional, Tuple
-from common.proto.context_pb2 import Connection, Device, DeviceList, EndPointId, Link, LinkList, Service
+from common.proto.context_pb2 import Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum
 from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest
 from common.tools.grpc.Tools import grpc_message_to_json_string
 from pathcomp.frontend.Config import BACKEND_URL
+from pathcomp.frontend.service.algorithms.tools.ConstantsMappings import DEVICE_LAYER_TO_SERVICE_TYPE, DeviceLayerEnum
+from .tools.EroPathToHops import eropath_to_hops
 from .tools.ComposeRequest import compose_device, compose_link, compose_service
+from .tools.ComputeSubServices import convert_explicit_path_hops_to_connections
 
 class _Algorithm:
     def __init__(self, algorithm_id : str, sync_paths : bool, class_name=__name__) -> None:
@@ -85,17 +88,17 @@ class _Algorithm:
     def execute(self, dump_request_filename : Optional[str] = None, dump_reply_filename : Optional[str] = None) -> None:
         request = {'serviceList': self.service_list, 'deviceList': self.device_list, 'linkList': self.link_list}
 
-        self.logger.debug('[execute] request={:s}'.format(str(request)))
+        self.logger.info('[execute] request={:s}'.format(str(request)))
         if dump_request_filename is not None:
             with open(dump_request_filename, 'w', encoding='UTF-8') as f:
                 f.write(json.dumps(request, sort_keys=True, indent=4))
 
-        self.logger.debug('[execute] BACKEND_URL: {:s}'.format(str(BACKEND_URL)))
+        self.logger.info('[execute] BACKEND_URL: {:s}'.format(str(BACKEND_URL)))
         reply = requests.post(BACKEND_URL, json=request)
         self.status_code = reply.status_code
         self.raw_reply = reply.content.decode('UTF-8')
 
-        self.logger.debug('[execute] status_code={:s} reply={:s}'.format(str(reply.status_code), str(self.raw_reply)))
+        self.logger.info('[execute] status_code={:s} reply={:s}'.format(str(reply.status_code), str(self.raw_reply)))
         if dump_reply_filename is not None:
             with open(dump_reply_filename, 'w', encoding='UTF-8') as f:
                 f.write('status_code={:s} reply={:s}'.format(str(self.status_code), str(self.raw_reply)))
@@ -106,41 +109,77 @@ class _Algorithm:
         
         self.json_reply = reply.json()
 
-    def add_path_to_connection(self, connection : Connection, path_endpoints : List[Dict]) -> None:
-        for endpoint in path_endpoints:
-            device_uuid = endpoint['device_id']
-            endpoint_uuid = endpoint['endpoint_uuid']
-            endpoint_id = connection.path_hops_endpoint_ids.add()
-            endpoint_id.CopyFrom(self.endpoint_dict[device_uuid][endpoint_uuid][1])
-
-    def add_connection_to_reply(self, reply : PathCompReply, service : Service) -> Connection:
+    def add_connection_to_reply(
+        self, reply : PathCompReply, connection_uuid : str, service : Service, path_hops : List[Dict]
+    ) -> Connection:
         connection = reply.connections.add()
-        connection.connection_id.connection_uuid.uuid = str(uuid.uuid4())
+
+        connection.connection_id.connection_uuid.uuid = connection_uuid
         connection.service_id.CopyFrom(service.service_id)
-        return connection
 
-    def add_service_to_reply(self, reply : PathCompReply, context_uuid : str, service_uuid : str) -> Service:
-        service_key = (context_uuid, service_uuid)
-        tuple_service = self.service_dict.get(service_key)
-        if tuple_service is None: raise Exception('ServiceKey({:s}) not found'.format(str(service_key)))
-        _, grpc_service = tuple_service
+        for path_hop in path_hops:
+            device_uuid = path_hop['device']
+
+            ingress_endpoint_uuid = path_hop['ingress_ep']
+            endpoint_id = connection.path_hops_endpoint_ids.add()
+            endpoint_id.CopyFrom(self.endpoint_dict[device_uuid][ingress_endpoint_uuid][1])
 
+            egress_endpoint_uuid = path_hop['egress_ep']
+            endpoint_id = connection.path_hops_endpoint_ids.add()
+            endpoint_id.CopyFrom(self.endpoint_dict[device_uuid][egress_endpoint_uuid][1])
+
+        return connection
+
+    def add_service_to_reply(
+        self, reply : PathCompReply, context_uuid : str, service_uuid : str,
+        device_layer : Optional[DeviceLayerEnum] = None, path_hops : List[Dict] = []
+    ) -> Service:
         # TODO: implement support for multi-point services
         # Control deactivated to enable disjoint paths with multiple redundant endpoints on each side
-        #service_endpoint_ids = grpc_service.service_endpoint_ids
+        #service_endpoint_ids = service.service_endpoint_ids
         #if len(service_endpoint_ids) != 2: raise NotImplementedError('Service must have 2 endpoints')
 
-        service = reply.services.add()
-        service.CopyFrom(grpc_service)
-
-        return grpc_service
+        service_key = (context_uuid, service_uuid)
+        tuple_service = self.service_dict.get(service_key)
+        if tuple_service is not None:
+            service = reply.services.add()
+            service.CopyFrom(tuple_service[1])
+        else:
+            service = reply.services.add()
+            service.service_id.context_id.context_uuid.uuid = context_uuid
+            service.service_id.service_uuid.uuid = service_uuid
+
+            if device_layer is not None:
+                service_type = DEVICE_LAYER_TO_SERVICE_TYPE.get(device_layer.value)
+                if service_type is None:
+                    MSG = 'Unable to map DeviceLayer({:s}) to ServiceType'
+                    raise Exception(MSG.format(str(device_layer)))
+                service.service_type = service_type
+
+            service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED
+
+            if path_hops is not None and len(path_hops) > 0:
+                ingress_endpoint_id = service.service_endpoint_ids.add()
+                ingress_endpoint_id.device_id.device_uuid.uuid = path_hops[0]['device']
+                ingress_endpoint_id.endpoint_uuid.uuid = path_hops[0]['ingress_ep']
+
+                egress_endpoint_id = service.service_endpoint_ids.add()
+                egress_endpoint_id.device_id.device_uuid.uuid = path_hops[-1]['device']
+                egress_endpoint_id.endpoint_uuid.uuid = path_hops[-1]['egress_ep']
+
+        return service
 
     def get_reply(self) -> PathCompReply:
         response_list = self.json_reply.get('response-list', [])
         reply = PathCompReply()
+        grpc_services : Dict[Tuple[str, str], Service] = {}
+        grpc_connections : Dict[str, Connection] = {}
         for response in response_list:
             service_id = response['serviceId']
-            grpc_service = self.add_service_to_reply(reply, service_id['contextId'], service_id['service_uuid'])
+            context_uuid = service_id['contextId']
+            service_uuid = service_id['service_uuid']
+            service_key = (context_uuid, service_uuid)
+            grpc_services[service_key] = self.add_service_to_reply(reply, context_uuid, service_uuid)
 
             no_path_issue = response.get('noPath', {}).get('issue')
             if no_path_issue is not None:
@@ -148,9 +187,38 @@ class _Algorithm:
                 # no_path_issue == 1 => no path due to a constraint
                 continue
 
-            for service_path in response['path']:
-                grpc_connection = self.add_connection_to_reply(reply, grpc_service)
-                self.add_path_to_connection(grpc_connection, service_path['devices'])
+            for service_path_ero in response['path']:
+                path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict)
+                connections = convert_explicit_path_hops_to_connections(path_hops, self.device_dict, service_uuid)
+
+                for connection in connections:
+                    connection_uuid,device_layer,path_hops,_ = connection
+                    service_key = (context_uuid, connection_uuid)
+                    grpc_service = grpc_services.get(service_key)
+                    if grpc_service is None:
+                        grpc_service = self.add_service_to_reply(
+                            reply, context_uuid, connection_uuid, device_layer=device_layer, path_hops=path_hops)
+                        grpc_services[service_key] = grpc_service
+
+                for connection in connections:
+                    connection_uuid,device_layer,path_hops,dependencies = connection
+
+                    service_key = (context_uuid, connection_uuid)
+                    grpc_service = grpc_services.get(service_key)
+                    if grpc_service is None: raise Exception('Service({:s}) not found'.format(str(service_key)))
+                        
+                    grpc_connection = grpc_connections.get(connection_uuid)
+                    if grpc_connection is not None: continue
+                    grpc_connection = self.add_connection_to_reply(reply, connection_uuid, grpc_service, path_hops)
+                    grpc_connections[connection_uuid] = grpc_connection
+
+                    for service_uuid in dependencies:
+                        sub_service_key = (context_uuid, service_uuid)
+                        grpc_sub_service = grpc_services.get(sub_service_key)
+                        if grpc_sub_service is None:
+                            raise Exception('Service({:s}) not found'.format(str(sub_service_key)))
+                        grpc_sub_service_id = grpc_connection.sub_service_ids.add()
+                        grpc_sub_service_id.CopyFrom(grpc_sub_service.service_id)
 
                 # ... "path-capacity": {"total-size": {"value": 200, "unit": 0}},
                 # ... "path-latency": {"fixed-latency-characteristic": "10.000000"},
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py b/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py
index d24597c6beac4d4a27c7cb141c3349b862076f20..7745043318a46649b26d24ae99abaed0577d6171 100644
--- a/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py
+++ b/src/pathcomp/frontend/service/algorithms/tools/ComposeRequest.py
@@ -17,7 +17,10 @@ from typing import Dict
 from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
 from common.proto.context_pb2 import Constraint, Device, EndPointId, Link, Service, ServiceId, TopologyId
 from common.tools.grpc.Tools import grpc_message_to_json_string
-from .Constants import CapacityUnit, LinkForwardingDirection, LinkPortDirection, TerminationDirection, TerminationState
+from .ConstantsMappings import (
+    CapacityUnit, LinkForwardingDirection, LinkPortDirection, TerminationDirection, TerminationState)
+
+LOGGER = logging.getLogger(__name__)
 
 LOGGER = logging.getLogger(__name__)
 
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py
new file mode 100644
index 0000000000000000000000000000000000000000..f2c66cb24ca3c15c71f22dbe4eeca634e18d985a
--- /dev/null
+++ b/src/pathcomp/frontend/service/algorithms/tools/ComputeSubServices.py
@@ -0,0 +1,96 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Convert the path defined as explicit hops with ingress and egress endpoints per device into a set of connections and
+# compute the dependencies among them.
+#
+# Example:
+# o-- int DC1 eth1 -- 10/1 CS1 1/2 -- 1/2 R2 2/1 -- a7.. OLS 60.. -- 2/1 R3 1/1 -- 1/1 CS2 10/1 -- eth1 DC2 int --o
+#         APP              PKT            PKT            CTRL            PKT           PKT              APP
+#
+# path_hops = [
+#     {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'},
+#     {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'},
+#     {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'},
+#     {'device': 'TN-OLS', 'ingress_ep': 'a7a80b23a703', 'egress_ep': '60519106029e'},
+#     {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'},
+#     {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'},
+#     {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'}
+# ]
+#
+# connections=[
+#     (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), <DeviceLayerEnum.OPTICAL_CONTROLLER: 1>, [
+#             {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'}
+#         ], []),
+#     (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), <DeviceLayerEnum.PACKET_DEVICE: 30>, [
+#             {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'},
+#             {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'},
+#             {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'},
+#             {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'}
+#         ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]),
+#     (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), <DeviceLayerEnum.APPLICATION_DEVICE: 40>, [
+#             {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'},
+#             {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'}
+#         ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')])
+# ]
+
+import queue, uuid
+from typing import Dict, List, Tuple
+from common.proto.context_pb2 import Device
+from .ConstantsMappings import DEVICE_TYPE_TO_LAYER, DeviceLayerEnum
+
+def convert_explicit_path_hops_to_connections(
+    path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], main_connection_uuid : str
+) -> List[Tuple[str, DeviceLayerEnum, List[str], List[str]]]:
+
+    connection_stack = queue.LifoQueue()
+    connections : List[Tuple[str, DeviceLayerEnum, List[str], List[str]]] = list()
+    old_device_layer = None
+    last_device_uuid = None
+    for path_hop in path_hops:
+        device_uuid = path_hop['device']
+        if last_device_uuid == device_uuid: continue
+        device_tuple = device_dict.get(device_uuid)
+        if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid)))
+        json_device,_ = device_tuple
+        device_type = json_device['device_type']
+        device_layer = DEVICE_TYPE_TO_LAYER.get(device_type)
+        if device_layer is None: raise Exception('Undefined Layer for DeviceType({:s})'.format(str(device_type)))
+
+        if old_device_layer is None:
+            # path ingress
+            connection_stack.put((main_connection_uuid, device_layer, [path_hop], []))
+        elif old_device_layer > device_layer:
+            # underlying connection begins
+            connection_uuid = str(uuid.uuid4())
+            connection_stack.put((connection_uuid, device_layer, [path_hop], []))
+        elif old_device_layer == device_layer:
+            # same connection continues
+            connection_stack.queue[-1][2].append(path_hop)
+        elif old_device_layer < device_layer:
+            # underlying connection ended
+            connection = connection_stack.get()
+            connections.append(connection)
+            connection_stack.queue[-1][3].append(connection[0])
+            connection_stack.queue[-1][2].append(path_hop)
+        else:
+            raise Exception('Uncontrolled condition')
+
+        old_device_layer = device_layer
+        last_device_uuid = device_uuid
+
+    # path egress
+    connections.append(connection_stack.get())
+    assert connection_stack.empty()
+    return connections
diff --git a/src/pathcomp/frontend/service/algorithms/tools/Constants.py b/src/pathcomp/frontend/service/algorithms/tools/Constants.py
deleted file mode 100644
index cb774669c97144fa65afdaf0f3373c67a67c3212..0000000000000000000000000000000000000000
--- a/src/pathcomp/frontend/service/algorithms/tools/Constants.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from enum import IntEnum
-
-class CapacityUnit(IntEnum):
-    TB   = 0
-    TBPS = 1
-    GB   = 2
-    GBPS = 3
-    MB   = 4
-    MBPS = 5
-    KB   = 6
-    KBPS = 7
-    GHZ  = 8
-    MHZ  = 9
-
-CAPACITY_MULTIPLIER = {
-    CapacityUnit.TB   : 1.e12,
-    CapacityUnit.TBPS : 1.e12,
-    CapacityUnit.GB   : 1.e9,
-    CapacityUnit.GBPS : 1.e9,
-    CapacityUnit.MB   : 1.e6,
-    CapacityUnit.MBPS : 1.e6,
-    CapacityUnit.KB   : 1.e3,
-    CapacityUnit.KBPS : 1.e3,
-    CapacityUnit.GHZ  : 1.e9,
-    CapacityUnit.MHZ  : 1.e6,
-}
-
-class LinkPortDirection(IntEnum):
-    BIDIRECTIONAL = 0
-    INPUT         = 1
-    OUTPUT        = 2
-    UNKNOWN       = 3
-
-class TerminationDirection(IntEnum):
-    BIDIRECTIONAL = 0
-    SINK          = 1
-    SOURCE        = 2
-    UNKNOWN       = 3
-
-class TerminationState(IntEnum):
-    CAN_NEVER_TERMINATE         = 0
-    NOT_TERMINATED              = 1
-    TERMINATED_SERVER_TO_CLIENT = 2
-    TERMINATED_CLIENT_TO_SERVER = 3
-    TERMINATED_BIDIRECTIONAL    = 4
-    PERMENANTLY_TERMINATED      = 5
-    TERMINATION_STATE_UNKNOWN   = 6
-
-class LinkForwardingDirection(IntEnum):
-    BIDIRECTIONAL  = 0
-    UNIDIRECTIONAL = 1
-    UNKNOWN        = 2
diff --git a/src/pathcomp/frontend/service/algorithms/tools/ConstantsMappings.py b/src/pathcomp/frontend/service/algorithms/tools/ConstantsMappings.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e4f5408398cca012dca52fb19bf11a2b84a5721
--- /dev/null
+++ b/src/pathcomp/frontend/service/algorithms/tools/ConstantsMappings.py
@@ -0,0 +1,105 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from enum import IntEnum
+from common.DeviceTypes import DeviceTypeEnum
+from common.proto.context_pb2 import ServiceTypeEnum
+
+class CapacityUnit(IntEnum):
+    TB   = 0
+    TBPS = 1
+    GB   = 2
+    GBPS = 3
+    MB   = 4
+    MBPS = 5
+    KB   = 6
+    KBPS = 7
+    GHZ  = 8
+    MHZ  = 9
+
+CAPACITY_MULTIPLIER = {
+    CapacityUnit.TB   : 1.e12,
+    CapacityUnit.TBPS : 1.e12,
+    CapacityUnit.GB   : 1.e9,
+    CapacityUnit.GBPS : 1.e9,
+    CapacityUnit.MB   : 1.e6,
+    CapacityUnit.MBPS : 1.e6,
+    CapacityUnit.KB   : 1.e3,
+    CapacityUnit.KBPS : 1.e3,
+    CapacityUnit.GHZ  : 1.e9,
+    CapacityUnit.MHZ  : 1.e6,
+}
+
+class LinkPortDirection(IntEnum):
+    BIDIRECTIONAL = 0
+    INPUT         = 1
+    OUTPUT        = 2
+    UNKNOWN       = 3
+
+class TerminationDirection(IntEnum):
+    BIDIRECTIONAL = 0
+    SINK          = 1
+    SOURCE        = 2
+    UNKNOWN       = 3
+
+class TerminationState(IntEnum):
+    CAN_NEVER_TERMINATE         = 0
+    NOT_TERMINATED              = 1
+    TERMINATED_SERVER_TO_CLIENT = 2
+    TERMINATED_CLIENT_TO_SERVER = 3
+    TERMINATED_BIDIRECTIONAL    = 4
+    PERMENANTLY_TERMINATED      = 5
+    TERMINATION_STATE_UNKNOWN   = 6
+
+class LinkForwardingDirection(IntEnum):
+    BIDIRECTIONAL  = 0
+    UNIDIRECTIONAL = 1
+    UNKNOWN        = 2
+
+class DeviceLayerEnum(IntEnum):
+    APPLICATION_CONTROLLER = 41     # Layer 4 domain controller
+    APPLICATION_DEVICE     = 40     # Layer 4 domain device
+    PACKET_CONTROLLER      = 31     # Layer 3 domain controller
+    PACKET_DEVICE          = 30     # Layer 3 domain device
+    MAC_LAYER_CONTROLLER   = 21     # Layer 2 domain controller
+    MAC_LAYER_DEVICE       = 20     # Layer 2 domain device
+    OPTICAL_CONTROLLER     =  1     # Layer 0 domain controller
+    OPTICAL_DEVICE         =  0     # Layer 0 domain device
+
+DEVICE_TYPE_TO_LAYER = {
+    DeviceTypeEnum.EMULATED_DATACENTER.value      : DeviceLayerEnum.APPLICATION_DEVICE,
+    DeviceTypeEnum.DATACENTER.value               : DeviceLayerEnum.APPLICATION_DEVICE,
+
+    DeviceTypeEnum.EMULATED_PACKET_ROUTER.value   : DeviceLayerEnum.PACKET_DEVICE,
+    DeviceTypeEnum.PACKET_ROUTER.value            : DeviceLayerEnum.PACKET_DEVICE,
+    DeviceTypeEnum.PACKET_SWITCH.value            : DeviceLayerEnum.MAC_LAYER_DEVICE,
+    DeviceTypeEnum.P4_SWITCH.value                : DeviceLayerEnum.MAC_LAYER_DEVICE,
+
+    DeviceTypeEnum.MICROVAWE_RADIO_SYSTEM.value   : DeviceLayerEnum.MAC_LAYER_CONTROLLER,
+
+    DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value: DeviceLayerEnum.OPTICAL_CONTROLLER,
+    DeviceTypeEnum.OPEN_LINE_SYSTEM.value         : DeviceLayerEnum.OPTICAL_CONTROLLER,
+
+    DeviceTypeEnum.OPTICAL_ROADM.value            : DeviceLayerEnum.OPTICAL_DEVICE,
+    DeviceTypeEnum.OPTICAL_TRANDPONDER.value      : DeviceLayerEnum.OPTICAL_DEVICE,
+}
+
+DEVICE_LAYER_TO_SERVICE_TYPE = {
+    DeviceLayerEnum.APPLICATION_DEVICE.value: ServiceTypeEnum.SERVICETYPE_L3NM,
+
+    DeviceLayerEnum.PACKET_DEVICE.value     : ServiceTypeEnum.SERVICETYPE_L3NM,
+    DeviceLayerEnum.MAC_LAYER_DEVICE.value  : ServiceTypeEnum.SERVICETYPE_L2NM,
+
+    DeviceLayerEnum.OPTICAL_CONTROLLER.value: ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
+}
diff --git a/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py b/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py
new file mode 100644
index 0000000000000000000000000000000000000000..021940937c23a7cb461a603aa32a15f16626eb1d
--- /dev/null
+++ b/src/pathcomp/frontend/service/algorithms/tools/EroPathToHops.py
@@ -0,0 +1,76 @@
+# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Convert the Explicit Route Object (ERO)-like paths produced by the PathComp component (response['path']) into
+# explicit hops with ingress and egress endpoints per device (path_hops).
+#
+# response['path'] = [{
+#     'path-capacity': {'total-size': {'value': 200, 'unit': 0}},
+#     'path-latency': {'fixed-latency-characteristic': '12.000000'},
+#     'path-cost': {'cost-name': '', 'cost-value': '6.000000', 'cost-algorithm': '0.000000'},
+#     'devices': [
+#         {'device_id': 'DC1-GW', 'endpoint_uuid': 'int'},
+#         {'device_id': 'DC1-GW', 'endpoint_uuid': 'eth1'},
+#         {'device_id': 'CS1-GW1', 'endpoint_uuid': '1/2'},
+#         {'device_id': 'TN-R2', 'endpoint_uuid': '2/1'},
+#         {'device_id': 'TN-OLS', 'endpoint_uuid': 'ca46812e8ad7'},
+#         {'device_id': 'TN-R3', 'endpoint_uuid': '1/1'},
+#         {'device_id': 'CS2-GW1', 'endpoint_uuid': '10/1'},
+#         {'device_id': 'DC2-GW', 'endpoint_uuid': 'int'}
+#     ]
+# }]
+#
+# path_hops = [
+#   {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'},
+#   {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'},
+#   {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'},
+#   {'device': 'TN-OLS', 'ingress_ep': '951f2f57e4a4', 'egress_ep': 'ca46812e8ad7'},
+#   {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'},
+#   {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'},
+#   {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'}
+# ]
+#
+
+from typing import Dict, List
+
+def eropath_to_hops(ero_path : List[Dict], endpoint_to_link_dict : Dict) -> List[Dict]:
+    path_hops = []
+    for endpoint in ero_path:
+        device_uuid = endpoint['device_id']
+        endpoint_uuid = endpoint['endpoint_uuid']
+
+        if len(path_hops) == 0:
+            path_hops.append({'device': device_uuid, 'ingress_ep': endpoint_uuid})
+            continue
+
+        last_hop = path_hops[-1]
+        if (last_hop['device'] == device_uuid):
+            if ('ingress_ep' not in last_hop) or ('egress_ep' in last_hop): continue
+            last_hop['egress_ep'] = endpoint_uuid
+            continue
+
+        endpoint_key = (last_hop['device'], last_hop['egress_ep'])
+        link_tuple = endpoint_to_link_dict.get(endpoint_key)
+        ingress = next(iter([
+            ep_id for ep_id in link_tuple[0]['link_endpoint_ids']
+            if (ep_id['endpoint_id']['device_id'] == device_uuid) and\
+                (ep_id['endpoint_id']['endpoint_uuid'] != endpoint_uuid)
+        ]), None)
+        if ingress['endpoint_id']['device_id'] != device_uuid: raise Exception('Malformed path')
+        path_hops.append({
+            'device': ingress['endpoint_id']['device_id'],
+            'ingress_ep': ingress['endpoint_id']['endpoint_uuid'],
+            'egress_ep': endpoint_uuid,
+        })
+    return path_hops
diff --git a/src/pathcomp/frontend/tests/MockService_Dependencies.py b/src/pathcomp/frontend/tests/MockService_Dependencies.py
index b5fe85aa9cec8dd3e3993493abf8a26956a1a886..16ff9a5efca5827fdb531dad74aabff29507a580 100644
--- a/src/pathcomp/frontend/tests/MockService_Dependencies.py
+++ b/src/pathcomp/frontend/tests/MockService_Dependencies.py
@@ -17,18 +17,12 @@ from typing import Union
 from common.Constants import ServiceNameEnum
 from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name
 from common.proto.context_pb2_grpc import add_ContextServiceServicer_to_server
-from common.proto.device_pb2_grpc import add_DeviceServiceServicer_to_server
-from common.proto.service_pb2_grpc import add_ServiceServiceServicer_to_server
 from common.tests.MockServicerImpl_Context import MockServicerImpl_Context
-from common.tests.MockServicerImpl_Device import MockServicerImpl_Device
-from common.tests.MockServicerImpl_Service import MockServicerImpl_Service
 from common.tools.service.GenericGrpcService import GenericGrpcService
 
 LOCAL_HOST = '127.0.0.1'
 
 SERVICE_CONTEXT = ServiceNameEnum.CONTEXT
-SERVICE_DEVICE  = ServiceNameEnum.DEVICE
-SERVICE_SERVICE = ServiceNameEnum.SERVICE
 
 class MockService_Dependencies(GenericGrpcService):
     # Mock Service implementing Context, Device, and Service to simplify unitary tests of PathComp
@@ -41,18 +35,6 @@ class MockService_Dependencies(GenericGrpcService):
         self.context_servicer = MockServicerImpl_Context()
         add_ContextServiceServicer_to_server(self.context_servicer, self.server)
 
-        self.device_servicer = MockServicerImpl_Device()
-        add_DeviceServiceServicer_to_server(self.device_servicer, self.server)
-
-        self.service_servicer = MockServicerImpl_Service()
-        add_ServiceServiceServicer_to_server(self.service_servicer, self.server)
-
     def configure_env_vars(self):
         os.environ[get_env_var_name(SERVICE_CONTEXT, ENVVAR_SUFIX_SERVICE_HOST     )] = str(self.bind_address)
         os.environ[get_env_var_name(SERVICE_CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port)
-
-        os.environ[get_env_var_name(SERVICE_DEVICE, ENVVAR_SUFIX_SERVICE_HOST     )] = str(self.bind_address)
-        os.environ[get_env_var_name(SERVICE_DEVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port)
-
-        os.environ[get_env_var_name(SERVICE_SERVICE, ENVVAR_SUFIX_SERVICE_HOST     )] = str(self.bind_address)
-        os.environ[get_env_var_name(SERVICE_SERVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port)
diff --git a/src/pathcomp/frontend/tests/Objects_DC_CSGW_TN.py b/src/pathcomp/frontend/tests/Objects_DC_CSGW_TN.py
index d40d76454078177fd5b61455d0445859512463f4..06e9bbbc715a85a2c0d979584c58b268bff687e6 100644
--- a/src/pathcomp/frontend/tests/Objects_DC_CSGW_TN.py
+++ b/src/pathcomp/frontend/tests/Objects_DC_CSGW_TN.py
@@ -140,7 +140,7 @@ LINK_TNR2_TNR4_ID, LINK_TNR2_TNR4 = compose_link(DEV_TNR2_EPS[4], DEV_TNR4_EPS[4
 # ----- Service --------------------------------------------------------------------------------------------------------
 SERVICE_DC1GW_DC2GW = compose_service(DEV_DC1GW_EPS[2], DEV_DC2GW_EPS[2], constraints=[
     json_constraint_custom('bandwidth[gbps]', 10.0),
-    json_constraint_custom('latency[ms]',     12.0),
+    json_constraint_custom('latency[ms]',     20.0),
 ])
 
 # ----- Containers -----------------------------------------------------------------------------------------------------
diff --git a/src/pathcomp/frontend/tests/Objects_DC_CSGW_TN_OLS.py b/src/pathcomp/frontend/tests/Objects_DC_CSGW_TN_OLS.py
index a9b4a1512599ce4436b3498beb0b5ae4ebae92fb..99fd83ed9e1a7ca27faa6acb11b07abd573423ef 100644
--- a/src/pathcomp/frontend/tests/Objects_DC_CSGW_TN_OLS.py
+++ b/src/pathcomp/frontend/tests/Objects_DC_CSGW_TN_OLS.py
@@ -150,7 +150,7 @@ LINK_TNR4_TOLS_ID, LINK_TNR4_TOLS = compose_link(DEV_TNR4_EPS[2], DEV_TOLS_EPS[3
 # ----- Service --------------------------------------------------------------------------------------------------------
 SERVICE_DC1GW_DC2GW = compose_service(DEV_DC1GW_EPS[2], DEV_DC2GW_EPS[2], constraints=[
     json_constraint_custom('bandwidth[gbps]', 10.0),
-    json_constraint_custom('latency[ms]',     12.0),
+    json_constraint_custom('latency[ms]',     20.0),
 ])
 
 # ----- Containers -----------------------------------------------------------------------------------------------------
diff --git a/src/pathcomp/frontend/tests/PrepareTestScenario.py b/src/pathcomp/frontend/tests/PrepareTestScenario.py
index cb0d8e466ca418226ff96f77a1cfaffc9dbdb6af..2e7002b0f70b81f0bbe728a7b8139730d004221e 100644
--- a/src/pathcomp/frontend/tests/PrepareTestScenario.py
+++ b/src/pathcomp/frontend/tests/PrepareTestScenario.py
@@ -17,7 +17,6 @@ from common.Constants import ServiceNameEnum
 from common.Settings import (
     ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_service_port_grpc)
 from context.client.ContextClient import ContextClient
-from device.client.DeviceClient import DeviceClient
 from pathcomp.frontend.client.PathCompClient import PathCompClient
 from pathcomp.frontend.service.PathCompService import PathCompService
 from pathcomp.frontend.tests.MockService_Dependencies import MockService_Dependencies
@@ -43,15 +42,7 @@ def context_client(mock_service : MockService_Dependencies): # pylint: disable=r
     _client.close()
 
 @pytest.fixture(scope='session')
-def device_client(mock_service : MockService_Dependencies): # pylint: disable=redefined-outer-name
-    _client = DeviceClient()
-    yield _client
-    _client.close()
-
-@pytest.fixture(scope='session')
-def pathcomp_service(
-    context_client : ContextClient, # pylint: disable=redefined-outer-name
-    device_client : DeviceClient):  # pylint: disable=redefined-outer-name
+def pathcomp_service(context_client : ContextClient):       # pylint: disable=redefined-outer-name
 
     _service = PathCompService()
     _service.start()
diff --git a/src/pathcomp/frontend/tests/test_unitary.py b/src/pathcomp/frontend/tests/test_unitary.py
index c09d4c7100629449e250dc87019f69f8ce6c3429..53f4d7065e5ee847cd99f431c87c1231e52bbd63 100644
--- a/src/pathcomp/frontend/tests/test_unitary.py
+++ b/src/pathcomp/frontend/tests/test_unitary.py
@@ -28,8 +28,8 @@ from pathcomp.frontend.client.PathCompClient import PathCompClient
 
 # Scenarios:
 #from .Objects_A_B_C import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES
-from .Objects_DC_CSGW_TN import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES
-#from .Objects_DC_CSGW_TN_OLS import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES
+#from .Objects_DC_CSGW_TN import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES
+from .Objects_DC_CSGW_TN_OLS import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES
 
 # configure backend environment variables before overwriting them with fixtures to use real backend pathcomp
 DEFAULT_PATHCOMP_BACKEND_SCHEME  = 'http'
@@ -54,18 +54,17 @@ os.environ['PATHCOMP_BACKEND_PORT'] = os.environ.get('PATHCOMP_BACKEND_PORT', ba
 
 from .PrepareTestScenario import ( # pylint: disable=unused-import
     # be careful, order of symbols is important here!
-    mock_service, pathcomp_service, context_client, device_client, pathcomp_client)
+    mock_service, pathcomp_service, context_client, pathcomp_client)
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.setLevel(logging.DEBUG)
 
 def test_prepare_environment(
-    context_client : ContextClient, # pylint: disable=redefined-outer-name
-    device_client : DeviceClient):  # pylint: disable=redefined-outer-name
+    context_client : ContextClient):    # pylint: disable=redefined-outer-name
 
     for context  in CONTEXTS  : context_client.SetContext (Context (**context ))
     for topology in TOPOLOGIES: context_client.SetTopology(Topology(**topology))
-    for device   in DEVICES   : device_client .AddDevice  (Device  (**device  ))
+    for device   in DEVICES   : context_client.SetDevice  (Device  (**device  ))
     for link     in LINKS     : context_client.SetLink    (Link    (**link    ))
 
     for topology_id, device_ids, link_ids in OBJECTS_PER_TOPOLOGY:
@@ -268,10 +267,9 @@ def test_request_service_kdisjointpath(
 
 
 def test_cleanup_environment(
-    context_client : ContextClient, # pylint: disable=redefined-outer-name
-    device_client : DeviceClient):  # pylint: disable=redefined-outer-name
+    context_client : ContextClient):    # pylint: disable=redefined-outer-name
 
     for link     in LINKS     : context_client.RemoveLink    (LinkId    (**link    ['link_id'    ]))
-    for device   in DEVICES   : device_client .DeleteDevice  (DeviceId  (**device  ['device_id'  ]))
+    for device   in DEVICES   : context_client.RemoveDevice  (DeviceId  (**device  ['device_id'  ]))
     for topology in TOPOLOGIES: context_client.RemoveTopology(TopologyId(**topology['topology_id']))
     for context  in CONTEXTS  : context_client.RemoveContext (ContextId (**context ['context_id' ]))