def event_sourced_aggregate_factory(element_cls, **opts): element_cls = derive_element_class(element_cls, BaseEventSourcedAggregate, **opts) # Iterate through methods marked as `@apply` and construct a projections map methods = inspect.getmembers(element_cls, predicate=inspect.isroutine) for method_name, method in methods: if not (method_name.startswith("__") and method_name.endswith("__")) and hasattr( method, "_event_cls"): element_cls._projections[fully_qualified_name( method._event_cls)].add(method) element_cls._events_cls_map[fully_qualified_name( method._event_cls)] = method._event_cls # Associate Event with the aggregate class if inspect.isclass(method._event_cls) and issubclass( method._event_cls, BaseEvent): # An Event can only be associated with one aggregate class, but multiple event handlers # can consume it. if (method._event_cls.meta_.aggregate_cls and method._event_cls.meta_.aggregate_cls != element_cls): raise IncorrectUsageError({ "_entity": [ f"{method._event_cls.__name__} Event cannot be associated with" f" {element_cls.__name__} because it is already associated with" f" {method._event_cls.meta_.aggregate_cls.__name__}" ] }) method._event_cls.meta_.aggregate_cls = element_cls return element_cls
def repository_factory(element_cls, **opts): element_cls = derive_element_class(element_cls, BaseRepository, **opts) if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError({ "_entity": [ f"Repository `{element_cls.__name__}` should be associated with an Aggregate" ] }) # FIXME Uncomment # if not issubclass(element_cls.meta_.aggregate_cls, BaseAggregate): # raise IncorrectUsageError( # {"_entity": [f"Repository `{element_cls.__name__}` can only be associated with an Aggregate"]} # ) # Ensure the value of `database` is among known databases if element_cls.meta_.database != "ALL" and element_cls.meta_.database not in [ database.value for database in Database ]: raise IncorrectUsageError({ "_entity": [ f"Repository `{element_cls.__name__}` should be associated with a valid Database" ] }) return element_cls
def entity_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseEntity) if element_cls.meta_.abstract is True: raise NotSupportedError( f"{element_cls.__name__} class has been marked abstract" f" and cannot be instantiated") element_cls.meta_.provider = (kwargs.pop("provider", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.provider) or "default") element_cls.meta_.model = (kwargs.pop("model", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.model) or None) element_cls.meta_.aggregate_cls = (kwargs.pop("aggregate_cls", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.aggregate_cls) or None) if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError( f"Entity `{element_cls.__name__}` needs to be associated with an Aggregate" ) return element_cls
def value_object_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseValueObject) if not (hasattr(element_cls.meta_, "aggregate_cls") and element_cls.meta_.aggregate_cls): element_cls.meta_.aggregate_cls = kwargs.pop("aggregate_cls", None) if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError( "Value Objects need to be associated with an Aggregate") return element_cls
def repository_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseRepository) if not (hasattr(element_cls.meta_, "aggregate_cls") and element_cls.meta_.aggregate_cls): element_cls.meta_.aggregate_cls = kwargs.pop("aggregate_cls", None) if not (hasattr(element_cls.meta_, "database") and element_cls.meta_.database): element_cls.meta_.database = kwargs.pop("database", "ALL") if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError( "Repositories need to be associated with an Aggregate") return element_cls
def aggregate_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseAggregate) if element_cls.meta_.abstract is True: raise NotSupportedError( f"{element_cls.__name__} class has been marked abstract" f" and cannot be instantiated") element_cls.meta_.provider = (kwargs.pop("provider", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.provider) or "default") element_cls.meta_.model = (kwargs.pop("model", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.model) or None) return element_cls
def entity_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseEntity, **kwargs) if element_cls.meta_.abstract is True: raise NotSupportedError({ "_entity": [ f"`{element_cls.__name__}` class has been marked abstract" f" and cannot be instantiated" ] }) if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError({ "_entity": [ f"Entity `{element_cls.__name__}` needs to be associated with an Aggregate" ] }) return element_cls
def command_handler_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseCommandHandler, **kwargs) if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError({ "_entity": [ f"Command Handler `{element_cls.__name__}` needs to be associated with an Aggregate" ] }) # Iterate through methods marked as `@handle` and construct a handler map if not element_cls._handlers: # Protect against re-registration methods = inspect.getmembers(element_cls, predicate=inspect.isroutine) for method_name, method in methods: if not (method_name.startswith("__") and method_name.endswith("__")) and hasattr( method, "_target_cls"): # Do not allow multiple handlers per command if (fully_qualified_name( method._target_cls) in element_cls._handlers and len(element_cls._handlers[fully_qualified_name( method._target_cls)]) != 0): raise NotSupportedError( f"Command {method._target_cls.__name__} cannot be handled by multiple handlers" ) # `_handlers` maps the command to its handler method element_cls._handlers[fully_qualified_name( method._target_cls)].add(method) # Associate Command with the handler's stream if inspect.isclass(method._target_cls) and issubclass( method._target_cls, BaseCommand): # Order of preference: # 1. Stream name defined in command # 2. Stream name derived from aggregate associated with command handler method._target_cls.meta_.stream_name = ( method._target_cls.meta_.stream_name or element_cls.meta_.aggregate_cls.meta_.stream_name) return element_cls
def event_handler_factory(element_cls, **opts): element_cls = derive_element_class(element_cls, BaseEventHandler, **opts) if not (element_cls.meta_.aggregate_cls or element_cls.meta_.stream_name): raise IncorrectUsageError({ "_entity": [ f"Event Handler `{element_cls.__name__}` needs to be associated with an aggregate or a stream" ] }) # Iterate through methods marked as `@handle` and construct a handler map # # Also, if `_target_cls` is an event, associate it with the event handler's # aggregate or stream methods = inspect.getmembers(element_cls, predicate=inspect.isroutine) for method_name, method in methods: if not (method_name.startswith("__") and method_name.endswith("__")) and hasattr( method, "_target_cls"): # `_handlers` is a dictionary mapping the event to the handler method. if method._target_cls == "$any": # This replaces any existing `$any` handler, by design. An Event Handler # can have only one `$any` handler method. element_cls._handlers["$any"] = {method} else: element_cls._handlers[fully_qualified_name( method._target_cls)].add(method) # Associate Event with the handler's stream if inspect.isclass(method._target_cls) and issubclass( method._target_cls, BaseEvent): # Order of preference: # 1. Stream name defined in event # 2. Stream name defined for the event handler # 3. Stream name derived from aggregate stream_name = element_cls.meta_.stream_name or ( element_cls.meta_.aggregate_cls.meta_.stream_name if element_cls.meta_.aggregate_cls else None) method._target_cls.meta_.stream_name = ( method._target_cls.meta_.stream_name or stream_name) return element_cls
def event_sourced_repository_factory(element_cls, **opts): element_cls = derive_element_class(element_cls, BaseEventSourcedRepository, **opts) if not element_cls.meta_.aggregate_cls: raise IncorrectUsageError({ "_entity": [ f"Repository `{element_cls.__name__}` should be associated with an Aggregate" ] }) if not issubclass(element_cls.meta_.aggregate_cls, BaseEventSourcedAggregate): raise IncorrectUsageError({ "_entity": [ f"Repository `{element_cls.__name__}` can only be associated with an Aggregate" ] }) return element_cls
def subscriber_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseSubscriber, **kwargs) if not element_cls.meta_.event: raise IncorrectUsageError( { "_entity": [ f"Subscriber `{element_cls.__name__}` needs to be associated with an Event" ] } ) if not element_cls.meta_.broker: raise IncorrectUsageError( { "_entity": [ f"Subscriber `{element_cls.__name__}` needs to be associated with a Broker" ] } ) return element_cls
def subscriber_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseSubscriber) element_cls.meta_.domain_event = (kwargs.pop("domain_event", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.domain_event) or None) element_cls.meta_.broker = (kwargs.pop("broker", None) or (hasattr(element_cls, "meta_") and element_cls.meta_.broker) or "default") if not element_cls.meta_.domain_event: raise IncorrectUsageError( f"Subscriber `{element_cls.__name__}` needs to be associated with a Domain Event" ) if not element_cls.meta_.broker: raise IncorrectUsageError( f"Subscriber `{element_cls.__name__}` needs to be associated with a Broker" ) return element_cls
def model_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseModel) if hasattr(element_cls, "Meta"): element_cls.meta_ = ModelMeta(element_cls.Meta) else: element_cls.meta_ = ModelMeta() if not (hasattr(element_cls.meta_, "entity_cls") and element_cls.meta_.entity_cls): element_cls.meta_.entity_cls = kwargs.pop("entity_cls", None) if not (hasattr(element_cls.meta_, "schema") and element_cls.meta_.schema): element_cls.meta_.schema = kwargs.pop("schema", None) if not (hasattr(element_cls.meta_, "database") and element_cls.meta_.database): element_cls.meta_.database = kwargs.pop("database", None) if not element_cls.meta_.entity_cls: raise IncorrectUsageError( "Models need to be associated with an Entity or Aggregate") return element_cls
def command_handler_factory(element_cls, **kwargs): return derive_element_class(element_cls, BaseCommandHandler)
def command_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseCommand, **kwargs) return element_cls
def email_factory(element_cls, **kwargs): return derive_element_class(element_cls, BaseEmail)
def application_service_factory(element_cls, **kwargs): return derive_element_class(element_cls, BaseApplicationService, **kwargs)
def aggregate_factory(element_cls, **kwargs): return derive_element_class(element_cls, BaseAggregate, **kwargs)
def domain_event_factory(element_cls, **kwargs): return derive_element_class(element_cls, BaseEvent, **kwargs)
def domain_service_factory(element_cls, **kwargs): return derive_element_class(element_cls, BaseDomainService, **kwargs)
def value_object_factory(element_cls, **kwargs): element_cls = derive_element_class(element_cls, BaseValueObject, **kwargs) return element_cls
def serializer_factory(element_cls, **kwargs): return derive_element_class(element_cls, BaseSerializer, **kwargs)