Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
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
Example #5
0
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
Example #6
0
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
Example #7
0
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
Example #8
0
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
Example #9
0
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
Example #11
0
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
Example #12
0
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
Example #13
0
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
Example #14
0
def command_handler_factory(element_cls, **kwargs):
    return derive_element_class(element_cls, BaseCommandHandler)
Example #15
0
def command_factory(element_cls, **kwargs):
    element_cls = derive_element_class(element_cls, BaseCommand, **kwargs)

    return element_cls
Example #16
0
def email_factory(element_cls, **kwargs):
    return derive_element_class(element_cls, BaseEmail)
Example #17
0
def application_service_factory(element_cls, **kwargs):
    return derive_element_class(element_cls, BaseApplicationService, **kwargs)
Example #18
0
def aggregate_factory(element_cls, **kwargs):
    return derive_element_class(element_cls, BaseAggregate, **kwargs)
Example #19
0
def domain_event_factory(element_cls, **kwargs):
    return derive_element_class(element_cls, BaseEvent, **kwargs)
Example #20
0
def domain_service_factory(element_cls, **kwargs):
    return derive_element_class(element_cls, BaseDomainService, **kwargs)
Example #21
0
def value_object_factory(element_cls, **kwargs):
    element_cls = derive_element_class(element_cls, BaseValueObject, **kwargs)

    return element_cls
Example #22
0
def serializer_factory(element_cls, **kwargs):
    return derive_element_class(element_cls, BaseSerializer, **kwargs)