def __init__(self, event_store, mutator=None, snapshot_strategy=None, use_cache=False, *args, **kwargs): super(EventSourcedRepository, self).__init__(*args, **kwargs) self._cache = {} self._snapshot_strategy = snapshot_strategy # self._use_cache = use_cache # Check we got an event store. assert isinstance(event_store, AbstractEventStore), type(event_store) self._event_store = event_store # Instantiate an event player for this repo. mutator = mutator or type(self).mutator self.event_player = EventPlayer( event_store=self.event_store, mutator=mutator, page_size=self.__page_size__, is_short=self.__is_short__, snapshot_strategy=self._snapshot_strategy, )
def __init__(self, event_store, mutator=None, snapshot_strategy=None, use_cache=False): self._cache = {} self._snapshot_strategy = snapshot_strategy # self._use_cache = use_cache # Check we got an event store. assert isinstance(event_store, AbstractEventStore) self.event_store = event_store # Instantiate an event player for this repo. mutator = mutator or type(self).mutator if mutator is None: raise ValueError( "Repository needs a mutator function (set class attribute or pass constructor arg)" ) self.event_player = EventPlayer( event_store=self.event_store, mutator=mutator, page_size=self.__page_size__, is_short=self.__is_short__, snapshot_strategy=self._snapshot_strategy, )
def test_replay_entity(self): # Store example events. # Create entity1. entity_id1 = uuid4() event1 = Example.Created(originator_id=entity_id1, a=1, b=2) self.entity_event_store.append(event1) # Create entity2. entity_id2 = uuid4() event2 = Example.Created(originator_id=entity_id2, a=2, b=4) self.entity_event_store.append(event2) # Create entity3. entity_id3 = uuid4() event3 = Example.Created(originator_id=entity_id3, a=3, b=6) self.entity_event_store.append(event3) # Discard entity3. event4 = Example.Discarded(originator_id=entity_id3, originator_version=1) self.entity_event_store.append(event4) # Check the entities can be replayed. event_player = EventPlayer(event_store=self.entity_event_store, mutator=Example._mutate) # Check recovered entities have correct attribute values. recovered1 = event_player.replay_entity(entity_id1) self.assertEqual(entity_id1, recovered1.id) self.assertEqual(1, recovered1.a) recovered2 = event_player.replay_entity(entity_id2) self.assertEqual(2, recovered2.a) recovered3 = event_player.replay_entity(entity_id3) self.assertEqual(None, recovered3) # Check it works for "short" entities (should be faster, but the main thing is that it still works). # - just use a trivial mutate that always instantiates the 'Example'. event5 = Example.AttributeChanged(originator_id=entity_id1, originator_version=1, name='a', value=10) self.entity_event_store.append(event5) recovered1 = event_player.replay_entity(entity_id1) self.assertEqual(10, recovered1.a) event_player = EventPlayer( event_store=self.entity_event_store, mutator=Example._mutate, is_short=True, ) self.assertEqual(10, event_player.replay_entity(entity_id1).a)
def test_take_snapshot(self): self.entity_persistence_policy = PersistencePolicy( event_store=self.entity_event_store, event_type=VersionedEntity.Event, ) self.snapshot_persistence_policy = PersistencePolicy( event_store=self.snapshot_store, event_type=Snapshot, ) snapshot_strategy = EventSourcedSnapshotStrategy( event_store=self.snapshot_store ) event_player = EventPlayer( event_store=self.entity_event_store, mutator=Example._mutate, snapshot_strategy=snapshot_strategy ) # Take a snapshot with a non-existent ID. unregistered_id = uuid4() # Check no snapshot is taken. self.assertIsNone(event_player.take_snapshot(unregistered_id)) # Check no snapshot is available. self.assertIsNone(event_player.get_snapshot(unregistered_id)) # Create a new entity. registered_example = create_new_example(a=123, b=234) # Take a snapshot of the new entity (no previous snapshots). snapshot1 = event_player.take_snapshot(registered_example.id, lt=registered_example.version) # Check the snapshot is pegged to the last applied version. self.assertEqual(snapshot1.originator_version, 0) # Replay from this snapshot. entity_from_snapshot1 = entity_from_snapshot(snapshot1) retrieved_example = event_player.replay_entity(registered_example.id, initial_state=entity_from_snapshot1, gte=entity_from_snapshot1._version) # Check the attributes are correct. self.assertEqual(retrieved_example.a, 123) # Remember the version now. version1 = retrieved_example._version self.assertEqual(version1, 1) # Change attribute value. retrieved_example.a = 999 # Remember the version now. version2 = retrieved_example._version self.assertEqual(version2, 2) # Change attribute value. retrieved_example.a = 9999 # Remember the version now. version3 = retrieved_example._version self.assertEqual(version3, 3) # Check the event sourced entities are correct. retrieved_example = event_player.replay_entity(registered_example.id) self.assertEqual(retrieved_example.a, 9999) # Take another snapshot. snapshot2 = event_player.take_snapshot(retrieved_example.id, lt=retrieved_example.version) # Replay from this snapshot. initial_state = entity_from_snapshot(snapshot2) retrieved_example = event_player.replay_entity( registered_example.id, initial_state=initial_state, gte=initial_state._version, ) # Check the attributes are correct. self.assertEqual(retrieved_example.a, 9999) # Check we can get historical state at version1. retrieved_example = event_player.replay_entity(registered_example.id, lt=version1) self.assertEqual(retrieved_example.a, 123) # Check we can get historical state at version2. retrieved_example = event_player.replay_entity(registered_example.id, lt=version2) self.assertEqual(retrieved_example.a, 999) # Check we can get historical state at version3. retrieved_example = event_player.replay_entity(registered_example.id, lt=version3) self.assertEqual(retrieved_example.a, 9999) # Similarly, check we can get historical state using a snapshot initial_state = entity_from_snapshot(snapshot1) retrieved_example = event_player.replay_entity( registered_example.id, initial_state=initial_state, gte=initial_state._version, lt=version2, ) self.assertEqual(retrieved_example.a, 999) # Discard the entity. registered_example = event_player.replay_entity(registered_example.id) registered_example.discard() # Take snapshot of discarded entity. snapshot3 = event_player.take_snapshot(registered_example.id) self.assertIsNone(snapshot3.state) self.assertIsNone(entity_from_snapshot(snapshot3))
class EventSourcedRepository(AbstractEntityRepository): # If the entity won't have very many events, marking the entity as # "short" by setting __is_short__ value equal to True will mean # the fastest path for getting all the events is used. If you set # a value for page size (see below), this option will have no effect. __is_short__ = False # The page size by which events are retrieved. If this # value is set to a positive integer, the events of # the entity will be retrieved in pages, using a series # of queries, rather than with one potentially large query. __page_size__ = None # The mutator function used by this repository. Can either # be set as a class attribute, or passed as a constructor arg. mutator = mutate_entity def __init__(self, event_store, mutator=None, snapshot_strategy=None, use_cache=False, *args, **kwargs): super(EventSourcedRepository, self).__init__(*args, **kwargs) self._cache = {} self._snapshot_strategy = snapshot_strategy # self._use_cache = use_cache # Check we got an event store. assert isinstance(event_store, AbstractEventStore), type(event_store) self._event_store = event_store # Instantiate an event player for this repo. mutator = mutator or type(self).mutator self.event_player = EventPlayer( event_store=self.event_store, mutator=mutator, page_size=self.__page_size__, is_short=self.__is_short__, snapshot_strategy=self._snapshot_strategy, ) @property def event_store(self): return self._event_store def __contains__(self, entity_id): """ Returns a boolean value according to whether entity with given ID exists. """ return self.get_entity(entity_id) is not None def __getitem__(self, entity_id): """ Returns entity with given ID. """ # # Get entity from the cache. # if self._use_cache: # try: # return self._cache[entity_id] # except KeyError: # pass # Reconstitute the entity. entity = self.get_entity(entity_id) # Never created or already discarded? if entity is None: raise RepositoryKeyError(entity_id) # # Put entity in the cache. # if self._use_cache: # self.add_cache(entity_id, entity) # Return entity. return entity # def add_cache(self, entity_id, entity): # self._cache[entity_id] = entity def get_entity(self, entity_id, lt=None, lte=None): """ Returns entity with given ID, optionally until position. """ # Get a snapshot (None if none exist). if self._snapshot_strategy is not None: snapshot = self._snapshot_strategy.get_snapshot(entity_id, lt=lt, lte=lte) else: snapshot = None # Decide the initial state of the entity, and the # version of the last item applied to the entity. if snapshot is None: initial_state = None gt = None else: initial_state = entity_from_snapshot(snapshot) gt = snapshot.originator_version # Replay domain events. return self.event_player.replay_entity(entity_id, gt=gt, lt=lt, lte=lte, initial_state=initial_state) def take_snapshot(self, entity_id, lt=None, lte=None): return self.event_player.take_snapshot(entity_id, lt=lt, lte=lte)
def test_take_snapshot(self): self.entity_persistence_policy = PersistencePolicy( event_store=self.entity_event_store, event_type=VersionedEntity.Event, ) self.snapshot_persistence_policy = PersistencePolicy( event_store=self.snapshot_store, event_type=Snapshot, ) snapshot_strategy = EventSourcedSnapshotStrategy( event_store=self.snapshot_store) event_player = EventPlayer(event_store=self.entity_event_store, mutator=Example._mutate, snapshot_strategy=snapshot_strategy) # Take a snapshot with a non-existent ID. unregistered_id = uuid4() # Check no snapshot is taken. self.assertIsNone(event_player.take_snapshot(unregistered_id)) # Check no snapshot is available. self.assertIsNone(event_player.get_snapshot(unregistered_id)) # Create a new entity. registered_example = create_new_example(a=123, b=234) # Take a snapshot of the new entity (no previous snapshots). snapshot1 = event_player.take_snapshot(registered_example.id, lt=registered_example.version) # Check the snapshot is pegged to the last applied version. self.assertEqual(snapshot1.originator_version, 0) # Replay from this snapshot. entity_from_snapshot1 = entity_from_snapshot(snapshot1) retrieved_example = event_player.replay_entity( registered_example.id, initial_state=entity_from_snapshot1, gte=entity_from_snapshot1._version) # Check the attributes are correct. self.assertEqual(retrieved_example.a, 123) # Remember the version now. version1 = retrieved_example._version self.assertEqual(version1, 1) # Change attribute value. retrieved_example.a = 999 # Remember the version now. version2 = retrieved_example._version self.assertEqual(version2, 2) # Change attribute value. retrieved_example.a = 9999 # Remember the version now. version3 = retrieved_example._version self.assertEqual(version3, 3) # Check the event sourced entities are correct. retrieved_example = event_player.replay_entity(registered_example.id) self.assertEqual(retrieved_example.a, 9999) # Take another snapshot. snapshot2 = event_player.take_snapshot(retrieved_example.id, lt=retrieved_example.version) # Replay from this snapshot. initial_state = entity_from_snapshot(snapshot2) retrieved_example = event_player.replay_entity( registered_example.id, initial_state=initial_state, gte=initial_state._version, ) # Check the attributes are correct. self.assertEqual(retrieved_example.a, 9999) # Check we can get historical state at version1. retrieved_example = event_player.replay_entity(registered_example.id, lt=version1) self.assertEqual(retrieved_example.a, 123) # Check we can get historical state at version2. retrieved_example = event_player.replay_entity(registered_example.id, lt=version2) self.assertEqual(retrieved_example.a, 999) # Check we can get historical state at version3. retrieved_example = event_player.replay_entity(registered_example.id, lt=version3) self.assertEqual(retrieved_example.a, 9999) # Similarly, check we can get historical state using a snapshot initial_state = entity_from_snapshot(snapshot1) retrieved_example = event_player.replay_entity( registered_example.id, initial_state=initial_state, gte=initial_state._version, lt=version2, ) self.assertEqual(retrieved_example.a, 999) # Discard the entity. registered_example = event_player.replay_entity(registered_example.id) registered_example.discard() # Take snapshot of discarded entity. snapshot3 = event_player.take_snapshot(registered_example.id) self.assertIsNone(snapshot3.state) self.assertIsNone(entity_from_snapshot(snapshot3))
def test_get_entity(self): # Store example events. entity_id1 = uuid4() event1 = Example.Created(entity_id=entity_id1, a=1, b=2) self.version_entity_event_store.append(event1) entity_id2 = uuid4() event2 = Example.Created(entity_id=entity_id2, a=2, b=4) self.version_entity_event_store.append(event2) entity_id3 = uuid4() event3 = Example.Created(entity_id=entity_id3, a=3, b=6) self.version_entity_event_store.append(event3) event4 = Example.Discarded(entity_id=entity_id3, entity_version=1) self.version_entity_event_store.append(event4) # Check the event sourced entities are correct. # - just use a trivial mutate that always instantiates the 'Example'. event_player = EventPlayer(event_store=self.version_entity_event_store, mutator=Example.mutate) # The the reconstituted entity has correct attribute values. self.assertEqual(entity_id1, event_player.replay_entity(entity_id1).id) self.assertEqual(1, event_player.replay_entity(entity_id1).a) self.assertEqual(2, event_player.replay_entity(entity_id2).a) self.assertEqual(None, event_player.replay_entity(entity_id3)) # Check entity3 raises KeyError. self.assertEqual(event_player.replay_entity(entity_id3), None) # Check it works for "short" entities (should be faster, but the main thing is that it still works). # - just use a trivial mutate that always instantiates the 'Example'. event5 = Example.AttributeChanged(entity_id=entity_id1, entity_version=1, name='a', value=10) self.version_entity_event_store.append(event5) event_player = EventPlayer(event_store=self.version_entity_event_store, mutator=Example.mutate) self.assertEqual(10, event_player.replay_entity(entity_id1).a) event_player = EventPlayer( event_store=self.version_entity_event_store, mutator=Example.mutate, is_short=True, ) self.assertEqual(10, event_player.replay_entity(entity_id1).a)
def test_snapshots(self): self.policy = CombinedPersistencePolicy( versioned_entity_event_store=self.version_entity_event_store, timestamped_entity_event_store=self.timestamp_entity_event_store, ) event_player = EventPlayer( event_store=self.version_entity_event_store, mutator=Example.mutate, snapshot_strategy=EventSourcedSnapshotStrategy( event_store=self.timestamp_entity_event_store ) ) # Take a snapshot with a non-existent ID. self.assertIsNone(event_player.take_snapshot(uuid4())) # Create a new entity. registered_example = register_new_example(a=123, b=234) # Take a snapshot. snapshot1 = event_player.take_snapshot(registered_example.id) # Replay from this snapshot. initial_state = entity_from_snapshot(snapshot1) retrieved_example = event_player.replay_entity(registered_example.id, initial_state=initial_state, gte=initial_state._version) # Check the attributes are correct. self.assertEqual(retrieved_example.a, 123) # Remember the version now. version1 = retrieved_example._version # Change attribute value. retrieved_example.a = 999 # Remember the version now. version2 = retrieved_example._version # Change attribute value. retrieved_example.a = 9999 # Remember the version now. version3 = retrieved_example._version # Check the event sourced entities are correct. retrieved_example = event_player.replay_entity(registered_example.id) self.assertEqual(retrieved_example.a, 9999) # Take another snapshot. snapshot2 = event_player.take_snapshot(retrieved_example.id) # Check we can replay from this snapshot. initial_state = entity_from_snapshot(snapshot2) retrieved_example = event_player.replay_entity( registered_example.id, initial_state=initial_state, gte=initial_state._version, ) # Check the attributes are correct. self.assertEqual(retrieved_example.a, 9999) # Check we can get historical state at version1. retrieved_example = event_player.replay_entity(registered_example.id, lt=version1) self.assertEqual(retrieved_example.a, 123) # Check we can get historical state at version2. retrieved_example = event_player.replay_entity(registered_example.id, lt=version2) self.assertEqual(retrieved_example.a, 999) # Check we can get historical state at version3. retrieved_example = event_player.replay_entity(registered_example.id, lt=version3) self.assertEqual(retrieved_example.a, 9999) # Similarly, check we can get historical state using a snapshot initial_state = entity_from_snapshot(snapshot1) retrieved_example = event_player.replay_entity( registered_example.id, initial_state=initial_state, gte=initial_state._version, lt=version2, ) self.assertEqual(retrieved_example.a, 999)