Index: flexoentity/domain.py
===================================================================
--- flexoentity/domain.py	(revision fd1913ff6e9ed33e7efe77fc664643ced7e81919)
+++ flexoentity/domain.py	(revision 95929362edb4f1170dc4e04ed3ae0c1ac0ce5e3c)
@@ -49,4 +49,5 @@
             classification=data.get("classification", "UNCLASSIFIED"),
             flexo_id=flexo_id,
+            _in_factory=True
         )
 
Index: flexoentity/domain_manager.py
===================================================================
--- flexoentity/domain_manager.py	(revision fd1913ff6e9ed33e7efe77fc664643ced7e81919)
+++ flexoentity/domain_manager.py	(revision 95929362edb4f1170dc4e04ed3ae0c1ac0ce5e3c)
@@ -73,5 +73,5 @@
                                          entity_type=EntityType.DOMAIN.value,
                                          state=EntityState.DRAFT.value, text=domain_id)
-        domain = Domain(flexo_id=flexo_id, **kwargs)
+        domain = Domain(flexo_id=flexo_id, _in_factory=True, **kwargs)
         return cls.register(domain)
 
Index: flexoentity/flexo_entity.py
===================================================================
--- flexoentity/flexo_entity.py	(revision fd1913ff6e9ed33e7efe77fc664643ced7e81919)
+++ flexoentity/flexo_entity.py	(revision 95929362edb4f1170dc4e04ed3ae0c1ac0ce5e3c)
@@ -155,4 +155,5 @@
     I am the living, editable layer that connects identity with accountability.
     """
+    _in_factory: bool = field(default=False, repr=False)
     subtype: str = "GENERIC"
     flexo_id: Optional[FlexOID] = field(default=None)
@@ -161,5 +162,5 @@
     owner_id: UUID = field(default=UUID(int=0))
     origin: Optional[str] = field(default=None)
-    
+
     def with_new_owner(self, new_owner: UUID):
         """I return a clone of this entity with a different owner."""
@@ -229,5 +230,5 @@
         )
 
-        obj = cls(flexo_id=flexo_id, **kwargs)
+        obj = cls(flexo_id=flexo_id, _in_factory=True, **kwargs)
         obj.fingerprint = obj._compute_fingerprint()
         return obj
@@ -240,5 +241,9 @@
             - Domain must always match flexo_id.domain.
         """
-
+        if not self._in_factory:
+            raise RuntimeError(
+                f"{self.__class__.__name__} must be created via with_domain_id() "
+                f"or from_dict(), not via direct construction."
+            )
         # Step 1: If flexo_id exists, restore canonical domain
         if self.flexo_id:
@@ -252,5 +257,5 @@
             raise ValueError(
                 f"{self.__class__.__name__} created without domain; "
-                f"use {self.__class__.__name__}.with_domain(domain=...) or pass domain explicitly."
+                f"use {self.__class__.__name__}.with_domain_id(domain=...) or pass domain explicitly."
             )
 
Index: tests/conftest.py
===================================================================
--- tests/conftest.py	(revision fd1913ff6e9ed33e7efe77fc664643ced7e81919)
+++ tests/conftest.py	(revision 95929362edb4f1170dc4e04ed3ae0c1ac0ce5e3c)
@@ -4,5 +4,5 @@
 from dataclasses import dataclass, field
 from typing import List
-from flexoentity import FlexOID, FlexoEntity, EntityType, EntityState, Domain
+from flexoentity import FlexOID, FlexoEntity, EntityType, EntityState, Domain, DomainManager
 
 @pytest.fixture
@@ -15,4 +15,14 @@
     return FixedDate
 
+@pytest.fixture(autouse=True)
+def reset_domain_manager():
+    DomainManager.clear()   # You need to implement this
+    yield
+    DomainManager.clear()
+
+@pytest.fixture(autouse=True)
+def auto_domains():
+    Domain.with_domain_id("GENERAL", fullname="General Domain")
+    Domain.with_domain_id("TEST", fullname="Test Domain")
 
 @dataclass
@@ -46,7 +56,7 @@
         if not getattr(self, "flexo_id", None):
             self.flexo_id = FlexOID.safe_generate(
-                domain=self.domain_id,
+                domain_id=self.domain_id,
                 entity_type=SingleChoiceQuestion.ENTITY_TYPE.value,     # 'I'
-                estate=EntityState.DRAFT.value,        # 'D'
+                state=EntityState.DRAFT.value,        # 'D'
                 text=self.text_seed or self.text,
                 version=1,
@@ -88,18 +98,13 @@
 def sample_domain():
     domain_id = "PY_ARITHM"
-    flexo_id = FlexOID.safe_generate(domain_id=domain_id,
-                                     entity_type=EntityType.DOMAIN.value,
-                                     state=EntityState.DRAFT.value, text=domain_id)
-    return Domain(flexo_id=flexo_id, fullname="PYTHON_ARITHMETIC",
-                  description="ALL ABOUT ARITHMETIC IN PYTHON")
+    return Domain.with_domain_id(domain_id=domain_id,
+                                 fullname="PYTHON_ARITHMETIC",
+                                 description="ALL ABOUT ARITHMETIC IN PYTHON")
 
 @pytest.fixture
 def sample_question(sample_domain):
-    flexo_id = FlexOID.safe_generate(domain_id=sample_domain.domain_id,
-                                     entity_type=EntityType.ITEM.value,
-                                     state=EntityState.DRAFT.value,
-                                     text="What is 2 + 2")
-    q = SingleChoiceQuestion(flexo_id=flexo_id, text="What is 2 + 2?",
-                             options=[])
+    q = SingleChoiceQuestion.with_domain_id(domain_id=sample_domain.domain_id,
+                                            text="What is 2 + 2?",
+                                            options=[])
     q._update_fingerprint()
     return q
