diff --git a/report_coverage_common.sh b/report_coverage_common.sh
new file mode 100755
index 0000000000000000000000000000000000000000..be7224ad610ddf2c266fa485c6535f165cdc72ca
--- /dev/null
+++ b/report_coverage_common.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./report_coverage_all.sh | grep --color -E -i "^common/.*$|$"
diff --git a/report_coverage_common_orm.sh b/report_coverage_common_orm.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b28d4e8aecdba4fce5104af04efb08900362394e
--- /dev/null
+++ b/report_coverage_common_orm.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./report_coverage_all.sh | grep -v -E "^(cent|comp|cont|devi|moni|serv|test)" | grep --color -E -i "^common/orm/.*$|$"
diff --git a/src/common/orm/_Database.py b/src/common/orm/_Database.py
index 95d483361ec230294027320e9dd26cf1171a3f4e..c1a0a9065b33adf8309ae5394d7dbf1594abcfbf 100644
--- a/src/common/orm/_Database.py
+++ b/src/common/orm/_Database.py
@@ -1,7 +1,6 @@
 import logging
 from typing import List, Set
 from .backend._Backend import _Backend
-from .fields.PrimaryKeyField import PrimaryKeyField
 from .model.Model import Model
 from .Exceptions import MutexException
 
@@ -26,17 +25,16 @@ class _Database(Model):
     @property
     def backend_key(self) -> str: return ''
 
-    def __enter__(self) -> '_Database':
-        self._acquired, self._owner_key = self._backend.lock()
-        if not self._acquired: raise MutexException('Unable to acquire database lock')
-        return self
+    #def __enter__(self) -> '_Database':
+    #    self._acquired, self._owner_key = self._backend.lock()
+    #    if not self._acquired: raise MutexException('Unable to acquire database lock')
+    #    return self
 
-    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
-        self._backend.unlock(self._owner_key)
+    #def __exit__(self, exc_type, exc_val, exc_tb) -> None:
+    #    self._backend.unlock(self._owner_key)
 
     def clear_all(self, keep_keys : Set[str] = set()) -> None:
-        keys = self._backend.keys()
-        for key in keys:
+        for key in self._backend.keys():
             if key in keep_keys: continue
             self._backend.delete(key)
 
diff --git a/src/common/orm/backend/Tools.py b/src/common/orm/backend/Tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc24bbf363215c2c60a66ba2641d3070623851ca
--- /dev/null
+++ b/src/common/orm/backend/Tools.py
@@ -0,0 +1,5 @@
+from typing import List, Union
+
+def key_to_str(key : Union[str, List[str]]) -> str:
+    if isinstance(key, str): return key
+    return '/'.join(map(str, key))
diff --git a/src/common/orm/backend/_Backend.py b/src/common/orm/backend/_Backend.py
index 6d6697bc4a7e2877367f9cfbd815cc4a2e7a18b1..a8dd7eac293e101605c461ef9131deaf72e54507 100644
--- a/src/common/orm/backend/_Backend.py
+++ b/src/common/orm/backend/_Backend.py
@@ -1,49 +1,49 @@
-from typing import Dict, List, Set, Tuple
+from typing import Dict, List, Optional, Tuple
 
 class _Backend:
     def __init__(self, **settings) -> None:
         raise NotImplementedError()
 
-    def lock(self) -> Tuple[bool, str]:
+    def lock(self, keys : List[List[str]], owner_key : Optional[str] = None) -> Tuple[bool, str]:
         raise NotImplementedError()
 
-    def unlock(self, owner_key : str) -> bool:
+    def unlock(self, keys : List[List[str]], owner_key : str) -> bool:
         raise NotImplementedError()
 
     def keys(self) -> list:
         raise NotImplementedError()
 
-    def exists(self, key_name : str) -> bool:
+    def exists(self, key : List[str]) -> bool:
         raise NotImplementedError()
 
-    def delete(self, key_name : str) -> bool:
+    def delete(self, key : List[str]) -> bool:
         raise NotImplementedError()
 
-    def dict_get(self, key_name : str, fields : List[str] = []) -> Dict[str, str]:
+    def dict_get(self, key : List[str], fields : List[str] = []) -> Dict[str, str]:
         raise NotImplementedError()
 
-    def dict_update(self, key_name : str, update_fields : Dict[str,str] = {}, remove_fields : Set[str] = set()) -> None:
+    def dict_update(self, key : List[str], fields : Dict[str,str] = {}) -> None:
         raise NotImplementedError()
 
-    def dict_delete(self, key_name : str, fields : List[str] = []) -> None:
+    def dict_delete(self, key : List[str], fields : List[str] = []) -> None:
         raise NotImplementedError()
 
-    def list_get_all(self, key_name : str) -> List[str]:
+    def list_get_all(self, key : List[str]) -> List[str]:
         raise NotImplementedError()
 
-    def list_push_last(self, key_name : str, item : str) -> None:
+    def list_push_last(self, key : List[str], item : str) -> None:
         raise NotImplementedError()
 
-    def list_remove_first_occurrence(self, key_name : str, item: str) -> None:
+    def list_remove_first_occurrence(self, key : List[str], item: str) -> None:
         raise NotImplementedError()
 
-    def set_add(self, key_name : str, item : str) -> None:
+    def set_add(self, key : List[str], item : str) -> None:
         raise NotImplementedError()
 
-    def set_has(self, key_name : str, item : str) -> bool:
+    def set_has(self, key : List[str], item : str) -> bool:
         raise NotImplementedError()
 
-    def set_remove(self, key_name : str, item : str) -> None:
+    def set_remove(self, key : List[str], item : str) -> None:
         raise NotImplementedError()
 
     def dump(self) -> List[str]:
diff --git a/src/common/orm/backend/inmemory/InMemoryBackend.py b/src/common/orm/backend/inmemory/InMemoryBackend.py
index ee141600a508ee4e5ef4ea351e9c9d3541500133..13a38027555799e23cabaf32c3d8ef55d826ef15 100644
--- a/src/common/orm/backend/inmemory/InMemoryBackend.py
+++ b/src/common/orm/backend/inmemory/InMemoryBackend.py
@@ -1,99 +1,135 @@
+# InMemeory Database Backend
+# --------------------------
+# - Concurrency is limited to 1 operation at a time
+# - All operations are strictly sequential by means of locks
+# - WARNING: DESIGNED AND BUILT FOR UNIT TESTING AND INTEGRATION TESTING PURPOSES ONLY !!!
+#            USE ANOTHER BACKEND IN PRODUCTION ENVIRONMENTS.
+
 import copy, logging, threading, uuid
-from typing import Dict, List, Set, Tuple
+from typing import Dict, List, Optional, Set, Tuple, Union
 from .._Backend import _Backend
+from ..Tools import key_to_str
 from .Tools import get_or_create_dict, get_or_create_list, get_or_create_set
 
 LOGGER = logging.getLogger(__name__)
 
 class InMemoryBackend(_Backend):
     def __init__(self, **settings):
-        self._internal_lock = threading.Lock()
-        self._external_lock = threading.Lock()
-        self._owner_key = None
-        self._keys = {} # name => set/list/dict/string
-
-    def lock(self) -> Tuple[bool, str]:
-        owner_key = str(uuid.uuid4())
-        with self._internal_lock:
-            acquired = self._external_lock.acquire()
-            if not acquired: return False, None
-            self._owner_key = owner_key
+        self._lock = threading.Lock()
+        self._keys : Dict[str, Union[Set[str], List[str], Dict[str, str], str]]= {} # key => set/list/dict/string
+
+    def lock(self, keys : List[List[str]], owner_key : Optional[str] = None) -> Tuple[bool, str]:
+        # InMemoryBackend uses a database where all operations are atomic. Locks are implemented by assigning the lock
+        # owner key into a string variable. If the field is empty and enables to 
+        owner_key = str(uuid.uuid4()) if owner_key is None else owner_key
+        str_keys = {key_to_str(key) for key in keys}
+        with self._lock:
+            acquired_lock_keys : Dict[str, str] = {}
+            for str_key in str_keys:
+                if (str_key in self._keys) and (len(self._keys[str_key]) > 0) and (self._keys[str_key] != owner_key):
+                    # lock already acquired, cannot acquire all locks atomically
+                    for str_key in acquired_lock_keys.keys():
+                        if str_key not in self._keys: continue
+                        del self._keys[str_key]
+                    return False, None
+                else:
+                    # lock available, temporarily acquire it; locks will be released if some of them for a requested
+                    # key is not available
+                    self._keys[str_key] = owner_key
+                    acquired_lock_keys[str_key] = owner_key
             return True, owner_key
 
-    def unlock(self, owner_key : str) -> bool:
-        with self._internal_lock:
-            if self._owner_key != owner_key: return False
-            self._external_lock.release()
-            self._owner_key = None
+    def unlock(self, keys : List[List[str]], owner_key : str) -> bool:
+        str_keys = {key_to_str(key) for key in keys}
+        with self._lock:
+            for str_key in str_keys:
+                if str_key not in self._keys: return False
+                if self._keys[str_key] != owner_key: return False
+            # Up to here, we own all the keys we want to release
+            for str_key in str_keys:
+                del self._keys[str_key]
             return True
 
     def keys(self) -> list:
-        with self._internal_lock:
+        with self._lock:
             return copy.deepcopy(list(self._keys.keys()))
 
-    def exists(self, key_name : str) -> bool:
-        with self._internal_lock:
-            return key_name in self._keys
+    def exists(self, key : List[str]) -> bool:
+        str_key = key_to_str(key)
+        with self._lock:
+            return str_key in self._keys
 
-    def delete(self, key_name : str) -> bool:
-        with self._internal_lock:
-            if key_name not in self._keys: return False
-            del self._keys[key_name]
+    def delete(self, key : List[str]) -> bool:
+        str_key = key_to_str(key)
+        with self._lock:
+            if str_key not in self._keys: return False
+            del self._keys[str_key]
             return True
 
-    def dict_get(self, key_name : str, fields : List[str] = []) -> Dict[str, str]:
-        with self._internal_lock:
-            container = get_or_create_dict(self._keys, key_name)
+    def dict_get(self, key : List[str], fields : List[str] = []) -> Dict[str, str]:
+        str_key = key_to_str(key)
+        with self._lock:
+            container = get_or_create_dict(self._keys, str_key)
             if len(fields) == 0: fields = container.keys()
             return copy.deepcopy({
                 field_name : field_value for field_name,field_value in container.items() if field_name in fields
             })
 
-    def dict_update(self, key_name : str, update_fields : Dict[str,str] = {}, remove_fields : Set[str] = set()) -> None:
-        with self._internal_lock:
-            container = get_or_create_dict(self._keys, key_name)
-            for field in list(remove_fields): container.pop(field, None)
-            container.update(update_fields)
-
-    def dict_delete(self, key_name : str, fields : List[str] = []) -> None:
-        with self._internal_lock:
-            container = get_or_create_dict(self._keys, key_name)
-            if len(fields) == 0: fields = container.keys()
-            for field in list(fields): container.pop(field, None)
-
-    def list_get_all(self, key_name : str) -> List[str]:
-        with self._internal_lock:
-            container = get_or_create_list(self._keys, key_name)
+    def dict_update(self, key : List[str], fields : Dict[str,str] = {}) -> None:
+        str_key = key_to_str(key)
+        with self._lock:
+            container = get_or_create_dict(self._keys, str_key)
+            container.update(fields)
+
+    def dict_delete(self, key : List[str], fields : List[str] = []) -> None:
+        str_key = key_to_str(key)
+        with self._lock:
+            if len(fields) == 0:
+                if str_key not in self._keys: return False
+                del self._keys[str_key]
+            else:
+                container = get_or_create_dict(self._keys, str_key)
+                for field in list(fields): container.pop(field, None)
+
+    def list_get_all(self, key : List[str]) -> List[str]:
+        str_key = key_to_str(key)
+        with self._lock:
+            container = get_or_create_list(self._keys, str_key)
             return copy.deepcopy(container)
 
-    def list_push_last(self, key_name : str, item : str) -> None:
-        with self._internal_lock:
-            container = get_or_create_list(self._keys, key_name)
+    def list_push_last(self, key : List[str], item : str) -> None:
+        str_key = key_to_str(key)
+        with self._lock:
+            container = get_or_create_list(self._keys, str_key)
             container.append(item)
 
-    def list_remove_first_occurrence(self, key_name : str, item: str) -> None:
-        with self._internal_lock:
-            container = get_or_create_list(self._keys, key_name)
+    def list_remove_first_occurrence(self, key : List[str], item: str) -> None:
+        str_key = key_to_str(key)
+        with self._lock:
+            container = get_or_create_list(self._keys, str_key)
             container.remove(item)
 
-    def set_add(self, key_name : str, item : str) -> None:
-        with self._internal_lock:
-            container = get_or_create_set(self._keys, key_name)
+    def set_add(self, key : List[str], item : str) -> None:
+        str_key = key_to_str(key)
+        with self._lock:
+            container = get_or_create_set(self._keys, str_key)
             container.add(item)
 
-    def set_has(self, key_name : str, item : str) -> bool:
-        with self._internal_lock:
-            container = get_or_create_set(self._keys, key_name)
+    def set_has(self, key : List[str], item : str) -> bool:
+        str_key = key_to_str(key)
+        with self._lock:
+            container = get_or_create_set(self._keys, str_key)
             return item in container
 
-    def set_remove(self, key_name : str, item : str) -> None:
-        with self._internal_lock:
-            container = get_or_create_set(self._keys, key_name)
+    def set_remove(self, key : List[str], item : str) -> None:
+        str_key = key_to_str(key)
+        with self._lock:
+            container = get_or_create_set(self._keys, str_key)
             container.discard(item)
 
     def dump(self) -> List[Tuple[str, str, str]]:
-        with self._internal_lock:
+        with self._lock:
             entries = []
-            for key_name,key_value in self._keys.items():
-                entries.append((key_name, type(key_value).__name__, str(key_value)))
+            for str_key,key_value in self._keys.items():
+                entries.append((str_key, type(key_value).__name__, str(key_value)))
         return entries
diff --git a/src/common/orm/backend/inmemory/Tools.py b/src/common/orm/backend/inmemory/Tools.py
index 7eb18c94732915bb35e5bdd4087f79647028c600..f8fb573504bc6daa9622c79482fc0676a31058f2 100644
--- a/src/common/orm/backend/inmemory/Tools.py
+++ b/src/common/orm/backend/inmemory/Tools.py
@@ -1,19 +1,22 @@
 from typing import Dict, List, Set, Union
 
-def get_or_create_dict(keys : Dict[str, Union[Dict, List, Set]], key_name : str) -> Dict:
-    container = keys.get(key_name, None)
-    if container is None: container = keys.setdefault(key_name, dict())
-    if not isinstance(container, dict): raise Exception('Key({}) is not a dict'.format(key_name))
+def get_or_create_dict(keys : Dict[str, Union[Dict, List, Set]], str_key : str) -> Dict:
+    container = keys.get(str_key, None)
+    if container is None: container = keys.setdefault(str_key, dict())
+    if not isinstance(container, dict):
+        raise Exception('Key({:s}, {:s}) is not a dict'.format(str(type(container).__name__), str(str_key)))
     return container
 
-def get_or_create_list(keys : Dict[str, Union[Dict, List, Set]], key_name : str) -> List:
-    container = keys.get(key_name, None)
-    if container is None: container = keys.setdefault(key_name, list())
-    if not isinstance(container, list): raise Exception('Key({}) is not a list'.format(key_name))
+def get_or_create_list(keys : Dict[str, Union[Dict, List, Set]], str_key : str) -> List:
+    container = keys.get(str_key, None)
+    if container is None: container = keys.setdefault(str_key, list())
+    if not isinstance(container, list):
+        raise Exception('Key({:s}, {:s}) is not a list'.format(str(type(container).__name__), str(str_key)))
     return container
 
-def get_or_create_set(keys : Dict[str, Union[Dict, List, Set]], key_name : str) -> Set:
-    container = keys.get(key_name, None)
-    if container is None: container = keys.setdefault(key_name, set())
-    if not isinstance(container, set): raise Exception('Key({}) is not a set'.format(key_name))
+def get_or_create_set(keys : Dict[str, Union[Dict, List, Set]], str_key : str) -> Set:
+    container = keys.get(str_key, None)
+    if container is None: container = keys.setdefault(str_key, set())
+    if not isinstance(container, set):
+        raise Exception('Key({:s}, {:s}) is not a set'.format(str(type(container).__name__), str(str_key)))
     return container
diff --git a/src/common/orm/backend/redis/RedisBackend.py b/src/common/orm/backend/redis/RedisBackend.py
index b18cd54b60aa1e1282a72587241733fe3f28e9cc..028c735540950d2f2c58145f595be278934450b7 100644
--- a/src/common/orm/backend/redis/RedisBackend.py
+++ b/src/common/orm/backend/redis/RedisBackend.py
@@ -1,15 +1,14 @@
 import os, uuid
-from typing import Any, Dict, List, Set, Tuple
+from typing import Any, Dict, List, Optional, Tuple, Union
 from redis.client import Redis
 from .._Backend import _Backend
+from ..Tools import key_to_str
 from .Mutex import Mutex
 
 DEFAULT_SERVICE_HOST = '127.0.0.1'
 DEFAULT_SERVICE_PORT = 6379
 DEFAULT_DATABASE_ID  = 0
 
-KEY_ENTIRE_DATABASE_LOCK = 'everything'
-
 def get_setting(settings : Dict[str, Any], name : str, default : Any) -> Any:
     value = settings.get(name, os.environ.get(name))
     return default if value is None else value
@@ -22,46 +21,57 @@ class RedisBackend(_Backend):
         self._client = Redis.from_url('redis://{host}:{port}/{dbid}'.format(host=host, port=port, dbid=dbid))
         self._mutex = Mutex(self._client)
 
-    def lock(self) -> Tuple[bool, str]:
-        owner_key = str(uuid.uuid4())
-        return self._mutex.acquire(KEY_ENTIRE_DATABASE_LOCK, owner_key=owner_key, blocking=True)
+    def lock(self, keys : List[List[str]], owner_key : Optional[str] = None) -> Tuple[bool, str]:
+        str_keys = {key_to_str(key) for key in keys}
+        owner_key = str(uuid.uuid4()) if owner_key is None else owner_key
+        return self._mutex.acquire(str_keys, owner_key=owner_key, blocking=True)
 
-    def unlock(self, owner_key : str) -> bool:
-        return self._mutex.release(KEY_ENTIRE_DATABASE_LOCK, owner_key)
+    def unlock(self, keys : List[List[str]], owner_key : str) -> bool:
+        str_keys = {key_to_str(key) for key in keys}
+        return self._mutex.release(str_keys, owner_key)
 
     def keys(self) -> list:
         return [k.decode('UTF-8') for k in self._client.keys()]
 
-    def exists(self, key_name : str) -> bool:
-        return self._client.exists(key_name) == 1
+    def exists(self, key : List[str]) -> bool:
+        str_key = key_to_str(key)
+        return self._client.exists(str_key) == 1
 
-    def delete(self, key_name : str) -> bool:
-        return self._client.delete(key_name) == 1
+    def delete(self, key : List[str]) -> bool:
+        str_key = key_to_str(key)
+        return self._client.delete(str_key) == 1
 
-    def set_has(self, key_name : str, item : str) -> bool:
-        return self._client.sismember(key_name, item) == 1
+    def set_has(self, key : List[str], item : str) -> bool:
+        str_key = key_to_str(key)
+        return self._client.sismember(str_key, item) == 1
 
-    def set_add(self, key_name : str, item : str) -> None:
-        self._client.sadd(key_name, item)
+    def set_add(self, key : List[str], item : str) -> None:
+        str_key = key_to_str(key)
+        self._client.sadd(str_key, item)
 
-    def set_remove(self, key_name : str, item : str) -> None:
-        self._client.srem(key_name, item)
+    def set_remove(self, key : List[str], item : str) -> None:
+        str_key = key_to_str(key)
+        self._client.srem(str_key, item)
 
-    def list_push_last(self, key_name : str, item : str) -> None:
-        self._client.rpush(key_name, item)
+    def list_push_last(self, key : List[str], item : str) -> None:
+        str_key = key_to_str(key)
+        self._client.rpush(str_key, item)
 
-    def list_get_all(self, key_name : str) -> List[str]:
-        return list(map(lambda m: m.decode('UTF-8'), self._client.lrange(key_name, 0, -1)))
+    def list_get_all(self, key : List[str]) -> List[str]:
+        str_key = key_to_str(key)
+        return list(map(lambda m: m.decode('UTF-8'), self._client.lrange(str_key, 0, -1)))
 
-    def list_remove_first_occurrence(self, key_name : str, item: str) -> None:
-        self._client.lrem(key_name, 1, item)
+    def list_remove_first_occurrence(self, key : List[str], item: str) -> None:
+        str_key = key_to_str(key)
+        self._client.lrem(str_key, 1, item)
 
-    def dict_get(self, key_name : str, fields : List[str] = []) -> Dict[str, str]:
+    def dict_get(self, key : List[str], fields : List[str] = []) -> Dict[str, str]:
+        str_key = key_to_str(key)
         if len(fields) == 0:
-            keys_values = self._client.hgetall(key_name).items()
+            keys_values = self._client.hgetall(str_key).items()
         else:
             fields = list(fields)
-            keys_values = zip(fields, self._client.hmget(key_name, fields))
+            keys_values = zip(fields, self._client.hmget(str_key, fields))
 
         attributes = {}
         for key,value in keys_values:
@@ -69,25 +79,23 @@ class RedisBackend(_Backend):
             attributes[str_key] = value.decode('UTF-8') if isinstance(value, bytes) else value
         return attributes
 
-    def dict_update(
-        self, key_name : str, update_fields : Dict[str, str] = {}, remove_fields : Set[str] = set()) -> None:
-        if len(remove_fields) > 0:
-            self._client.hdel(key_name, *remove_fields)
-
-        if len(update_fields) > 0:
-            self._client.hset(key_name, mapping=update_fields)
+    def dict_update(self, key : List[str], fields : Dict[str, str] = {}) -> None:
+        str_key = key_to_str(key)
+        if len(fields) > 0:
+            self._client.hset(str_key, mapping=fields)
 
-    def dict_delete(self, key_name : str, fields : List[str] = []) -> None:
+    def dict_delete(self, key : List[str], fields : List[str] = []) -> None:
+        str_key = key_to_str(key)
         if len(fields) == 0:
-            self._client.delete(key_name)
+            self._client.delete(str_key)
         else:
-            self._client.hdel(key_name, set(fields))
+            self._client.hdel(str_key, set(fields))
 
     def dump(self) -> List[Tuple[str, str, str]]:
         entries = []
-        for key_name in self._client.keys():
-            key_name = key_name.decode('UTF-8')
-            key_type = self._client.type(key_name)
+        for str_key in self._client.keys():
+            str_key = str_key.decode('UTF-8')
+            key_type = self._client.type(str_key)
             if key_type is not None: key_type = key_type.decode('UTF-8')
             key_type = {
                 'hash'  : 'dict',
@@ -101,5 +109,5 @@ class RedisBackend(_Backend):
                 'set' : lambda key: {m.decode('UTF-8') for m in self._client.smembers(key)},
                 'str' : lambda key: self._client.get(key).decode('UTF-8'),
             }.get(key_type, lambda key: 'UNSUPPORTED_TYPE')
-            entries.append((key_name, key_type, key_content(key_name)))
+            entries.append((str_key, key_type, key_content(str_key)))
         return entries
diff --git a/src/common/orm/fields/BooleanField.py b/src/common/orm/fields/BooleanField.py
index 2eadaf5f3034cb11d2791b045cfb4fe7a1d9faab..e802b4e4bf1ed6b69b57a2d84b47010618f63bd7 100644
--- a/src/common/orm/fields/BooleanField.py
+++ b/src/common/orm/fields/BooleanField.py
@@ -1,12 +1,24 @@
-from typing import Any
+from __future__ import annotations
+from typing import TYPE_CHECKING
 from common.type_checkers.Checkers import chk_boolean, chk_not_none
 from .Field import Field
 
+if TYPE_CHECKING:
+    from ..model.Model import Model
+
+BOOL_TRUE_VALUES = {'TRUE', 'T', '1'}
+
 class BooleanField(Field):
     def __init__(self, *args, **kwargs) -> None:
         super().__init__(*args, type_=bool, **kwargs)
 
-    def __set__(self, instance, value : bool) -> None:
+    def __set__(self, instance : 'Model', value : bool) -> None:
         if self.required: chk_not_none(self.name, value)
         if value is None: super().__set__(instance, value)
         super().__set__(instance, chk_boolean(self.name, value))
+
+    def serialize(self, value : bool) -> str:
+        return str(value)
+
+    def deserialize(self, value : str) -> bool:
+        return (value.upper() in BOOL_TRUE_VALUES)
diff --git a/src/common/orm/fields/Field.py b/src/common/orm/fields/Field.py
index e10938c306dfd4f3934cacaf32654519e9c289e1..80ba0a8c8229ce6c68335e1e63627c808f9ec364 100644
--- a/src/common/orm/fields/Field.py
+++ b/src/common/orm/fields/Field.py
@@ -1,6 +1,10 @@
-from typing import Any, List, Set, Tuple, Union
+from __future__ import annotations
+from typing import TYPE_CHECKING, Any, List, Set, Tuple, Union
 from common.type_checkers.Checkers import chk_boolean, chk_string, chk_type
 
+if TYPE_CHECKING:
+    from ..model.Model import Model
+
 class Field:
     def __init__(
         self, name : str = None, type_ : Union[type, Set[type], Tuple[type], List[type]] = object,
@@ -9,8 +13,14 @@ class Field:
         self.type_ = chk_type('Field.type', type_, (type, set, tuple, list))
         self.required = chk_boolean('Field.required', required)
 
-    def __set__(self, instance, value : Any):
+    def __set__(self, instance : 'Model', value : Any) -> None:
         instance.__dict__[self.name] = value
 
-    def __delete__(self, instance):
+    def __delete__(self, instance : 'Model'):
         raise AttributeError('Attribute "{:s}" cannot be deleted'.format(self.name))
+
+    def serialize(self, value : Any) -> str:
+        raise NotImplementedError
+
+    def deserialize(self, value : str) -> Any:
+        raise NotImplementedError
diff --git a/src/common/orm/fields/FloatField.py b/src/common/orm/fields/FloatField.py
index f53cd25136ddb5326c7092a617110ca3007b7012..b1485b33f07d71dd684aa0b1b1dd87a2e3ec7058 100644
--- a/src/common/orm/fields/FloatField.py
+++ b/src/common/orm/fields/FloatField.py
@@ -1,7 +1,11 @@
-from typing import Optional
+from __future__ import annotations
+from typing import TYPE_CHECKING, Optional
 from common.type_checkers.Checkers import chk_float, chk_not_none
 from .Field import Field
 
+if TYPE_CHECKING:
+    from ..model.Model import Model
+
 class FloatField(Field):
     def __init__(
         self, *args, min_value : Optional[float] = None, max_value : Optional[float] = None, **kwargs) -> None:
@@ -12,7 +16,13 @@ class FloatField(Field):
         self._max_value = None if max_value is None else \
             chk_float('{}.max_value'.format(self.name), max_value, min_value=self._min_value)
 
-    def __set__(self, instance, value : float) -> None:
+    def __set__(self, instance : 'Model', value : float) -> None:
         if self.required: chk_not_none(self.name, value)
         if value is None: super().__set__(instance, value)
         super().__set__(instance, chk_float(self.name, value, min_value=self._min_value, max_value=self._max_value))
+
+    def serialize(self, value : float) -> str:
+        return str(value)
+
+    def deserialize(self, value : str) -> float:
+        return float(value)
diff --git a/src/common/orm/fields/ForeignKeyField.py b/src/common/orm/fields/ForeignKeyField.py
index f6e7fd394a6a0bd548238d017ba95b8fbed05eda..ac34403d5bd7e6edef98a160e0c2d1d1b71dcb2b 100644
--- a/src/common/orm/fields/ForeignKeyField.py
+++ b/src/common/orm/fields/ForeignKeyField.py
@@ -1,16 +1,20 @@
-from typing import Optional
-from .Field import Field
+from __future__ import annotations
+from typing import TYPE_CHECKING
+from common.type_checkers.Checkers import chk_issubclass, chk_not_none, chk_type
+from ..backend.Tools import key_to_str
+from .StringField import StringField
 
-class ForeignKeyField(Field):
-    def __init__(self, *args, foreign_model : 'Model' = None, **kwargs) -> None:
-        self.foreign_model = foreign_model
-        super().__init__(*args, type_=(float, int), **kwargs)
+if TYPE_CHECKING:
+    from ..model.Model import Model
 
-    def __set__(self, instance, value):
-        if self.min_value is not None and value < self.min_value:
-            raise ValueError('Attribute "{:s}" expects value >= {:s}; requested({:s})'.format(
-                str(self.name), str(self.min_value), str(value)))
-        if self.max_value is not None and value > self.max_value:
-            raise ValueError('Attribute "{:s}" expects value <= {:s}; requested({:s})'.format(
-                str(self.name), str(self.max_value), str(value)))
-        super().__set__(instance, value)
+class ForeignKeyField(StringField):
+    def __init__(self, foreign_model : 'Model', *args, required : bool = True, **kwargs) -> None:
+        from ..model.Model import Model
+        self.foreign_model : Model = chk_issubclass('Field.foreign_model', foreign_model, Model)
+        super().__init__(*args, required=required, allow_empty=not required, **kwargs)
+
+    def __set__(self, instance : 'Model', value : 'Model') -> None:
+        if self.required: chk_not_none(self.name, value)
+        if value is None: super().__set__(instance, value)
+        model_instance : 'Model' = chk_type('value', value, self.foreign_model)
+        super().__set__(instance, key_to_str(model_instance.backend_key))
diff --git a/src/common/orm/fields/IntegerField.py b/src/common/orm/fields/IntegerField.py
index 5622af9755a824689fa3b002d2a65dd84bb3d9e4..72cccdac7fd7bad734e9503434764be7c9e797ea 100644
--- a/src/common/orm/fields/IntegerField.py
+++ b/src/common/orm/fields/IntegerField.py
@@ -1,7 +1,11 @@
-from typing import Optional
+from __future__ import annotations
+from typing import TYPE_CHECKING, Optional
 from common.type_checkers.Checkers import chk_integer, chk_not_none
 from .Field import Field
 
+if TYPE_CHECKING:
+    from ..model.Model import Model
+
 class IntegerField(Field):
     def __init__(
         self, *args, min_value : Optional[int] = None, max_value : Optional[int] = None, **kwargs) -> None:
@@ -12,7 +16,13 @@ class IntegerField(Field):
         self._max_value = None if max_value is None else \
             chk_integer('{}.max_value'.format(self.name), max_value, min_value=self._min_value)
 
-    def __set__(self, instance, value : int) -> None:
+    def __set__(self, instance : 'Model', value : int) -> None:
         if self.required: chk_not_none(self.name, value)
         if value is None: super().__set__(instance, value)
         super().__set__(instance, chk_integer(self.name, value, min_value=self._min_value, max_value=self._max_value))
+
+    def serialize(self, value : int) -> str:
+        return str(value)
+
+    def deserialize(self, value : str) -> int:
+        return int(value)
diff --git a/src/common/orm/fields/PrimaryKeyField.py b/src/common/orm/fields/PrimaryKeyField.py
index 9f0cdc50151f595afd6ad30f6611077f028e3ac0..2111c1b65e9af6f7899a0ee9254ce103d3bb3882 100644
--- a/src/common/orm/fields/PrimaryKeyField.py
+++ b/src/common/orm/fields/PrimaryKeyField.py
@@ -1,13 +1,17 @@
-from typing import Any
+from __future__ import annotations
+from typing import TYPE_CHECKING
 from common.type_checkers.Checkers import chk_not_none
 from .StringField import StringField
 
+if TYPE_CHECKING:
+    from ..model.Model import Model
+
 class PrimaryKeyField(StringField):
     def __init__(self, *args, required : bool = True, **kwargs) -> None:
         super().__init__(*args, required=True, allow_empty=False, min_length=1, **kwargs)
 
-    def __set__(self, instance, value : str):
+    def __set__(self, instance : 'Model', value : str) -> None:
         chk_not_none(self.name, value) # Always required
         if (self.name in instance.__dict__) and (instance.__dict__[self.name] is not None):
             raise ValueError('PrimaryKeyField cannot be modified')
-        return super().__set__(instance, value)
+        super().__set__(instance, value)
diff --git a/src/common/orm/fields/StringField.py b/src/common/orm/fields/StringField.py
index ca5a5a9ccfcf258de1c9f7ce1d10e25dcddaee48..8a6e260a116aab29c140ff377b7a033248c4dcaa 100644
--- a/src/common/orm/fields/StringField.py
+++ b/src/common/orm/fields/StringField.py
@@ -1,8 +1,12 @@
+from __future__ import annotations
 import re
-from typing import Optional, Pattern, Union
+from typing import TYPE_CHECKING, Optional, Pattern, Union
 from common.type_checkers.Checkers import chk_boolean, chk_integer, chk_not_none, chk_string
 from .Field import Field
 
+if TYPE_CHECKING:
+    from ..model.Model import Model
+
 class StringField(Field):
     def __init__(
         self, *args, allow_empty : bool = False, min_length : Optional[int] = None, max_length : Optional[int] = None,
@@ -16,9 +20,15 @@ class StringField(Field):
             chk_integer('{}.max_length'.format(self.name), max_length, min_value=self._min_length)
         self._pattern = None if pattern is None else re.compile(pattern)
 
-    def __set__(self, instance, value : str) -> None:
+    def __set__(self, instance : 'Model', value : str) -> None:
         if self.required: chk_not_none(self.name, value)
         if value is None: super().__set__(instance, value)
         super().__set__(instance, chk_string(
             self.name, value, allow_empty=self._allow_empty, min_length=self._min_length, max_length=self._max_length,
             pattern=self._pattern))
+
+    def serialize(self, value : str) -> str:
+        return value
+
+    def deserialize(self, value : str) -> str:
+        return value
diff --git a/src/common/orm/fields/__init__.py b/src/common/orm/fields/__init__.py
index b145d2b87ef70d0f2c8770a8fe07b115ca5d75c6..b97f0f08a646c23d51b98b03bf7fe29f9c7d8bb8 100644
--- a/src/common/orm/fields/__init__.py
+++ b/src/common/orm/fields/__init__.py
@@ -1,5 +1,8 @@
+from .BooleanField import BooleanField
 from .Field import Field
 from .FloatField import FloatField
+from .ForeignKeyField import ForeignKeyField
 from .IntegerField import IntegerField
+from .PrimaryKeyField import PrimaryKeyField
 from .StringField import StringField
-__all__ = ['Field', 'FloatField', 'IntegerField', 'StringField']
+__all__ = ['BooleanField', 'Field', 'FloatField', 'ForeignKeyField', 'IntegerField', 'PrimaryKeyField', 'StringField']
diff --git a/src/common/orm/model/Model.py b/src/common/orm/model/Model.py
index 0124d163c66b6cac563fd13c6fb95eefe30ff7b1..b4950d163208aec8981be13d56c3c6011d47c7c5 100644
--- a/src/common/orm/model/Model.py
+++ b/src/common/orm/model/Model.py
@@ -1,9 +1,12 @@
 from __future__ import annotations
-from typing import Any, Dict, Mapping, Tuple
-from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Set, Tuple, Type
+from common.orm.backend.Tools import key_to_str
+from common.orm.fields.ForeignKeyField import ForeignKeyField
 from common.type_checkers.Checkers import chk_none
+from ..Exceptions import MutexException
 from ..backend._Backend import _Backend
 from ..fields.Field import Field
+from ..fields.PrimaryKeyField import PrimaryKeyField
 from .Tools import NoDupOrderedDict
 
 class MetaModel(type):
@@ -18,12 +21,12 @@ class MetaModel(type):
             if not isinstance(value, Field): continue
             attrs[key].name = key
             field_names.append(key)
-            if isinstance(value, PrimaryKeyField):
-                if primary_key_field is None:
-                    primary_key_field = value
-                    continue
-                raise AttributeError('PrimaryKey for Model({:s}) already set to attribute({:s})'.format(
-                    str(name), str(primary_key_field.name)))
+            if not isinstance(value, PrimaryKeyField): continue
+            if primary_key_field is None:
+                primary_key_field = value
+                continue
+            raise AttributeError('PrimaryKey for Model({:s}) already set to attribute({:s})'.format(
+                str(name), str(primary_key_field.name)))
         cls_obj = super().__new__(metacls, name, bases, dict(attrs))
         setattr(cls_obj, '_primary_key_field', primary_key_field)
         setattr(cls_obj, '_field_names_list', field_names)
@@ -38,18 +41,21 @@ class Model(metaclass=MetaModel):
         self._parent = parent
         self._backend = self._parent.backend
         self._class_name = type(self).__name__
-        self._backend_key = '{:s}/{:s}'.format(self.parent.backend_key, self._class_name)
+        backend_key = self._class_name
         if self._primary_key_field is not None: # pylint: disable=no-member
             primary_key_field_name = self._primary_key_field.name # pylint: disable=no-member
-            print('primary_key_field_name', primary_key_field_name)
             setattr(self, primary_key_field_name, primary_key) 
-            self._backend_key += '[{:s}]'.format(getattr(self, primary_key_field_name))
+            backend_key += '[{:s}]'.format(getattr(self, primary_key_field_name))
         else:
             try:
                 chk_none('primary_key', primary_key)
             except:
                 msg = 'Unable to set primary_key({:s}) since no PrimaryKeyField is defined in the model'
                 raise AttributeError(msg.format(str(primary_key)))
+        self._backend_key : str = key_to_str([self.parent.backend_key, backend_key])
+        self._backend_parent_key : str = self.parent.backend_key
+        self._backend_references_key : str = key_to_str([self.parent.backend_key, backend_key, 'references'])
+        self._owner_key : Optional[str] = None
 
     @property
     def parent(self) -> 'Model': return self._parent
@@ -60,21 +66,100 @@ class Model(metaclass=MetaModel):
     @property
     def backend_key(self) -> str: return self._backend_key
 
+    def lock(self, extra_keys : List[List[str]] = []):
+        lock_keys = [self._backend_key, self.parent.backend_key, self._backend_references_key] + extra_keys
+        lock_keys = [key_to_str([lock_key, 'lock']) for lock_key in lock_keys]
+        acquired,self._owner_key = self._backend.lock(lock_keys, owner_key=self._owner_key)
+        if acquired: return
+        raise MutexException('Unable to lock keys {:s} using owner_key {:s}'.format(
+            str(lock_keys), str(self._owner_key)))
+
+    def unlock(self, extra_keys : List[List[str]] = []):
+        lock_keys = [self._backend_key, self.parent.backend_key, self._backend_references_key] + extra_keys
+        lock_keys = [key_to_str([lock_key, 'lock']) for lock_key in lock_keys]
+        released = self._backend.unlock(lock_keys, self._owner_key)
+        if released: return
+        raise Exception('Unable to unlock keys {:s} using owner_key {:s}'.format(
+            str(lock_keys), str(self._owner_key)))
+
     def load(self) -> None:
-        attributes = self._backend.dict_get(self._backend_key).items()
-        for name in self._field_names_list: # pylint: disable=no-member
-            if name not in attributes: continue
-            setattr(self, name, attributes[name])
+        self.lock()
+
+        model_class = type(self)
+        attributes = self._backend.dict_get(self._backend_key)
+        pk_field = self._primary_key_field # pylint: disable=no-member
+        pk_field_name = None if pk_field is None else pk_field.name
+        for field_name in self._field_names_list: # pylint: disable=no-member
+            if field_name == pk_field_name: continue
+            if field_name not in attributes: continue
+            raw_field_value = attributes[field_name]
+            field_class : 'Field' = getattr(model_class, field_name)
+            field_value = field_class.deserialize(raw_field_value)
+            setattr(self, field_name, field_value)
+
+        self.unlock()
 
     def save(self) -> None:
-        attributes : Dict[str, Any] = {
-            name:repr(getattr(self, name))
-            for name in self._field_names_list # pylint: disable=no-member
-        }
-        self._backend.dict_update(self._backend_key, attributes)
+        model_class = type(self)
+
+        attributes : Dict[str, Any] = dict()
+        required_keys : Set[str] = set()
+        foreign_additions : Dict[str, str] = dict()
+        foreign_removals : Dict[str, str] = dict()
+        for field_name in self._field_names_list: # pylint: disable=no-member
+            field_value = str(getattr(self, field_name))
+            field_instance : 'Field' = getattr(model_class, field_name)
+            serialized_field_value = field_instance.serialize(field_value)
+            if isinstance(field_instance, ForeignKeyField):
+                foreign_reference = '{:s}:{:s}'.format(self._backend_key, field_name)
+                field_value_stored = getattr(self, field_name + '_stored', None)
+                if field_value_stored is not None:
+                    foreign_removals[key_to_str([field_value_stored, 'references'])] = foreign_reference
+                foreign_additions[key_to_str([serialized_field_value, 'references'])] = foreign_reference
+                required_keys.add(serialized_field_value)
+            attributes[field_name] = serialized_field_value
+
+        if len(self._backend_parent_key) > 0:
+            required_keys.add(self._backend_parent_key)
+
+        extra_keys = []
+        extra_keys.extend(list(foreign_removals.keys()))
+        extra_keys.extend(list(foreign_additions.keys()))
+
+        print('self._backend_key', self._backend_key)
+        print('extra_keys', extra_keys)
+        print('attributes', attributes)
+        print('foreign_removals', foreign_removals)
+        print('foreign_additions', foreign_additions)
+
+        try:
+            print('dump_before', self._backend.dump())
+            self.lock(extra_keys=extra_keys)
+            print('dump_after', self._backend.dump())
+
+            not_exists = []
+            for required_key in required_keys:
+                if self._backend.exists(required_key): continue
+                not_exists.append('{:s}'.format(str(required_key)))
+            if len(not_exists) > 0:
+                raise ValueError('Required Keys ({:s}) does not exist'.format(', '.join(sorted(not_exists))))
+
+            self._backend.dict_update(self._backend_key, attributes)
+            for serialized_field_value,foreign_reference in foreign_removals.items():
+                self._backend.set_remove(serialized_field_value, foreign_reference)
+
+            for serialized_field_value,foreign_reference in foreign_additions.items():
+                self._backend.set_add(serialized_field_value, foreign_reference)
+        finally:
+            self.unlock(extra_keys=extra_keys)
+
+        for serialized_field_value,foreign_reference in foreign_additions.items():
+            setattr(self, (foreign_reference.split(':')[-1]) + '_stored', field_value_stored)
 
     def delete(self) -> None:
-        self._backend.dict_delete(self._backend_key)
+        self.lock()
+        self._backend.delete(self._backend_key)
+        self.unlock()
 
     def dump_id(self) -> Dict:
         raise NotImplementedError()
diff --git a/src/common/orm/tests/test_unitary.py b/src/common/orm/tests/test_unitary.py
deleted file mode 100644
index fb2525f9e69023a18cb542ef4d2e822320739498..0000000000000000000000000000000000000000
--- a/src/common/orm/tests/test_unitary.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import logging, pytest
-from common.orm._Database import _Database
-from common.orm.backend._Backend import _Backend
-from common.orm.backend.inmemory.InMemoryBackend import InMemoryBackend
-from common.orm.fields.FloatField import FloatField
-from common.orm.fields.IntegerField import IntegerField
-from common.orm.fields.PrimaryKeyField import PrimaryKeyField
-from common.orm.fields.StringField import StringField
-from common.orm.model.Model import Model
-
-logging.basicConfig(level=logging.INFO)
-
-def test_database_instantiation():
-    with pytest.raises(AttributeError) as e:
-        _Database(None)
-    str_class_path = '{}.{}'.format(_Backend.__module__, _Backend.__name__)
-    assert str(e.value) == 'backend must inherit from {}'.format(str_class_path)
-
-    assert _Database(InMemoryBackend()) is not None
-
-def test_model_without_attributes():
-    with pytest.raises(AttributeError) as e:
-        Model(None, 'valid-uuid')
-    str_class_path = '{}.{}'.format(Model.__module__, Model.__name__)
-    assert str(e.value) == 'parent must inherit from {}'.format(str_class_path)
-
-    database = _Database(InMemoryBackend())
-
-    with pytest.raises(AttributeError) as e:
-        Model(database, '')
-    assert str(e.value) == 'Unable to set primary_key() since no PrimaryKeyField is defined in the model'
-
-    with pytest.raises(AttributeError) as e:
-        Model(database, 'primary-key')
-    assert str(e.value) == 'Unable to set primary_key(primary-key) since no PrimaryKeyField is defined in the model'
-
-def test_model_with_primarykey():
-    database = _Database(InMemoryBackend())
-
-    class TestModel(Model):
-        pk = PrimaryKeyField()
-        name = StringField(min_length=1)
-        age = IntegerField(min_value=0)
-        salary = FloatField(min_value=0.0)
-
-    with pytest.raises(ValueError) as e:
-        TestModel(database)
-    assert str(e.value) == 'pk(None) is None.'
-
-    with pytest.raises(ValueError) as e:
-        TestModel(database, '')
-    assert str(e.value) == 'pk() is out of range: allow_empty(False).'
-
-    obj = TestModel(database, 'valid-pk')
-    assert obj is not None
-
-def test_model_with_primarykey_and_attributes():
-    database = _Database(InMemoryBackend())
-
-    class TestModel(Model):
-        pk = PrimaryKeyField()
-        name = StringField(min_length=1)
-        age = IntegerField(min_value=0)
-        salary = FloatField(min_value=0.0)
-
-    with pytest.raises(AttributeError) as e:
-        Attributes(obj, None, {}, {})
-    assert str(e.value) == 'key_pattern must be a non-empty instance of str'
-
-#    # should fail with invalid attribute key
-#    with pytest.raises(AttributeError) as e:
-#        Attributes(obj, '', {}, {})
-#    assert str(e.value) == 'key_pattern must be a non-empty instance of str'
-#
-#    # should fail with invalid attribute validators
-#    with pytest.raises(AttributeError) as e:
-#        Attributes(obj, 'valid-attributes-key', [], {})
-#    assert str(e.value) == 'validators must be an instance of dict'
-#
-#    # should fail with invalid attribute transcoders
-#    with pytest.raises(AttributeError) as e:
-#        Attributes(obj, 'valid-attributes-key', {}, [])
-#    assert str(e.value) == 'transcoders must be an instance of dict'
-#
-#    # should work
-#    attrs = Attributes(obj, 'valid-attributes-key', {}, {})
-#    assert attrs is not None
-
-#def testModel_attributes_gets_invalid_parameters():
-#    # should work
-#    rootModel = _Database(InMemoryBackend())
-#    validators = {'attr': lambda v: True}
-#    entity_attrs = Attributes(rootModel, 'valid-attributes-key', validators, {})
-#    assert entity_attrs is not None
-#
-#    with pytest.raises(AttributeError) as e:
-#        entity_attrs.update(update_attributes={'non-defined-attr': 'random-value'})
-#    assert str(e.value) == "Unexpected update_attributes: {'non-defined-attr': 'random-value'}"
-#
-#    with pytest.raises(AttributeError) as e:
-#        entity_attrs.update(remove_attributes=['non-defined-attr'])
-#    assert str(e.value) == "Unexpected remove_attributes: {'non-defined-attr'}"
diff --git a/src/common/orm/tests/test_unitary_orm.py b/src/common/orm/tests/test_unitary_orm.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ae5a1a79363aab2728a790cc35e32cbbcf23355
--- /dev/null
+++ b/src/common/orm/tests/test_unitary_orm.py
@@ -0,0 +1,247 @@
+import logging, pytest
+from common.orm._Database import _Database
+from common.orm.backend._Backend import _Backend
+from common.orm.backend.inmemory.InMemoryBackend import LOGGER, InMemoryBackend
+from common.orm.fields.BooleanField import BooleanField
+from common.orm.fields.FloatField import FloatField
+from common.orm.fields.ForeignKeyField import ForeignKeyField
+from common.orm.fields.IntegerField import IntegerField
+from common.orm.fields.PrimaryKeyField import PrimaryKeyField
+from common.orm.fields.StringField import StringField
+from common.orm.model.Model import Model
+
+logging.basicConfig(level=logging.INFO)
+
+def test_database_instantiation():
+    with pytest.raises(AttributeError) as e:
+        _Database(None)
+    str_class_path = '{}.{}'.format(_Backend.__module__, _Backend.__name__)
+    assert str(e.value) == 'backend must inherit from {}'.format(str_class_path)
+
+    assert _Database(InMemoryBackend()) is not None
+
+def test_model_without_attributes():
+    with pytest.raises(AttributeError) as e:
+        Model(None, 'valid-uuid')
+    str_class_path = '{}.{}'.format(Model.__module__, Model.__name__)
+    assert str(e.value) == 'parent must inherit from {}'.format(str_class_path)
+
+    database = _Database(InMemoryBackend())
+
+    with pytest.raises(AttributeError) as e:
+        Model(database, '')
+    assert str(e.value) == 'Unable to set primary_key() since no PrimaryKeyField is defined in the model'
+
+    with pytest.raises(AttributeError) as e:
+        Model(database, 'primary-key')
+    assert str(e.value) == 'Unable to set primary_key(primary-key) since no PrimaryKeyField is defined in the model'
+
+def test_model_with_primarykey():
+    database = _Database(InMemoryBackend())
+
+    with pytest.raises(AttributeError) as e:
+        class WrongTestModel(Model): # pylint: disable=unused-variable
+            pk = PrimaryKeyField()
+            name = StringField(min_length=1)
+            age = IntegerField(min_value=0)
+            salary = FloatField(min_value=0.0)
+            active = BooleanField()
+            pk2 = PrimaryKeyField()
+    assert str(e.value) == 'PrimaryKey for Model(WrongTestModel) already set to attribute(pk)'
+
+    class TestModel(Model):
+        pk = PrimaryKeyField()
+        name = StringField(min_length=1)
+        age = IntegerField(min_value=0)
+        salary = FloatField(min_value=0.0)
+        active = BooleanField()
+
+    with pytest.raises(ValueError) as e:
+        TestModel(database)
+    assert str(e.value) == 'pk(None) is None.'
+
+    with pytest.raises(ValueError) as e:
+        TestModel(database, '')
+    assert str(e.value) == 'pk() is out of range: allow_empty(False).'
+
+    obj = TestModel(database, 'valid-pk')
+    assert obj is not None
+
+    with pytest.raises(ValueError) as e:
+        obj.pk = 'another-valid-pk'
+    assert str(e.value) == 'PrimaryKeyField cannot be modified'
+
+def test_model_with_primarykey_and_attributes():
+    database = _Database(InMemoryBackend())
+
+    class TestModel(Model):
+        pk = PrimaryKeyField()
+        name = StringField(min_length=5, max_length=10)
+        age = IntegerField(min_value=0)
+        salary = FloatField(min_value=0.0)
+        active = BooleanField()
+
+    obj = TestModel(database, 'valid-pk')
+    assert obj is not None
+
+    with pytest.raises(AttributeError) as e:
+        del obj.name
+    assert str(e.value) == 'Attribute "name" cannot be deleted'
+
+    with pytest.raises(TypeError) as e:
+        obj.name = 55
+    assert str(e.value) == "name(55) is of a wrong type(int). Accepted type_or_types(<class 'str'>)."
+
+    with pytest.raises(TypeError) as e:
+        obj.name = 55.5
+    assert str(e.value) == "name(55.5) is of a wrong type(float). Accepted type_or_types(<class 'str'>)."
+
+    with pytest.raises(TypeError) as e:
+        obj.name = True
+    assert str(e.value) == "name(True) is of a wrong type(bool). Accepted type_or_types(<class 'str'>)."
+
+    with pytest.raises(TypeError) as e:
+        obj.age = 'too old'
+    assert str(e.value) == "age(too old) is of a wrong type(str). Accepted type_or_types(<class 'int'>)."
+
+    with pytest.raises(TypeError) as e:
+        obj.age = 37.5
+    assert str(e.value) == "age(37.5) is of a wrong type(float). Accepted type_or_types(<class 'int'>)."
+
+    with pytest.raises(TypeError) as e:
+        obj.salary = 'too high'
+    msg = "salary(too high) is of a wrong type(str). Accepted type_or_types((<class 'int'>, <class 'float'>))."
+    assert str(e.value) == msg
+
+    with pytest.raises(TypeError) as e:
+        obj.active = 'active'
+    assert str(e.value) == "active(active) is of a wrong type(str). Accepted type_or_types(<class 'bool'>)."
+
+    with pytest.raises(TypeError) as e:
+        obj.active = 27
+    assert str(e.value) == "active(27) is of a wrong type(int). Accepted type_or_types(<class 'bool'>)."
+
+    with pytest.raises(TypeError) as e:
+        obj.active = 92.5
+    assert str(e.value) == "active(92.5) is of a wrong type(float). Accepted type_or_types(<class 'bool'>)."
+
+    with pytest.raises(ValueError) as e:
+        obj.name = ''
+    assert str(e.value) == 'name() is out of range: allow_empty(False).'
+
+    with pytest.raises(ValueError) as e:
+        obj.name = 'John'
+    assert str(e.value) == 'name(John) is out of range: min_length(5).'
+
+    with pytest.raises(ValueError) as e:
+        obj.name = 'John Smith Willson'
+    assert str(e.value) == 'name(John Smith Willson) is out of range: max_value(10).'
+
+    obj.name = 'John Smith'
+    obj.age = 37
+    obj.salary = 5023.52
+    obj.active = True
+    assert repr(obj) == "TestModel(pk='valid-pk'(PK), name='John Smith', age=37, salary=5023.52, active=True)"
+
+def test_model_database_operations():
+    database = _Database(InMemoryBackend())
+
+    class TestModel(Model):
+        pk = PrimaryKeyField()
+        name = StringField(min_length=5, max_length=10)
+        age = IntegerField(min_value=0)
+        salary = FloatField(min_value=0.0)
+        active = BooleanField()
+
+    obj = TestModel(database, 'valid-pk')
+    assert obj is not None
+
+    obj.name = 'John Smith'
+    obj.age = 37
+    obj.salary = 5023.52
+    obj.active = True
+    assert repr(obj) == "TestModel(pk='valid-pk'(PK), name='John Smith', age=37, salary=5023.52, active=True)"
+
+    obj.save()
+
+    database_dump = database.dump()
+    assert len(database_dump) == 1
+    db_entry_repr = '[dict] /TestModel[valid-pk]'\
+                    '                                                                                 '\
+                    ':: {\'pk\': \'valid-pk\', \'name\': \'John Smith\', \'age\': \'37\', \'salary\': \'5023.52\', '\
+                    '\'active\': \'True\'}'
+    assert database_dump[0] == db_entry_repr
+
+    obj2 = TestModel(database, 'valid-pk')
+    assert obj2 is not None
+
+    obj2.load()
+    assert repr(obj2) == "TestModel(pk='valid-pk'(PK), name='John Smith', age=37, salary=5023.52, active=True)"
+
+    obj2.delete()
+    assert len(database.dump()) == 0
+
+    obj2.save()
+    assert len(database.dump()) == 1
+
+    database.clear_all()
+    assert len(database.dump()) == 0
+
+def test_model_with_foreignkeys():
+    database = _Database(InMemoryBackend())
+
+    class Team(Model):
+        pk = PrimaryKeyField()
+        name = StringField(max_length=10)
+
+    class Workplace(Model):
+        pk = PrimaryKeyField()
+        name = StringField(max_length=10)
+
+    class Member(Model):
+        pk = PrimaryKeyField()
+        place = ForeignKeyField(Workplace)
+        name = StringField(max_length=10)
+
+    team_dev_ops = Team(database, 'dev-ops')
+    team_dev_ops.name = 'Dev Ops'
+    assert team_dev_ops is not None
+    assert repr(team_dev_ops) == "Team(pk='dev-ops'(PK), name='Dev Ops')"
+
+    workplace_bcn = Workplace(database, 'bcn')
+    workplace_bcn.name = 'Barcelona'
+    assert workplace_bcn is not None
+    assert repr(workplace_bcn) == "Workplace(pk='bcn'(PK), name='Barcelona')"
+
+    member_john = Member(team_dev_ops, 'john')
+    member_john.name = 'John'
+    member_john.place = workplace_bcn
+    assert member_john is not None
+    assert repr(member_john) == "Member(pk='john'(PK), place='/Workplace[bcn]', name='John')"
+
+    with pytest.raises(ValueError) as e:
+        member_john.save()
+    assert str(e.value) == 'Required Keys (/Team[dev-ops], /Workplace[bcn]) does not exist'
+
+    workplace_bcn.save()
+
+    with pytest.raises(ValueError) as e:
+        member_john.save()
+    assert str(e.value) == 'Required Keys (/Team[dev-ops]) does not exist'
+
+    team_dev_ops.save()
+    member_john.save()
+
+    database_dump = database.dump()
+    LOGGER.info('----- Database Dump: -----')
+    for database_entry in database_dump:
+        LOGGER.info('  ' + database_entry)
+    LOGGER.info('--------------------------')
+    #assert len(database_dump) == 1
+    raise Exception()
+
+
+# TODO: Test remove foreign key
+# TODO: Test change foreign key
+# TODO: Test remove entity with foreign keys pointing to another entity
+# TODO: Test remove entity with primary key being pointed by another entity
diff --git a/src/common/type_checkers/Checkers.py b/src/common/type_checkers/Checkers.py
index 1876321be3e00c7e521446a3da525ebe725abe8a..b3693d87d29dc2a9c34343bfb8eb1cc69d1c2381 100644
--- a/src/common/type_checkers/Checkers.py
+++ b/src/common/type_checkers/Checkers.py
@@ -16,6 +16,11 @@ def chk_type(name : str, value : Any, type_or_types : Union[type, Set[type]] = s
     msg = '{}({}) is of a wrong type({}). Accepted type_or_types({}).'
     raise TypeError(msg.format(str(name), str(value), type(value).__name__, str(type_or_types)))
 
+def chk_issubclass(name : str, value : type, class_or_classes : Union[type, Set[type]] = set()) -> Any:
+    if issubclass(value, class_or_classes): return value
+    msg = '{}({}) is of a wrong class({}). Accepted class_or_classes({}).'
+    raise TypeError(msg.format(str(name), str(value), type(value).__name__, str(class_or_classes)))
+
 def chk_length(
     name : str, value : Sized, allow_empty : bool = False,
     min_length : Optional[int] = None, max_length : Optional[int] = None) -> Any: