Aggregate
This section provide detail information about Aggregate, Event and their interaction
Create custom aggregate
from pyeventor.aggregate import Aggregate
from random import rarandintnd
class CustomAggregate(Aggregate[int]):
def _init_empty_attributes(self):
# this method should be overrided in case of provided attributes to event
# to set the default values for them
# without this it'll be tricky to make a right annotation
self.attrib: int = 0
@classmethod
def _id_factory(cls) -> int:
# defining the factory for ids.
# return type should be the same as the generic in the class definition
# this id will be used to find the aggregate in event store and work with it
return randint()
aggregate = CustomAggregate() # _id_factory is ued for id
aggregate = CustomAggregate(20) # id is provided manually
Create custom event
from pyeventor.event import Event
from random import rarandintnd
from datetime import datetime
class CustomEvent(Event[int, datetime]):
def _sequence_generate(self) -> datetime:
# define the generator for the event sequence
# according to that it will define the order of events later
# it should be consistent and provide an order for all the events of the Aggregate
return datetime.now()
event = CustomEvent(2) # _sequence_generate is ued for sequence_order
assert event.data == 2
event = CustomEvent(2, sequence_order=datetime.now()) # sequence_order is provided manually
Set up handlers for events
The main flow for registration
from pyeventor.aggregate import Aggregate
from pyeventor.event import Event
from pyeventor.decorator import register_handler
class CustomEventA(Event[int, None]):
...
class CustomAggregate(Aggregate[int]):
@register_handler(CustomEventA) # register from aggregate
def custom_handler(self, event: CustomEventA):
...
class CustomEventC(Event[int, None]):
@register_handler(CustomAggregate) # register from event
def custom_handler(self, aggregate: CustomAggregate):
...
aggregate = CustomAggregate()
event_a = CustomEventA()
event_b = CustomEventB()
aggregate.apply(event_a)
aagregate.apply(event_b)
The behaviour for event inheritance
from pyeventor.aggregate import Aggregate
from pyeventor.event import Event
from pyeventor.decorator import register_handler
class CustomEventA(Event[int, None]):
...
class CustomEventB(Event[int, None]):
...
class CustomAggregate(Aggregate[int]):
@register_handler(Event) # use for any event and derived classes
def custom_handler(self, event: Event):
...
@register_handler(CustomEventA) # use for CustomEventA and derived classes
def special_custom_handler(self, event: CustomEventA):
...
aggregate = CustomAggregate()
event_a = CustomEventA()
event_b = CustomEventB()
aggregate.apply(event_a) # special_custom_handler will be used as more specific
aagregate.apply(event_b) # custom_handler will be used as there is no more specific handler
The behaviour for aggregate inheritance
from pyeventor.aggregate import Aggregate
from pyeventor.event import Event
from pyeventor.decorator import register_handler
class CustomEventA(Event[int, None]):
...
class CustomEventB(Event[int, None]):
...
class CustomAggregate(Aggregate[int]):
@register_handler(CustomEventB)
def custom_handler(self, event: CustomEventB):
...
@register_handler(CustomEventA)
def special_custom_handler(self, event: CustomEventA):
...
class CustomDerivedAggregate(CustomAggregate[int]):
@register_handler(CustomEventB)
def some_handler(self, event: CustomEventB):
...
def special_custom_handler(self, event: CustomEventA): # won't be used anyway, as not registered
...
aggregate = CustomDerivedAggregate()
event_a = CustomEventA()
event_b = CustomEventB()
aggregate.apply(event_a) # custom_handler from CustomAggregate will be used
aagregate.apply(event_b) # some_handler from CustomDerivedAggregate will be used as more specific
the flow for resolving the handlers is:
try to find the handler for exact class for exact event
try to find the handler for exact class for base of event class
get the base of aggregate class and use it to resolve the handling with steps 1-3