Index: flexoentity/__init__.py
===================================================================
--- flexoentity/__init__.py	(revision 4f91fed022a3de01238c2feecb1aad16a9603f94)
+++ flexoentity/__init__.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -14,4 +14,9 @@
 from .flexo_entity import FlexoEntity, EntityType, EntityState
 from .flexo_collection import FlexoCollection
+from .typed_collection import TypedCollection
+from .in_memory_backend import InMemoryBackend
+from .composite_backend import CompositeBackend
+from .entity_manager import EntityManager
+from .sqlite_entity_backend import SQLiteEntityBackend
 from .domain import Domain
 from .domain_manager import DomainManager, DuplicateDomainError
@@ -26,8 +31,13 @@
     "EntityType",
     "Domain",
+    "EntityManager",
+    "SQLiteEntityBackend",
+    "InMemoryBackend",
+    "CompositeBackend",
     "DomainManager",
     "DuplicateDomainError",
     "EntityState",
     "FlexoCollection",
+    "TypedCollection",
     "FlexoSignature",
     "EntityRegistry",
Index: flexoentity/composite_backend.py
===================================================================
--- flexoentity/composite_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
+++ flexoentity/composite_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -0,0 +1,110 @@
+from .persistance_backend import PersistanceBackend
+from .flexo_entity import FlexoEntity
+from .in_memory_backend import InMemoryBackend
+
+class CompositeBackend(PersistanceBackend):
+    """
+    A backend that wraps multiple real backends.
+
+    - Reads always come from the first backend (e.g., InMemoryBackend).
+    - Writes propagate to all backends.
+    """
+
+    def __init__(self, entity_class, *backends):
+        if not issubclass(entity_class, FlexoEntity):
+            raise TypeError("entity_class must be a subclass of FlexOEntity")
+
+        self.entity_class = entity_class
+
+        # Default: create an in-memory backend as the primary backend
+        if not backends:
+            backends = (InMemoryBackend(entity_class),)
+
+        # Validate all backends
+        for b in backends:
+            if getattr(b, "entity_class", None) != entity_class:
+                raise TypeError(
+                    f"Backend {b} does not match entity_class={entity_class.__name__}"
+                )
+
+        self.backends = list(backends)
+
+    @property
+    def primary(self):
+        """The backend used for all read operations."""
+        return self.backends[0]
+
+    def add_backend(self, backend, clear=False):
+        """
+        Append an additional backend.
+        If clear=True, backend is wiped before syncing.
+        """
+        if backend.entity_class != self.entity_class:
+            raise TypeError("Backend entity_class mismatch")
+
+        if clear:
+            backend.delete_all()
+
+        # Sync current data into backend
+        for entity in self.primary.load_all():
+            backend.save(entity)
+
+        self.backends.append(backend)
+
+    def remove_backend(self, backend):
+        """
+        Remove a backend. Primary backend cannot be removed.
+        """
+        if backend is self.primary:
+            raise ValueError("Cannot remove the primary backend")
+        self.backends.remove(backend)           
+    # ---------------------------------------------------------
+    # Write operations propagate to *all* backends
+    # ---------------------------------------------------------
+
+    def save(self, entity):
+        for b in self.backends:
+            b.save(entity)
+
+    def update(self, entity):
+        for b in self.backends:
+            b.update(entity)
+
+    def delete(self, flexo_id: str):
+        for b in self.backends:
+            b.delete(flexo_id)
+
+    # ---------------------------------------------------------
+    # Read operations use only the *primary* backend
+    # ---------------------------------------------------------
+
+    def load(self, flexo_id: str):
+        return self.primary.load(flexo_id)
+
+    def load_all(self):
+        return self.primary.load_all()
+
+    # ---------------------------------------------------------
+    # Optional: flush from primary backend to all others
+    # ---------------------------------------------------------
+
+    def sync_all(self):
+        """
+        Push all data from the primary backend to the other backends.
+        Useful if secondary backends were empty initially.
+        """
+        primary = self.backends[0]
+        for entity in primary.load_all():
+            for b in self.backends[1:]:
+                b.save(entity)
+
+    def clear(self):
+        for b in self.backends:
+            if hasattr(b, "clear"):
+                b.clear()
+
+    # ---------------------------------------------------------
+
+    def __repr__(self):
+        names = ", ".join(b.__class__.__name__ for b in self.backends)
+        return f"<CompositeBackend [{names}]>"
Index: flexoentity/domain_manager.py
===================================================================
--- flexoentity/domain_manager.py	(revision 4f91fed022a3de01238c2feecb1aad16a9603f94)
+++ flexoentity/domain_manager.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -1,5 +1,4 @@
-# flexoentity/domain_manager.py
-
 from __future__ import annotations
+import json
 
 from typing import Iterable
@@ -8,4 +7,5 @@
 from .domain import Domain
 from .flexo_entity import EntityState
+from .entity_manager import EntityManager
 
 
@@ -22,96 +22,109 @@
 
 
-class DomainManager:
+class DomainManager(EntityManager):
     """
-    Manager for Domain entities, implemented using composition.
+    Manager for Domain entities using the new backend architecture.
 
-    - Uses FlexoCollection internally.
-    - Domains are keyed by domain.domain_id (explicit override).
-    - No inheritance from FlexoCollection.
-    - Responsible ONLY for domain-specific rules.
+    - Primary storage: backend.load_all() (InMemory, SQLite, JSON, ...)
+    - Secondary index: domain_id → Domain
+    - Domain-specific business rules included.
     """
 
-    def __init__(self, registry):
-        # store is a general-purpose collection
-        self._store = FlexoCollection()
+    ENTITY_CLASS = Domain
+
+    def __init__(self, backend, registry=None):
+        super().__init__(backend)
         self._registry = registry
+        self._index_by_domain_id = {}
 
-    # ---------------------------------------------------------------
-    # Core API
-    # ---------------------------------------------------------------
+        self._rebuild_index()
 
-    def add(self, domain: Domain) -> None:
-        """
-        Add a domain using domain.domain_id as key.
-        """
-        if not isinstance(domain, Domain):
-            raise TypeError(
-                f"DomainManager accepts only Domain instances, got {type(domain)}"
-            )
-        if domain.domain_id in self._store:
-            raise DuplicateDomainError(f"Domain '{domain.domain_id} already exists.")
-        self._store.add(domain, key=domain.domain_id)
+    # ------------------------------------------------------------
+    # Index rebuild
+    # ------------------------------------------------------------
 
-    def get(self, domain_id: str) -> Domain:
-        """Retrieve a domain or raise DomainNotFoundError."""
-        if isinstance(domain_id, Domain):
-            raise TypeError("DomainManager.get() expects a domain_id string, not a Domain object.")
-        result = self._store.get(domain_id)
-        if result is None:
-            raise DomainNotFoundError(f"Domain '{domain_id}' not found.")
-        return result
+    def _rebuild_index(self):
+        """Rebuild domain_id lookup table from backend contents."""
+        self._index_by_domain_id.clear()
+        for dom in self.backend.load_all():
+            self._index_by_domain_id[dom.domain_id] = dom
 
-    def can_delete(self, domain_id):
+    # ------------------------------------------------------------
+    # Overrides for CRUD
+    # ------------------------------------------------------------
+
+    def add(self, domain: Domain):
+        # Domain-specific uniqueness rule
+        if domain.domain_id in self._index_by_domain_id:
+            raise DuplicateDomainError(f"Domain '{domain.domain_id}' already exists.")
+
+        super().add(domain)
+        self._index_by_domain_id[domain.domain_id] = domain
+
+    def update(self, domain: Domain):
+        super().update(domain)
+        self._index_by_domain_id[domain.domain_id] = domain
+
+    def delete(self, flexo_id: str):
+        dom = self.backend.load(flexo_id)
+        if dom is None:
+            return
+
+        domain_id = dom.domain_id
+
+        # Check domain deletion rules
+        if not self.can_delete(domain_id):
+            raise ValueError(f"Domain '{domain_id}' is still referenced.")
+
+        super().delete(flexo_id)
+
+        # Remove from index
+        self._index_by_domain_id.pop(domain_id, None)
+
+    # ------------------------------------------------------------
+    # Domain-specific lookup
+    # ------------------------------------------------------------
+
+    def get_by_domain_id(self, domain_id: str) -> Domain:
+        dom = self._index_by_domain_id.get(domain_id)
+        if not dom:
+            raise KeyError(f"Domain '{domain_id}' not found.")
+        return dom
+
+    def find_by_domain_id(self, domain_id: str):
+        return self._index_by_domain_id.get(domain_id)
+
+    def all_domain_ids(self):
+        return list(self._index_by_domain_id.keys())
+
+    def all(self):
+        return list(self._index_by_domain_id.values())
+    # ------------------------------------------------------------
+    # Domain deletion rules
+    # ------------------------------------------------------------
+
+    def can_delete(self, domain_id: str) -> bool:
+        if not self._registry:
+            return True
         return not any(self._registry.entities_by_domain(domain_id))
 
-    def delete(self, domain_id):
-        if not self.can_delete(domain_id):
-            raise ValueError(f"Domain {domain_id} is still referenced.")
-        self.remove(domain_id)
-
-    def remove(self, domain_id: str) -> None:
-        """Remove a domain (rarely used, mostly for tests)."""
-        if domain_id not in self._store:
-            raise DomainNotFoundError(domain_id)
-        self._store.remove(domain_id)
-
-    def all(self) -> list[Domain]:
-        """List of all domains."""
-        return self._store.entities()
-
-    def all_domain_ids(self):
-        return self._store.ids() 
-
-    def clear(self):
-        self._store.clear()
-
-    # ---------------------------------------------------------------
-    # Lifecycle / Approval Helpers
-    # ---------------------------------------------------------------
+    # ------------------------------------------------------------
+    # Domain lifecycle helpers
+    # ------------------------------------------------------------
 
     def ensure_approved(self, domain_id: str) -> None:
-        """
-        Used during entity.transition_to().
-        Raises if the domain does not exist or is not APPROVED.
-        """
-        dom = self.get(domain_id)
+        dom = self.get_by_domain_id(domain_id)
 
         if dom.state != EntityState.APPROVED:
-            raise InvalidDomainError(
+            raise ValueError(
                 f"Domain '{domain_id}' must be APPROVED before "
-                f"entities in this domain may be promoted. "
+                f"entities in this domain can be promoted. "
                 f"(Current state: {dom.state})"
             )
 
-    # ---------------------------------------------------------------
-    # Representations
-    # ---------------------------------------------------------------
+    def clear(self):
+        self.backend.clear()
 
-    def __len__(self) -> int:
-        return len(self._store)
+    def __repr__(self):
+        return f"<DomainManager domains={self.all_domain_ids()}>"
 
-    def __contains__(self, domain_id: str) -> bool:
-        return domain_id in self._store
-
-    def __repr__(self) -> str:
-        return f"<DomainManager domains={self._store.keys()}>"
Index: flexoentity/entity_manager.py
===================================================================
--- flexoentity/entity_manager.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
+++ flexoentity/entity_manager.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -0,0 +1,96 @@
+from .flexo_entity import FlexoEntity
+from .typed_collection import TypedCollection
+
+
+class EntityManager:
+    """
+    Backend-agnostic manager for any FlexOEntity subclass.
+
+    Responsibilities:
+      - enforce ENTITY_CLASS (e.g., FlexoUser, Domain, Question)
+      - convert entities <-> dicts
+      - delegate persistence to backend
+      - provide a consistent CRUD API for GUIs and CLI
+      - avoid backend-specific code in subclasses
+    """
+
+    ENTITY_CLASS = None  # must be overridden by subclasses
+
+    def __init__(self, backend):
+        if self.ENTITY_CLASS is None:
+            raise ValueError("Subclasses must define ENTITY_CLASS")
+
+        if not issubclass(self.ENTITY_CLASS, FlexoEntity):
+            raise TypeError("ENTITY_CLASS must be a subclass of FlexOEntity")
+        self.backend = backend
+
+    # ------------------------------------------------------------------
+    # CRUD operations
+    # ------------------------------------------------------------------
+
+    def add(self, entity):
+        """Insert a new entity."""
+        self._ensure_type(entity)
+        self.backend.save(entity)
+
+    def update(self, entity):
+        """Replace an existing entity (same flexo_id)."""
+        self._ensure_type(entity)
+        self.backend.update(entity)
+
+    def delete(self, flexo_id: str):
+        """Remove entity by string flexo_id."""
+        self.backend.delete(flexo_id)
+
+    # ------------------------------------------------------------------
+    # Retrieval
+    # ------------------------------------------------------------------
+
+    def get(self, flexo_id: str):
+        """
+        Load entity by flexo_id str.
+        Returns ENTITY_CLASS instance or None.
+        """
+        return self.backend.load(flexo_id)
+
+    def all(self):
+        """Load all entities as a list of instances."""
+        return self.backend.load_all()
+    # ------------------------------------------------------------------
+    # Helpers
+    # ------------------------------------------------------------------
+
+    def exists(self, flexo_id: str) -> bool:
+        return self.get(flexo_id) is not None
+
+    def count(self) -> int:
+        return len(self.backend.load_all())
+
+    def add_or_update(self, entity):
+        """
+        Convenience for GUIs:
+        Insert or overwrite based on whether flexo_id exists.
+        """
+        if self.exists(entity.flexo_id.value):
+            self.update(entity)
+        else:
+            self.add(entity)
+
+    def as_collection(self):
+        return TypedCollection(self.ENTITY_CLASS, items=self.backend.load_all())
+
+    # ------------------------------------------------------------------
+    # Internal
+    # ------------------------------------------------------------------
+
+    def _ensure_type(self, entity):
+        if not isinstance(entity, self.ENTITY_CLASS):
+            raise TypeError(
+                f"Expected {self.ENTITY_CLASS.__name__}, "
+                f"got {type(entity).__name__}"
+            )
+
+    # ------------------------------------------------------------------
+
+    def __repr__(self):
+        return f"<FlexoEntityManager for {self.ENTITY_CLASS.__name__}>"
Index: flexoentity/flexo_collection.py
===================================================================
--- flexoentity/flexo_collection.py	(revision 4f91fed022a3de01238c2feecb1aad16a9603f94)
+++ flexoentity/flexo_collection.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -11,5 +11,5 @@
 - Backwards compatible API
 """
-
+from abc import abstractmethod
 from .flexo_entity import FlexoEntity
 from .id_factory import FlexOID
@@ -115,20 +115,17 @@
     # ------------------------------------------------------------------
 
-    def to_dict_list(self):
+    def serialize(self):
         """Serialize all entities to list of dicts."""
-        return [e.to_dict() for e in self._items.values()]
+        return [e.to_dict() for e in self.entities()]
 
     @classmethod
-    def from_dict_list(cls, data):
+    @abstractmethod
+    def deserialize(cls, data):
         """
         Deserialize a list of dicts into a FlexoCollection.
 
-        Uses FlexoEntity.from_dict() to dispatch to the correct subclass.
+        Uses from_dict() to dispatch to the correct subclass.
         """
-        c = cls()
-        for d in data:
-            entity = FlexoEntity.from_dict(d)
-            c.add(entity)
-        return c
+        pass
 
     # ------------------------------------------------------------------
Index: flexoentity/in_memory_backend.py
===================================================================
--- flexoentity/in_memory_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
+++ flexoentity/in_memory_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -0,0 +1,70 @@
+import json
+from .flexo_entity import FlexoEntity
+from .persistance_backend import PersistanceBackend
+
+
+class InMemoryBackend(PersistanceBackend):
+    """
+    Persistence backend for a *typed* collection of FlexOEntities.
+
+    - Stores dicts internally (no entities)
+    - Reconstructs entities using ENTITY_CLASS.from_dict() on load
+    - Accepts entity_class injected at construction time
+    """
+
+    def __init__(self, entity_class, storage=None):
+        if not issubclass(entity_class, FlexoEntity):
+            raise TypeError("entity_class must be a subclass of FlexOEntity")
+
+        self.entity_class = entity_class
+        self._storage = storage if storage is not None else {}
+
+    # ---------------------------------------------------------
+    # Core persistence API (dict-based internally)
+    # ---------------------------------------------------------
+
+    def save(self, entity):
+        """Store serialized entity dict by flexo_id."""
+        data = entity.to_dict()
+        flexo_id = data["meta"]["flexo_id"]
+        self._storage[flexo_id] = data
+
+    def update(self, entity):
+        self.save(entity)
+
+    def delete(self, flexo_id: str):
+        self._storage.pop(flexo_id, None)
+
+    def load(self, flexo_id: str):
+        """Return an entity instance or None."""
+        d = self._storage.get(flexo_id)
+        return None if d is None else self.entity_class.from_dict(d)
+
+    def load_all(self):
+        """Return a list of entity instances."""
+        return [
+            self.entity_class.from_dict(d)
+            for d in self._storage.values()
+        ]
+
+    def clear(self):
+        self._storage.clear()
+
+    # ---------------------------------------------------------
+    # Optional: JSON file persistence
+    # ---------------------------------------------------------
+
+    def save_to_file(self, path):
+        with open(path, "w", encoding="utf-8") as f:
+            json.dump(
+                list(self._storage.values()),
+                f,
+                ensure_ascii=False,
+                indent=2
+            )
+
+    def load_from_file(self, path):
+        with open(path, "r", encoding="utf-8") as f:
+            data = json.load(f)
+        # Replace storage with file content
+        self._storage = {d["meta"]["flexo_id"]: d for d in data}
Index: flexoentity/json_file_backend.py
===================================================================
--- flexoentity/json_file_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
+++ flexoentity/json_file_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -0,0 +1,43 @@
+import json
+from .persistance_backend import PersistanceBackend
+
+
+class JsonFileBackend(PersistanceBackend):
+    def __init__(self, path: str):
+        self.path = path
+        self.data = []
+        self._load_file()
+
+    def _load_file(self):
+        try:
+            with open(self.path, "r", encoding="utf-8") as f:
+                self.data = json.load(f)
+        except FileNotFoundError:
+            self.data = []
+
+    def save(self, entity_dict):
+        self.data.append(entity_dict)
+        self._flush()
+
+    def update(self, entity_dict):
+        fid = entity_dict["flexo_id"]
+        self.data = [d for d in self.data if d["flexo_id"] != fid]
+        self.data.append(entity_dict)
+        self._flush()
+
+    def delete(self, flexo_id):
+        self.data = [d for d in self.data if d["flexo_id"] != flexo_id]
+        self._flush()
+
+    def load(self, flexo_id):
+        for d in self.data:
+            if d["flexo_id"] == flexo_id:
+                return d
+        return None
+
+    def load_all(self):
+        return self.data
+
+    def _flush(self):
+        with open(self.path, "w", encoding="utf-8") as f:
+            json.dump(self.data, f, ensure_ascii=False, indent=2)
Index: flexoentity/persistance_backend.py
===================================================================
--- flexoentity/persistance_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
+++ flexoentity/persistance_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -0,0 +1,25 @@
+class PersistanceBackend:
+    """
+    Interface for all persistence backends.
+
+    Stores serialized FlexOEntity dicts with a flexo_id key.
+    """
+
+    def save(self, entity_dict: dict) -> None:
+        raise NotImplementedError
+
+    def update(self, entity_dict: dict) -> None:
+        raise NotImplementedError
+
+    def delete(self, flexo_id: str) -> None:
+        raise NotImplementedError
+
+    def load(self, flexo_id: str) -> dict | None:
+        raise NotImplementedError
+
+    def load_all(self) -> list[dict]:
+        raise NotImplementedError
+
+    def clear(self):
+        """Remove all stored entities."""
+        raise NotImplementedError
Index: flexoentity/sqlite_entity_backend.py
===================================================================
--- flexoentity/sqlite_entity_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
+++ flexoentity/sqlite_entity_backend.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -0,0 +1,146 @@
+import sqlite3
+import json
+from typing import Optional, Iterable
+
+from .persistance_backend import PersistanceBackend
+
+
+class SQLiteEntityBackend(PersistanceBackend):
+    """
+    Generic SQLite persistence layer for FlexOEntity subclasses.
+    
+    Subclasses must define:
+      - TABLE_NAME        (e.g. "users")
+      - ENTITY_CLASS      (e.g. FlexoUser)
+      - INDEXED_FIELDS    (list of field names to index)
+    """
+
+    TABLE_NAME = None
+    ENTITY_CLASS = None
+    INDEXED_FIELDS: list[str] = []
+
+    def __init__(self, db_path: str):
+        if self.TABLE_NAME is None:
+            raise ValueError("Subclasses must define TABLE_NAME")
+        if self.ENTITY_CLASS is None:
+            raise ValueError("Subclasses must define ENTITY_CLASS")
+
+        self.db_path = db_path
+        self.conn = sqlite3.connect(db_path)
+        self.conn.row_factory = sqlite3.Row
+        self._init_schema()
+
+    # -----------------------------------------------------------
+    # Schema
+    # -----------------------------------------------------------
+    def _init_schema(self):
+        # Base schema: store full entity JSON and the FlexOID
+        columns = [
+            "flexo_id TEXT PRIMARY KEY",
+            "json TEXT NOT NULL",
+        ]
+
+        # Add indexed metadata columns
+        for field in self.INDEXED_FIELDS:
+            columns.append(f"{field} TEXT")
+
+        colspec = ", ".join(columns)
+
+        self.conn.execute(
+            f"CREATE TABLE IF NOT EXISTS {self.TABLE_NAME} ({colspec})"
+        )
+
+        # Create indices on metadata fields
+        for field in self.INDEXED_FIELDS:
+            self.conn.execute(
+                f"CREATE INDEX IF NOT EXISTS idx_{self.TABLE_NAME}_{field} "
+                f"ON {self.TABLE_NAME}({field})"
+            )
+
+        self.conn.commit()
+
+    # -----------------------------------------------------------
+    # CRUD operations
+    # -----------------------------------------------------------
+    def add(self, obj):
+        data = json.dumps(obj.to_dict(), ensure_ascii=False)
+
+        # Collect row data dynamically
+        fields = ["flexo_id", "json"] + self.INDEXED_FIELDS
+        placeholders = ", ".join("?" for _ in fields)
+        sql = (
+            f"INSERT INTO {self.TABLE_NAME} ({', '.join(fields)}) "
+            f"VALUES ({placeholders})"
+        )
+
+        params = [obj.flexo_id.value, data]
+        params.extend(getattr(obj, f) for f in self.INDEXED_FIELDS)
+
+        self.conn.execute(sql, params)
+        self.conn.commit()
+
+    def update(self, obj):
+        data = json.dumps(obj.to_dict(), ensure_ascii=False)
+        set_parts = ["json = ?"] + [f"{f} = ?" for f in self.INDEXED_FIELDS]
+
+        sql = (
+            f"UPDATE {self.TABLE_NAME} SET "
+            f"{', '.join(set_parts)} "
+            f"WHERE flexo_id = ?"
+        )
+
+        params = [data]
+        params.extend(getattr(obj, f) for f in self.INDEXED_FIELDS)
+        params.append(obj.flexo_id.value)
+
+        self.conn.execute(sql, params)
+        self.conn.commit()
+
+    def delete(self, flexo_id: str):
+        self.conn.execute(
+            f"DELETE FROM {self.TABLE_NAME} WHERE flexo_id = ?",
+            (flexo_id,),
+        )
+        self.conn.commit()
+
+    # -----------------------------------------------------------
+    # Retrieval
+    # -----------------------------------------------------------
+    def get(self, flexo_id: str):
+        row = self.conn.execute(
+            f"SELECT json FROM {self.TABLE_NAME} WHERE flexo_id = ?",
+            (flexo_id,),
+        ).fetchone()
+        if not row:
+            return None
+        return self.ENTITY_CLASS.from_dict(json.loads(row["json"]))
+
+    def all(self):
+        rows = self.conn.execute(
+            f"SELECT json FROM {self.TABLE_NAME}"
+        ).fetchall()
+        return [
+            self.ENTITY_CLASS.from_dict(json.loads(r["json"])) for r in rows
+        ]
+
+    # -----------------------------------------------------------
+    # Dynamic finders: find_by_<field>
+    # -----------------------------------------------------------
+    def __getattr__(self, name):
+        # e.g. find_by_username("foo")
+        if name.startswith("find_by_"):
+            field = name[len("find_by_"):]
+            if field not in self.INDEXED_FIELDS:
+                raise AttributeError(f"No such indexed field: {field}")
+
+            def finder(value):
+                rows = self.conn.execute(
+                    f"SELECT json FROM {self.TABLE_NAME} WHERE {field} = ?",
+                    (value,),
+                )
+                for row in rows:
+                    yield self.ENTITY_CLASS.from_dict(json.loads(row["json"]))
+
+            return finder
+
+        raise AttributeError(name)
Index: flexoentity/typed_collection.py
===================================================================
--- flexoentity/typed_collection.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
+++ flexoentity/typed_collection.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -0,0 +1,44 @@
+from flexoentity.flexo_collection import FlexoCollection
+from flexoentity import FlexoEntity
+
+class TypedCollection(FlexoCollection):
+    """
+    A type-safe collection of FlexOEntity subclasses.
+    """
+
+    def __init__(self, entity_class, items=None):
+        if not issubclass(entity_class, FlexoEntity):
+            raise TypeError("entity_class must be a subclass of FlexOEntity")
+
+        self.entity_class = entity_class
+        super().__init__()
+
+        if items:
+            for e in items:
+                self.add(e)
+
+    def add(self, entity: FlexoEntity, key=None):
+        if not isinstance(entity, self.entity_class):
+            raise TypeError(
+                f"TypedCollection[{self.entity_class.__name__}] "
+                f"cannot accept {type(entity).__name__}"
+            )
+        super().add(entity, key)
+
+    # ------------------------------------------------------------------
+    # Deserialization
+    # ------------------------------------------------------------------
+    @classmethod
+    def deserialize(cls, entity_class, list_of_dicts):
+        """
+        Create a TypedCollection of `entity_class` from a list of dicts.
+        """
+        # create empty collection
+        coll = cls(entity_class)
+
+        # build entity objects
+        for d in list_of_dicts:
+            entity = entity_class.from_dict(d)
+            coll.add(entity)
+
+        return coll
Index: tests/conftest.py
===================================================================
--- tests/conftest.py	(revision 4f91fed022a3de01238c2feecb1aad16a9603f94)
+++ tests/conftest.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -3,5 +3,5 @@
 from pathlib import Path
 from datetime import datetime
-from flexoentity import Domain, FlexoSignature, DomainManager, EntityRegistry
+from flexoentity import Domain, FlexoSignature, DomainManager, EntityRegistry, CompositeBackend
 from flexoentity import get_signing_backend, CertificateReference
 
@@ -30,5 +30,5 @@
 @pytest.fixture
 def sample_domain_manager():
-    return DomainManager(EntityRegistry())
+    return DomainManager(CompositeBackend(Domain), EntityRegistry())
 
 # ─────────────────────────────────────────────────────────────
Index: tests/test_domain.py
===================================================================
--- tests/test_domain.py	(revision 4f91fed022a3de01238c2feecb1aad16a9603f94)
+++ tests/test_domain.py	(revision c296f7606ce58fb9891ac74bd3c41851f1728f1f)
@@ -20,5 +20,5 @@
     sample_domain_manager.add(sample_domain)
     # Manager must know it now
-    assert sample_domain_manager.get("PY_ARITHM") is sample_domain
+    assert sample_domain_manager.get_by_domain_id("PY_ARITHM") is sample_domain
 
 
@@ -38,7 +38,6 @@
 def test_lookup_by_oid(sample_domain, sample_domain_manager):
     sample_domain_manager.add(sample_domain)
-    found = sample_domain_manager.get(sample_domain.domain_id)
+    found = sample_domain_manager.get_by_domain_id(sample_domain.domain_id)
     assert found is sample_domain
-
 
 # ---------------------------------------------------------------
