diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleCondition.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleCondition.java
index df92c5c1af8a5bf47df980dcea4db1d2309351b4..e835a818846a1cb87e1accc66a90ff2fa4c51b30 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleCondition.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleCondition.java
@@ -16,6 +16,9 @@
 
 package eu.teraflow.policy.model;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import eu.teraflow.policy.monitoring.model.KpiValue;
 
 public class PolicyRuleCondition {
@@ -26,8 +29,18 @@ public class PolicyRuleCondition {
 
     public PolicyRuleCondition(
             String kpiId, NumericalOperator numericalOperator, KpiValue<?> kpiValue) {
+        checkNotNull(kpiId, "Kpi ID must not be null.");
+        checkArgument(!kpiId.isBlank(), "Kpi ID must not be empty.");
         this.kpiId = kpiId;
+        checkArgument(
+                numericalOperator != NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_UNDEFINED,
+                "Numerical operator cannot be undefined");
         this.numericalOperator = numericalOperator;
+        checkNotNull(kpiValue, "Kpi value must not be null.");
+        checkArgument(
+                isKpiValueValid(kpiValue),
+                "Kpi value must be: String, Float, Boolean or Integer but it was [%s].",
+                kpiValue.getValue().getClass().getName());
         this.kpiValue = kpiValue;
     }
 
@@ -43,6 +56,24 @@ public class PolicyRuleCondition {
         return kpiValue;
     }
 
+    private boolean isKpiValueValid(KpiValue<?> kpiValue) {
+        final var kpiValueType = kpiValue.getValue();
+
+        if (kpiValueType instanceof String) {
+            return true;
+        }
+
+        if (kpiValueType instanceof Boolean) {
+            return true;
+        }
+
+        if (kpiValueType instanceof Integer) {
+            return true;
+        }
+
+        return kpiValueType instanceof Float;
+    }
+
     @Override
     public String toString() {
         return String.format(
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleDevice.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleDevice.java
index 46151ea5c50f5b2f1b6542098594813e5cbc2a50..6dbbe2e00949ca1cfbb00f2db4b2aa01981417c3 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleDevice.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleDevice.java
@@ -16,6 +16,9 @@
 
 package eu.teraflow.policy.model;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import eu.teraflow.policy.common.Util;
 import java.util.List;
 
@@ -25,7 +28,10 @@ public class PolicyRuleDevice {
     private final List<String> deviceIds;
 
     public PolicyRuleDevice(PolicyRuleBasic policyRuleBasic, List<String> deviceIds) {
+        checkNotNull(policyRuleBasic, "PolicyRuleBasic must not be null.");
         this.policyRuleBasic = policyRuleBasic;
+        checkNotNull(deviceIds, "Device Ids must not be null.");
+        checkArgument(!deviceIds.isEmpty(), "Device Ids must not be empty.");
         this.deviceIds = deviceIds;
     }
 
diff --git a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleService.java b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleService.java
index ac0710d8590153ef8df1e9dbeb6465eac88c3fa1..fcdfffdb64ae68738adca5e417f7e852902c544b 100644
--- a/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleService.java
+++ b/src/policy/src/main/java/eu/teraflow/policy/model/PolicyRuleService.java
@@ -16,6 +16,8 @@
 
 package eu.teraflow.policy.model;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import eu.teraflow.policy.common.Util;
 import eu.teraflow.policy.context.model.ServiceId;
 import java.util.List;
@@ -28,8 +30,11 @@ public class PolicyRuleService {
 
     public PolicyRuleService(
             PolicyRuleBasic policyRuleBasic, ServiceId serviceId, List<String> deviceIds) {
+        checkNotNull(policyRuleBasic, "PolicyRuleBasic must not be null.");
         this.policyRuleBasic = policyRuleBasic;
+        checkNotNull(serviceId, "Service Id must not be null.");
         this.serviceId = serviceId;
+        checkNotNull(deviceIds, "Device Ids must not be null.");
         this.deviceIds = deviceIds;
     }
 
diff --git a/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleBasicValidationTest.java b/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleBasicValidationTest.java
index 955ad2a62255155bee0ec7f8aa171d91410d8519..53cddf6bea05162a9ee046c528da0055a1f8b467 100644
--- a/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleBasicValidationTest.java
+++ b/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleBasicValidationTest.java
@@ -56,7 +56,7 @@ class PolicyRuleBasicValidationTest {
     }
 
     private List<PolicyRuleCondition> createPolicyRuleConditions(
-            String kpiId, NumericalOperator numericalOperator, KpiValue kpiValue) {
+            String kpiId, NumericalOperator numericalOperator, KpiValue<?> kpiValue) {
         final var policyRuleCondition = new PolicyRuleCondition(kpiId, numericalOperator, kpiValue);
 
         return List.of(policyRuleCondition);
@@ -70,7 +70,7 @@ class PolicyRuleBasicValidationTest {
     }
 
     @Test
-    void shouldThrowIllegalArgumentExceptionGivenNullPolicyRuleId() {
+    void shouldThrowNullPointerExceptionGivenNullPolicyRuleId() {
         final var policyRuleConditions =
                 createPolicyRuleConditions(
                         UUID.randomUUID().toString(),
diff --git a/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleConditionValidationTest.java b/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleConditionValidationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..635876dcba59eef27273ff2bc24a7921991e2975
--- /dev/null
+++ b/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleConditionValidationTest.java
@@ -0,0 +1,139 @@
+/*
+* 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.
+*/
+
+package eu.teraflow.policy;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import eu.teraflow.policy.model.NumericalOperator;
+import eu.teraflow.policy.model.PolicyRuleCondition;
+import eu.teraflow.policy.monitoring.model.BooleanKpiValue;
+import eu.teraflow.policy.monitoring.model.FloatKpiValue;
+import eu.teraflow.policy.monitoring.model.IntegerKpiValue;
+import eu.teraflow.policy.monitoring.model.KpiValue;
+import eu.teraflow.policy.monitoring.model.StringKpiValue;
+import io.quarkus.test.junit.QuarkusTest;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mockito;
+
+@QuarkusTest
+class PolicyRuleConditionValidationTest {
+
+    private PolicyRuleCondition createPolicyRuleCondition(
+            String kpiId, NumericalOperator numericalOperator, KpiValue<?> kpiValue) {
+
+        return new PolicyRuleCondition(kpiId, numericalOperator, kpiValue);
+    }
+
+    private static Stream<Arguments> provideKpiValues() {
+        return Stream.of(
+                Arguments.of(new StringKpiValue("stringKpiValue")),
+                Arguments.of(new BooleanKpiValue(true)),
+                Arguments.of(new IntegerKpiValue(44)),
+                Arguments.of(new FloatKpiValue(12.3f)));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideKpiValues")
+    void shouldThrowNullPointerExceptionGivenNullKpiId(KpiValue<?> kpiValue) {
+        assertThatExceptionOfType(NullPointerException.class)
+                .isThrownBy(
+                        () ->
+                                createPolicyRuleCondition(
+                                        null,
+                                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_GREATER_THAN,
+                                        kpiValue));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideKpiValues")
+    void shouldThrowIllegalArgumentExceptionGivenEmptyKpiId(KpiValue<?> kpiValue) {
+        assertThatExceptionOfType(IllegalArgumentException.class)
+                .isThrownBy(
+                        () ->
+                                createPolicyRuleCondition(
+                                        "", NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_NOT_EQUAL, kpiValue));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideKpiValues")
+    void shouldThrowIllegalArgumentExceptionGivenWhiteSpacedKpiId(KpiValue<?> kpiValue) {
+        assertThatExceptionOfType(IllegalArgumentException.class)
+                .isThrownBy(
+                        () ->
+                                createPolicyRuleCondition(
+                                        " ", NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_NOT_EQUAL, kpiValue));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideKpiValues")
+    void shouldThrowIllegalArgumentExceptionGivenUndefinedNumericalOperator(KpiValue<?> kpiValue) {
+        assertThatExceptionOfType(IllegalArgumentException.class)
+                .isThrownBy(
+                        () ->
+                                createPolicyRuleCondition(
+                                        "kpiId",
+                                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_UNDEFINED,
+                                        kpiValue));
+    }
+
+    @Test
+    void shouldThrowNullPointerExceptionGivenNullKpiValue() {
+        assertThatExceptionOfType(NullPointerException.class)
+                .isThrownBy(
+                        () ->
+                                createPolicyRuleCondition(
+                                        "kpiId", NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_GREATER_THAN, null));
+    }
+
+    @Test
+    void shouldThrowIllegalArgumentExceptionIfIsKpiValueIsOfInvalidType() {
+        final var kpiValue = Mockito.mock(KpiValue.class);
+        Mockito.when(kpiValue.getValue()).thenReturn(1_2L);
+
+        assertThatExceptionOfType(IllegalArgumentException.class)
+                .isThrownBy(
+                        () ->
+                                createPolicyRuleCondition(
+                                        "kpiId",
+                                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_GREATER_THAN,
+                                        kpiValue));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideKpiValues")
+    void shouldCreatePolicyRuleConditionObject(KpiValue<?> kpiValue) {
+        final var expectedKpiId = "expectedKpiId";
+        final var expectedNumericalOperator =
+                NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_LESS_THAN;
+
+        final var expectedPolicyRuleCondition =
+                new PolicyRuleCondition(expectedKpiId, expectedNumericalOperator, kpiValue);
+
+        final var policyRuleCondition =
+                createPolicyRuleCondition(
+                        "expectedKpiId", NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_LESS_THAN, kpiValue);
+
+        assertThat(policyRuleCondition)
+                .usingRecursiveComparison()
+                .isEqualTo(expectedPolicyRuleCondition);
+    }
+}
diff --git a/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleDeviceValidationTest.java b/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleDeviceValidationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..064cd7ee03a35d453f8de46d12c8f205f4ab804d
--- /dev/null
+++ b/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleDeviceValidationTest.java
@@ -0,0 +1,178 @@
+/*
+* 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.
+*/
+
+package eu.teraflow.policy;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import eu.teraflow.policy.model.BooleanOperator;
+import eu.teraflow.policy.model.NumericalOperator;
+import eu.teraflow.policy.model.PolicyRuleAction;
+import eu.teraflow.policy.model.PolicyRuleActionEnum;
+import eu.teraflow.policy.model.PolicyRuleBasic;
+import eu.teraflow.policy.model.PolicyRuleCondition;
+import eu.teraflow.policy.model.PolicyRuleDevice;
+import eu.teraflow.policy.model.PolicyRuleState;
+import eu.teraflow.policy.model.RuleState;
+import eu.teraflow.policy.monitoring.model.IntegerKpiValue;
+import eu.teraflow.policy.monitoring.model.KpiValue;
+import io.quarkus.test.junit.QuarkusTest;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import org.junit.jupiter.api.Test;
+
+@QuarkusTest
+class PolicyRuleDeviceValidationTest {
+
+    private PolicyRuleBasic createPolicyRuleBasic(
+            String policyRuleId,
+            int priority,
+            PolicyRuleState policyRuleState,
+            BooleanOperator booleanOperator,
+            List<PolicyRuleCondition> policyRuleConditions,
+            List<PolicyRuleAction> policyRuleActions) {
+
+        return new PolicyRuleBasic(
+                policyRuleId,
+                policyRuleState,
+                priority,
+                policyRuleConditions,
+                booleanOperator,
+                policyRuleActions);
+    }
+
+    private List<PolicyRuleCondition> createPolicyRuleConditions(
+            String kpiId, NumericalOperator numericalOperator, KpiValue<?> kpiValue) {
+        final var policyRuleCondition = new PolicyRuleCondition(kpiId, numericalOperator, kpiValue);
+
+        return List.of(policyRuleCondition);
+    }
+
+    private List<PolicyRuleAction> createPolicyRuleActions(
+            PolicyRuleActionEnum policyRuleActionEnum, List<String> parameters) {
+        final var policyRuleAction = new PolicyRuleAction(policyRuleActionEnum, parameters);
+
+        return List.of(policyRuleAction);
+    }
+
+    private PolicyRuleDevice createPolicyRuleDevice(
+            PolicyRuleBasic policyRuleBasic, List<String> deviceIds) {
+
+        return new PolicyRuleDevice(policyRuleBasic, deviceIds);
+    }
+
+    private List<String> createDeviceIds() {
+        return List.of("deviceId1", "deviceId2");
+    }
+
+    @Test
+    void shouldThrowNullPointerExceptionGivenNullPolicyRuleBasic() {
+        final var deviceIds = createDeviceIds();
+
+        assertThatExceptionOfType(NullPointerException.class)
+                .isThrownBy(() -> createPolicyRuleDevice(null, deviceIds));
+    }
+
+    @Test
+    void shouldThrowNullPointerExceptionGivenNullDeviceIds() {
+        final var policyRuleConditions =
+                createPolicyRuleConditions(
+                        UUID.randomUUID().toString(),
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_LESS_THAN,
+                        new IntegerKpiValue(3));
+        final var policyRuleActions =
+                createPolicyRuleActions(
+                        PolicyRuleActionEnum.POLICY_RULE_ACTION_ADD_SERVICE_CONSTRAINT,
+                        List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
+
+        final var policyRuleState = new PolicyRuleState(RuleState.POLICY_EFFECTIVE);
+
+        final var policyRuleBasic =
+                createPolicyRuleBasic(
+                        "policyRuleId",
+                        3,
+                        policyRuleState,
+                        BooleanOperator.POLICYRULE_CONDITION_BOOLEAN_OR,
+                        policyRuleConditions,
+                        policyRuleActions);
+
+        assertThatExceptionOfType(NullPointerException.class)
+                .isThrownBy(() -> createPolicyRuleDevice(policyRuleBasic, null));
+    }
+
+    @Test
+    void shouldThrowIllegalArgumentExceptionGivenEmptyDeviceIds() {
+        final var policyRuleConditions =
+                createPolicyRuleConditions(
+                        UUID.randomUUID().toString(),
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_GREATER_THAN_EQUAL,
+                        new IntegerKpiValue(3));
+        final var policyRuleActions =
+                createPolicyRuleActions(
+                        PolicyRuleActionEnum.POLICY_RULE_ACTION_ADD_SERVICE_CONFIGRULE,
+                        List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
+
+        final var policyRuleState = new PolicyRuleState(RuleState.POLICY_EFFECTIVE);
+
+        final var policyRuleBasic =
+                createPolicyRuleBasic(
+                        "policyRuleId1",
+                        213,
+                        policyRuleState,
+                        BooleanOperator.POLICYRULE_CONDITION_BOOLEAN_AND,
+                        policyRuleConditions,
+                        policyRuleActions);
+
+        final var deviceIds = Collections.<String>emptyList();
+
+        assertThatExceptionOfType(IllegalArgumentException.class)
+                .isThrownBy(() -> createPolicyRuleDevice(policyRuleBasic, deviceIds));
+    }
+
+    @Test
+    void shouldCreatePolicyRuleDeviceObject() {
+        final var policyRuleConditions =
+                createPolicyRuleConditions(
+                        UUID.randomUUID().toString(),
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_NOT_EQUAL,
+                        new IntegerKpiValue(3));
+        final var policyRuleActions =
+                createPolicyRuleActions(
+                        PolicyRuleActionEnum.POLICY_RULE_ACTION_SET_DEVICE_STATUS,
+                        List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
+
+        final var policyRuleState = new PolicyRuleState(RuleState.POLICY_EFFECTIVE);
+
+        final var policyRuleBasic =
+                createPolicyRuleBasic(
+                        "policyRuleId",
+                        3,
+                        policyRuleState,
+                        BooleanOperator.POLICYRULE_CONDITION_BOOLEAN_OR,
+                        policyRuleConditions,
+                        policyRuleActions);
+
+        final var deviceIds = createDeviceIds();
+
+        final var expectedPolicyRuleDevice = new PolicyRuleDevice(policyRuleBasic, deviceIds);
+
+        final var policyRuleDevice = createPolicyRuleDevice(policyRuleBasic, deviceIds);
+
+        assertThat(policyRuleDevice).usingRecursiveComparison().isEqualTo(expectedPolicyRuleDevice);
+    }
+}
diff --git a/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleServiceValidationTest.java b/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleServiceValidationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b565b7e75b5cf9eb7748d0ba92e4fa3416b3b52e
--- /dev/null
+++ b/src/policy/src/test/java/eu/teraflow/policy/PolicyRuleServiceValidationTest.java
@@ -0,0 +1,222 @@
+/*
+* 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.
+*/
+
+package eu.teraflow.policy;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+import eu.teraflow.policy.context.model.ServiceId;
+import eu.teraflow.policy.model.BooleanOperator;
+import eu.teraflow.policy.model.NumericalOperator;
+import eu.teraflow.policy.model.PolicyRuleAction;
+import eu.teraflow.policy.model.PolicyRuleActionEnum;
+import eu.teraflow.policy.model.PolicyRuleBasic;
+import eu.teraflow.policy.model.PolicyRuleCondition;
+import eu.teraflow.policy.model.PolicyRuleService;
+import eu.teraflow.policy.model.PolicyRuleState;
+import eu.teraflow.policy.model.RuleState;
+import eu.teraflow.policy.monitoring.model.IntegerKpiValue;
+import eu.teraflow.policy.monitoring.model.KpiValue;
+import io.quarkus.test.junit.QuarkusTest;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import org.junit.jupiter.api.Test;
+
+@QuarkusTest
+class PolicyRuleServiceValidationTest {
+
+    private List<PolicyRuleCondition> createPolicyRuleConditions(
+            String kpiId, NumericalOperator numericalOperator, KpiValue<?> kpiValue) {
+        final var policyRuleCondition = new PolicyRuleCondition(kpiId, numericalOperator, kpiValue);
+
+        return List.of(policyRuleCondition);
+    }
+
+    private List<PolicyRuleAction> createPolicyRuleActions(
+            PolicyRuleActionEnum policyRuleActionEnum, List<String> parameters) {
+        final var policyRuleAction = new PolicyRuleAction(policyRuleActionEnum, parameters);
+
+        return List.of(policyRuleAction);
+    }
+
+    private PolicyRuleBasic createPolicyRuleBasic(
+            String policyRuleId,
+            int priority,
+            PolicyRuleState policyRuleState,
+            BooleanOperator booleanOperator,
+            List<PolicyRuleCondition> policyRuleConditions,
+            List<PolicyRuleAction> policyRuleActions) {
+
+        return new PolicyRuleBasic(
+                policyRuleId,
+                policyRuleState,
+                priority,
+                policyRuleConditions,
+                booleanOperator,
+                policyRuleActions);
+    }
+
+    private ServiceId createServiceId(String contextId, String id) {
+        return new ServiceId(contextId, id);
+    }
+
+    private List<String> createDeviceIds() {
+        return List.of("deviceIdA", "deviceIdB");
+    }
+
+    private PolicyRuleService createPolicyRuleService(
+            PolicyRuleBasic policyRuleBasic, ServiceId serviceId, List<String> deviceIds) {
+
+        return new PolicyRuleService(policyRuleBasic, serviceId, deviceIds);
+    }
+
+    @Test
+    void shouldThrowNullPointerExceptionGivenNullPolicyRuleBasic() {
+        final var serviceId = createServiceId("CONTEXT_ID", "id");
+        final var deviceIds = createDeviceIds();
+
+        assertThatExceptionOfType(NullPointerException.class)
+                .isThrownBy(() -> createPolicyRuleService(null, serviceId, deviceIds));
+    }
+
+    @Test
+    void shouldThrowNullPointerExceptionGivenNullServiceId() {
+        final var policyRuleConditions =
+                createPolicyRuleConditions(
+                        UUID.randomUUID().toString(),
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_GREATER_THAN_EQUAL,
+                        new IntegerKpiValue(3));
+        final var policyRuleActions =
+                createPolicyRuleActions(
+                        PolicyRuleActionEnum.POLICY_RULE_ACTION_ADD_SERVICE_CONSTRAINT,
+                        List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
+
+        final var policyRuleState = new PolicyRuleState(RuleState.POLICY_EFFECTIVE);
+
+        final var deviceIds = createDeviceIds();
+
+        final var policyRuleBasic =
+                createPolicyRuleBasic(
+                        "policyRuleId1",
+                        3,
+                        policyRuleState,
+                        BooleanOperator.POLICYRULE_CONDITION_BOOLEAN_OR,
+                        policyRuleConditions,
+                        policyRuleActions);
+
+        assertThatExceptionOfType(NullPointerException.class)
+                .isThrownBy(() -> createPolicyRuleService(policyRuleBasic, null, deviceIds));
+    }
+
+    @Test
+    void shouldThrowNullPointerExceptionGivenNullDeviceIds() {
+        final var serviceId = createServiceId("contextId", "ID");
+
+        final var policyRuleConditions =
+                createPolicyRuleConditions(
+                        UUID.randomUUID().toString(),
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_LESS_THAN,
+                        new IntegerKpiValue(3));
+        final var policyRuleActions =
+                createPolicyRuleActions(
+                        PolicyRuleActionEnum.POLICY_RULE_ACTION_ADD_SERVICE_CONFIGRULE,
+                        List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
+
+        final var policyRuleState = new PolicyRuleState(RuleState.POLICY_EFFECTIVE);
+
+        final var policyRuleBasic =
+                createPolicyRuleBasic(
+                        "policyRuleId2",
+                        2,
+                        policyRuleState,
+                        BooleanOperator.POLICYRULE_CONDITION_BOOLEAN_AND,
+                        policyRuleConditions,
+                        policyRuleActions);
+
+        assertThatExceptionOfType(NullPointerException.class)
+                .isThrownBy(() -> createPolicyRuleService(policyRuleBasic, serviceId, null));
+    }
+
+    @Test
+    void shouldCreatePolicyRuleServiceObjectGivenEmptyDeviceIds() {
+        final var serviceId = createServiceId("contextId", "id");
+        final var deviceIds = Collections.<String>emptyList();
+
+        final var policyRuleConditions =
+                createPolicyRuleConditions(
+                        UUID.randomUUID().toString(),
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_GREATER_THAN,
+                        new IntegerKpiValue(3));
+        final var policyRuleActions =
+                createPolicyRuleActions(
+                        PolicyRuleActionEnum.POLICY_RULE_ACTION_SET_DEVICE_STATUS,
+                        List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
+
+        final var policyRuleState = new PolicyRuleState(RuleState.POLICY_EFFECTIVE);
+
+        final var policyRuleBasic =
+                createPolicyRuleBasic(
+                        "policyRuleId",
+                        777,
+                        policyRuleState,
+                        BooleanOperator.POLICYRULE_CONDITION_BOOLEAN_AND,
+                        policyRuleConditions,
+                        policyRuleActions);
+
+        final var expectedPolicyRuleService =
+                new PolicyRuleService(policyRuleBasic, serviceId, deviceIds);
+
+        final var policyRuleService = createPolicyRuleService(policyRuleBasic, serviceId, deviceIds);
+
+        assertThat(policyRuleService).usingRecursiveComparison().isEqualTo(expectedPolicyRuleService);
+    }
+
+    @Test
+    void shouldCreatePolicyRuleServiceObject() {
+        final var serviceId = createServiceId("contextId", "id");
+        final var deviceIds = List.of("deviceIdA", "deviceIdB");
+
+        final var policyRuleConditions =
+                createPolicyRuleConditions(
+                        UUID.randomUUID().toString(),
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_LESS_THAN,
+                        new IntegerKpiValue(3));
+        final var policyRuleActions =
+                createPolicyRuleActions(
+                        PolicyRuleActionEnum.POLICY_RULE_ACTION_ADD_SERVICE_CONSTRAINT,
+                        List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
+
+        final var policyRuleState = new PolicyRuleState(RuleState.POLICY_EFFECTIVE);
+
+        final var policyRuleBasic =
+                createPolicyRuleBasic(
+                        "policyRuleId",
+                        3,
+                        policyRuleState,
+                        BooleanOperator.POLICYRULE_CONDITION_BOOLEAN_OR,
+                        policyRuleConditions,
+                        policyRuleActions);
+
+        final var expectedPolicyRuleService =
+                new PolicyRuleService(policyRuleBasic, serviceId, deviceIds);
+
+        final var policyRuleService = createPolicyRuleService(policyRuleBasic, serviceId, deviceIds);
+
+        assertThat(policyRuleService).usingRecursiveComparison().isEqualTo(expectedPolicyRuleService);
+    }
+}
diff --git a/src/policy/src/test/java/eu/teraflow/policy/PolicyServiceTest.java b/src/policy/src/test/java/eu/teraflow/policy/PolicyServiceTest.java
index 7e3044c1d2212d6bf8726c55673ff84e5d174f4f..3f123243204d46db6794a5ca067276d4cbfb7e14 100644
--- a/src/policy/src/test/java/eu/teraflow/policy/PolicyServiceTest.java
+++ b/src/policy/src/test/java/eu/teraflow/policy/PolicyServiceTest.java
@@ -144,11 +144,26 @@ class PolicyServiceTest {
     void shouldAddPolicyDevice() throws ExecutionException, InterruptedException, TimeoutException {
         CompletableFuture<String> message = new CompletableFuture<>();
 
+        final var expectedDeviceIdUuid1 =
+                serializer.serializeUuid("20db867c-772d-4872-9179-244ecafb3257");
+        final var expectedDeviceIdUuid2 =
+                serializer.serializeUuid("095974ac-d757-412d-b317-bcf355220aa9");
+
+        final var expectedDeviceId1 =
+                ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(expectedDeviceIdUuid1).build();
+        final var expectedDeviceId2 =
+                ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(expectedDeviceIdUuid2).build();
+
         final var policyRuleBasic = createPolicyRuleBasic();
+        final var deviceIds = List.of(expectedDeviceId1, expectedDeviceId2);
+
         final var expectedPolicyRuleState = policyRuleBasic.getPolicyRuleState();
 
         final var policyRuleDevice =
-                Policy.PolicyRuleDevice.newBuilder().setPolicyRuleBasic(policyRuleBasic).build();
+                Policy.PolicyRuleDevice.newBuilder()
+                        .setPolicyRuleBasic(policyRuleBasic)
+                        .addAllDeviceList(deviceIds)
+                        .build();
 
         client
                 .policyAddDevice(policyRuleDevice)
diff --git a/src/policy/src/test/java/eu/teraflow/policy/SerializerTest.java b/src/policy/src/test/java/eu/teraflow/policy/SerializerTest.java
index 755dc08d8ad93319a96022809af3ca780adf22f7..05f835d58f6552f3bd21f467b02026c9c509a67c 100644
--- a/src/policy/src/test/java/eu/teraflow/policy/SerializerTest.java
+++ b/src/policy/src/test/java/eu/teraflow/policy/SerializerTest.java
@@ -2367,8 +2367,30 @@ class SerializerTest {
         assertThat(subscriptionId).isEqualTo(expectedSubscriptionId);
     }
 
+    private static Stream<Arguments> provideNumericalOperators() {
+        return Stream.of(
+                Arguments.of(
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_EQUAL,
+                        PolicyCondition.NumericalOperator.POLICYRULE_CONDITION_NUMERICAL_EQUAL),
+                Arguments.of(
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_NOT_EQUAL,
+                        PolicyCondition.NumericalOperator.POLICYRULE_CONDITION_NUMERICAL_NOT_EQUAL),
+                Arguments.of(
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_LESS_THAN,
+                        PolicyCondition.NumericalOperator.POLICYRULE_CONDITION_NUMERICAL_LESS_THAN),
+                Arguments.of(
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_LESS_THAN_EQUAL,
+                        PolicyCondition.NumericalOperator.POLICYRULE_CONDITION_NUMERICAL_LESS_THAN_EQUAL),
+                Arguments.of(
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_GREATER_THAN,
+                        PolicyCondition.NumericalOperator.POLICYRULE_CONDITION_NUMERICAL_GREATER_THAN),
+                Arguments.of(
+                        NumericalOperator.POLICY_RULE_CONDITION_NUMERICAL_GREATER_THAN_EQUAL,
+                        PolicyCondition.NumericalOperator.POLICYRULE_CONDITION_NUMERICAL_GREATER_THAN_EQUAL));
+    }
+
     @ParameterizedTest
-    @MethodSource("provideNumericalOperator")
+    @MethodSource("provideNumericalOperators")
     void shouldSerializePolicyRuleConditionGivenMultipleNumericalOperators(
             NumericalOperator expectedNumericalOperator) {
         final var expectedPolicyRuleConditionKpiId = "expectedPolicyRuleConditionKpiId";
diff --git a/src/policy/target/kubernetes/kubernetes.yml b/src/policy/target/kubernetes/kubernetes.yml
index fa8f8df32cdd910f2be7801387325c502d401303..680929bc031df90594a743eb96316d3c99508cef 100644
--- a/src/policy/target/kubernetes/kubernetes.yml
+++ b/src/policy/target/kubernetes/kubernetes.yml
@@ -3,20 +3,20 @@ apiVersion: v1
 kind: Service
 metadata:
   annotations:
-    app.quarkus.io/commit-id: 164149a37bb250bca49a933b823f7c82877b277a
-    app.quarkus.io/build-timestamp: 2022-08-02 - 11:19:07 +0000
+    app.quarkus.io/commit-id: 7d24cfdf286fcca1ff04620ee026933bedd8960e
+    app.quarkus.io/build-timestamp: 2022-08-04 - 22:13:18 +0000
   labels:
     app.kubernetes.io/name: policyservice
     app: policyservice
   name: policyservice
 spec:
   ports:
-    - name: grpc
-      port: 6060
-      targetPort: 6060
     - name: http
       port: 8080
       targetPort: 8080
+    - name: grpc
+      port: 6060
+      targetPort: 6060
   selector:
     app.kubernetes.io/name: policyservice
   type: ClusterIP
@@ -25,8 +25,8 @@ apiVersion: apps/v1
 kind: Deployment
 metadata:
   annotations:
-    app.quarkus.io/commit-id: 164149a37bb250bca49a933b823f7c82877b277a
-    app.quarkus.io/build-timestamp: 2022-08-02 - 11:19:07 +0000
+    app.quarkus.io/commit-id: 7d24cfdf286fcca1ff04620ee026933bedd8960e
+    app.quarkus.io/build-timestamp: 2022-08-04 - 22:13:18 +0000
   labels:
     app: policyservice
     app.kubernetes.io/name: policyservice
@@ -39,8 +39,8 @@ spec:
   template:
     metadata:
       annotations:
-        app.quarkus.io/commit-id: 164149a37bb250bca49a933b823f7c82877b277a
-        app.quarkus.io/build-timestamp: 2022-08-02 - 11:19:07 +0000
+        app.quarkus.io/commit-id: 7d24cfdf286fcca1ff04620ee026933bedd8960e
+        app.quarkus.io/build-timestamp: 2022-08-04 - 22:13:18 +0000
       labels:
         app: policyservice
         app.kubernetes.io/name: policyservice
@@ -51,12 +51,12 @@ spec:
               valueFrom:
                 fieldRef:
                   fieldPath: metadata.namespace
-            - name: MONITORING_SERVICE_HOST
-              value: monitoringservice
-            - name: SERVICE_SERVICE_HOST
-              value: serviceservice
             - name: CONTEXT_SERVICE_HOST
               value: contextservice
+            - name: SERVICE_SERVICE_HOST
+              value: serviceservice
+            - name: MONITORING_SERVICE_HOST
+              value: monitoringservice
           image: registry.gitlab.com/teraflow-h2020/controller/policy:0.1.0
           imagePullPolicy: Always
           livenessProbe:
@@ -71,12 +71,12 @@ spec:
             timeoutSeconds: 10
           name: policyservice
           ports:
-            - containerPort: 6060
-              name: grpc
-              protocol: TCP
             - containerPort: 8080
               name: http
               protocol: TCP
+            - containerPort: 6060
+              name: grpc
+              protocol: TCP
           readinessProbe:
             failureThreshold: 3
             httpGet:
diff --git a/tutorial/1-1-1-create-vm-oracle-virtualbox.md b/tutorial/1-1-1-create-vm-oracle-virtualbox.md
index c53601e145d4ad0ca41c55803c27d55309070231..ea0da6cabfa46ba2c16166b07cc8c6e345da2246 100644
--- a/tutorial/1-1-1-create-vm-oracle-virtualbox.md
+++ b/tutorial/1-1-1-create-vm-oracle-virtualbox.md
@@ -26,7 +26,8 @@ __Note__: IP address 10.0.2.10 is the one that will be assigned to the VM.
 - RAM: 8 GB
 - Disk: 40 GB, Virtual Disk Image (VDI), Dynamically allocated
 - Optical Drive ISO Image: "ubuntu-20.04.4-live-server-amd64.iso"
-  (from [Ubuntu Server 20.04 LTS](https://releases.ubuntu.com/20.04/))
+  - Download the file "ubuntu-20.04.4-live-server-amd64.iso" from [Ubuntu 20.04 LTS](https://releases.ubuntu.com/20.04/).
+  - __Note__: use Ubuntu Server image instead of Ubuntu Desktop to create a lightweight VM.
 - Network Adapter 1 (*): enabled, attached to NAT Network "TFS-NAT-Net"
 - Minor adjustments (*):
   - Audio: disabled