Skip to content

Commit

Permalink
Remove domain-level event raise_ functionality
Browse files Browse the repository at this point in the history
Events, at this point, should always be connected to aggregates,
and raised from within them as part of business logic. Directly
adding events at the domain level was helper functionality, but
with the addition of metadata and headers at the event level,
events have a lot more contextuality. They cannot be raised or
appended to event store directly.

We will need to revisit this functionality if we need Domain
Events (or some variation thereof) that are not connected to
aggregates.
  • Loading branch information
subhashb committed Jun 27, 2024
1 parent f89ec6f commit 6b49016
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 34 deletions.
2 changes: 1 addition & 1 deletion src/protean/core/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __new__(cls, *args, **kwargs):
return super().__new__(cls)

# Track Metadata
_metadata = ValueObject(Metadata, default=lambda: Metadata())
_metadata = ValueObject(Metadata, default=lambda: Metadata()) # pragma: no cover

def __init_subclass__(subclass) -> None:
super().__init_subclass__()
Expand Down
18 changes: 16 additions & 2 deletions src/protean/core/event_sourced_repository.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging

from protean import BaseEventSourcedAggregate
from protean import BaseEventSourcedAggregate, UnitOfWork
from protean.container import Element, OptionsMixin
from protean.exceptions import (
IncorrectUsageError,
Expand Down Expand Up @@ -31,7 +31,21 @@ def __init__(self, domain) -> None:
self._domain = domain

def add(self, aggregate: BaseEventSourcedAggregate) -> None:
current_uow._add_to_identity_map(aggregate)
# `add` is typically invoked in handler methods in Command Handlers and Event Handlers, which are
# enclosed in a UoW automatically. Therefore, if there is a UoW in progress, we can assume
# that it is the active session. If not, we will start a new UoW and commit it after the operation
# is complete.
own_current_uow = None
if not (current_uow and current_uow.in_progress):
own_current_uow = UnitOfWork()
own_current_uow.start()

uow = current_uow or own_current_uow
uow._add_to_identity_map(aggregate)

# If we started a UnitOfWork, commit it now
if own_current_uow:
own_current_uow.commit()

def get(self, identifier: Identifier) -> BaseEventSourcedAggregate:
"""Retrieve a fully-formed Aggregate from a stream of Events.
Expand Down
12 changes: 0 additions & 12 deletions src/protean/domain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -949,18 +949,6 @@ def command_handler_for(self, command: BaseCommand) -> Optional[BaseCommandHandl
###################
# Handling Events #
###################
def raise_(self, event: BaseEvent) -> None:
"""Raise Domain Event in the stream specified in its options, or using the associated
aggregate's stream name.
Args:
event (BaseEvent): Event to record
Returns:
None: Returns nothing.
"""
self.event_store.store.append(event)

def handlers_for(self, event: BaseEvent) -> List[BaseEventHandler]:
"""Return Event Handlers listening to a specific event
Expand Down
18 changes: 0 additions & 18 deletions tests/event/test_event_meta.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from uuid import uuid4

import pytest

from protean import BaseEvent, BaseEventSourcedAggregate
Expand Down Expand Up @@ -36,22 +34,6 @@ def test_event_definition_without_aggregate_or_stream(test_domain):
}


def test_event_definition_with_just_part_of(test_domain):
try:
identifier = str(uuid4())
test_domain.raise_(UserLoggedIn(user_id=identifier))
except IncorrectUsageError:
pytest.fail("Failed raising event when associated with Aggregate")


def test_event_definition_with_just_stream(test_domain):
try:
identifier = str(uuid4())
test_domain.raise_(UserLoggedIn(user_id=identifier))
except IncorrectUsageError:
pytest.fail("Failed raising event when associated with Stream")


def test_that_abstract_events_can_be_defined_without_aggregate_or_stream(test_domain):
class AbstractEvent(BaseEvent):
foo = String()
Expand Down
5 changes: 4 additions & 1 deletion tests/event/test_raising_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ def test_raising_event(test_domain):
test_domain.register(UserLoggedIn, stream_name="authentication")

identifier = str(uuid4())
test_domain.raise_(UserLoggedIn(user_id=identifier))
user = User(id=identifier, email="[email protected]", name="Test User")
user.raise_(UserLoggedIn(user_id=identifier))

test_domain.repository_for(User).add(user)

messages = test_domain.event_store.store.read("authentication")

Expand Down
37 changes: 37 additions & 0 deletions tests/event_sourced_repository/test_add_uow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import mock
import pytest

from protean import BaseEventSourcedAggregate
from protean.fields import Identifier, String


class User(BaseEventSourcedAggregate):
id = Identifier(identifier=True)
email = String()
name = String()


@pytest.fixture(autouse=True)
def register_elements(test_domain):
test_domain.register(User)
test_domain.init(traverse=False)


@mock.patch("protean.core.repository.UnitOfWork.start")
@mock.patch("protean.core.repository.UnitOfWork.commit")
def test_that_method_is_enclosed_in_uow(mock_commit, mock_start, test_domain):
mock_parent = mock.Mock()

mock_parent.attach_mock(mock_start, "m1")
mock_parent.attach_mock(mock_commit, "m2")

with test_domain.domain_context():
user = User(id=1, email="[email protected]", name="John Doe")
test_domain.repository_for(User).add(user)

mock_parent.assert_has_calls(
[
mock.call.m1(),
mock.call.m2(),
]
)

0 comments on commit 6b49016

Please sign in to comment.