from .persistance_backend import PersistanceBackend from .flexo_entity import FlexoEntity class CompositeBackend(PersistanceBackend): """ Backend wrapper. Option A semantics: - Reads come from the primary backend only. - Writes propagate to primary and all sync backends. - All backends store/return dicts. """ def __init__(self, authoritative_backend, sync_backends=None): # Validate entity_class existence and compatibility entity_class = getattr(authoritative_backend, "entity_class", None) if entity_class is None: raise TypeError("primary_backend must expose .entity_class") if not issubclass(entity_class, FlexoEntity): raise TypeError("entity_class must be a subclass of FlexoEntity") super().__init__(entity_class) self._primary = authoritative_backend self.sync_backends = list(sync_backends or []) for b in self.sync_backends: if b.entity_class != self._primary.entity_class: raise TypeError( f"Backend {b} does not match entity_class={self.entity_class.__name__}" ) @property def primary(self): return self._primary def add_sync_backend(self, backend, clear=False): if backend.entity_class != self.primary.entity_class: raise TypeError("Backend entity_class mismatch") if clear: backend.clear() # Sync current data into backend for d in self.primary.load_all(): backend.save(d) self.sync_backends.append(backend) def remove_backend(self, backend): if backend is self.primary: raise ValueError("Cannot remove the primary backend") self.sync_backends.remove(backend) # --------------------------------------------------------- # Write operations propagate to *all* backends (dicts) # --------------------------------------------------------- def save(self, entity_dict: dict): self.primary.save(entity_dict) for b in self.sync_backends: b.save(entity_dict) def update(self, entity_dict: dict): self.primary.update(entity_dict) for b in self.sync_backends: b.update(entity_dict) def delete(self, flexo_id: str): self.primary.delete(flexo_id) for b in self.sync_backends: b.delete(flexo_id) # --------------------------------------------------------- # Read operations from primary only # --------------------------------------------------------- def load(self, flexo_id: str): return self.primary.load(flexo_id) def load_all(self): return self.primary.load_all() # --------------------------------------------------------- # Sync helpers # --------------------------------------------------------- def sync_all(self, clear_targets=False): """ Push all data from primary backend to the other backends. If clear_targets=True, wipe sync backends first. """ if clear_targets: for b in self.sync_backends: b.clear() for d in self.primary.load_all(): for b in self.sync_backends: b.save(d) def clear(self): self.primary.clear() for b in self.sync_backends: b.clear() def __repr__(self): names = ", ".join(b.__class__.__name__ for b in self.sync_backends) return f""