eventsourcing 9.3.3__py3-none-any.whl → 9.3.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of eventsourcing might be problematic. Click here for more details.
- eventsourcing/application.py +0 -15
- eventsourcing/system.py +25 -2
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/METADATA +1 -1
- eventsourcing-9.3.5.dist-info/RECORD +24 -0
- eventsourcing/examples/__init__.py +0 -0
- eventsourcing/examples/aggregate1/__init__.py +0 -0
- eventsourcing/examples/aggregate1/application.py +0 -27
- eventsourcing/examples/aggregate1/domainmodel.py +0 -16
- eventsourcing/examples/aggregate1/test_application.py +0 -37
- eventsourcing/examples/aggregate2/__init__.py +0 -0
- eventsourcing/examples/aggregate2/application.py +0 -27
- eventsourcing/examples/aggregate2/domainmodel.py +0 -22
- eventsourcing/examples/aggregate2/test_application.py +0 -37
- eventsourcing/examples/aggregate3/__init__.py +0 -0
- eventsourcing/examples/aggregate3/application.py +0 -27
- eventsourcing/examples/aggregate3/domainmodel.py +0 -38
- eventsourcing/examples/aggregate3/test_application.py +0 -37
- eventsourcing/examples/aggregate4/__init__.py +0 -0
- eventsourcing/examples/aggregate4/application.py +0 -27
- eventsourcing/examples/aggregate4/domainmodel.py +0 -114
- eventsourcing/examples/aggregate4/test_application.py +0 -38
- eventsourcing/examples/aggregate5/__init__.py +0 -0
- eventsourcing/examples/aggregate5/application.py +0 -27
- eventsourcing/examples/aggregate5/domainmodel.py +0 -131
- eventsourcing/examples/aggregate5/test_application.py +0 -38
- eventsourcing/examples/aggregate6/__init__.py +0 -0
- eventsourcing/examples/aggregate6/application.py +0 -30
- eventsourcing/examples/aggregate6/domainmodel.py +0 -123
- eventsourcing/examples/aggregate6/test_application.py +0 -38
- eventsourcing/examples/aggregate6a/__init__.py +0 -0
- eventsourcing/examples/aggregate6a/application.py +0 -40
- eventsourcing/examples/aggregate6a/domainmodel.py +0 -149
- eventsourcing/examples/aggregate6a/test_application.py +0 -45
- eventsourcing/examples/aggregate7/__init__.py +0 -0
- eventsourcing/examples/aggregate7/application.py +0 -53
- eventsourcing/examples/aggregate7/domainmodel.py +0 -142
- eventsourcing/examples/aggregate7/persistence.py +0 -57
- eventsourcing/examples/aggregate7/test_application.py +0 -45
- eventsourcing/examples/aggregate7/test_compression_and_encryption.py +0 -45
- eventsourcing/examples/aggregate7/test_snapshotting_intervals.py +0 -67
- eventsourcing/examples/aggregate7a/__init__.py +0 -0
- eventsourcing/examples/aggregate7a/application.py +0 -56
- eventsourcing/examples/aggregate7a/domainmodel.py +0 -168
- eventsourcing/examples/aggregate7a/test_application.py +0 -46
- eventsourcing/examples/aggregate7a/test_compression_and_encryption.py +0 -45
- eventsourcing/examples/aggregate8/__init__.py +0 -0
- eventsourcing/examples/aggregate8/application.py +0 -47
- eventsourcing/examples/aggregate8/domainmodel.py +0 -71
- eventsourcing/examples/aggregate8/persistence.py +0 -57
- eventsourcing/examples/aggregate8/test_application.py +0 -44
- eventsourcing/examples/aggregate8/test_compression_and_encryption.py +0 -44
- eventsourcing/examples/aggregate8/test_snapshotting_intervals.py +0 -38
- eventsourcing/examples/bankaccounts/__init__.py +0 -0
- eventsourcing/examples/bankaccounts/application.py +0 -70
- eventsourcing/examples/bankaccounts/domainmodel.py +0 -56
- eventsourcing/examples/bankaccounts/test.py +0 -173
- eventsourcing/examples/cargoshipping/__init__.py +0 -0
- eventsourcing/examples/cargoshipping/application.py +0 -126
- eventsourcing/examples/cargoshipping/domainmodel.py +0 -330
- eventsourcing/examples/cargoshipping/interface.py +0 -143
- eventsourcing/examples/cargoshipping/test.py +0 -231
- eventsourcing/examples/contentmanagement/__init__.py +0 -0
- eventsourcing/examples/contentmanagement/application.py +0 -118
- eventsourcing/examples/contentmanagement/domainmodel.py +0 -69
- eventsourcing/examples/contentmanagement/test.py +0 -180
- eventsourcing/examples/contentmanagement/utils.py +0 -26
- eventsourcing/examples/contentmanagementsystem/__init__.py +0 -0
- eventsourcing/examples/contentmanagementsystem/application.py +0 -54
- eventsourcing/examples/contentmanagementsystem/postgres.py +0 -17
- eventsourcing/examples/contentmanagementsystem/sqlite.py +0 -17
- eventsourcing/examples/contentmanagementsystem/system.py +0 -14
- eventsourcing/examples/contentmanagementsystem/test_system.py +0 -180
- eventsourcing/examples/searchablecontent/__init__.py +0 -0
- eventsourcing/examples/searchablecontent/application.py +0 -45
- eventsourcing/examples/searchablecontent/persistence.py +0 -23
- eventsourcing/examples/searchablecontent/postgres.py +0 -118
- eventsourcing/examples/searchablecontent/sqlite.py +0 -136
- eventsourcing/examples/searchablecontent/test_application.py +0 -110
- eventsourcing/examples/searchablecontent/test_recorder.py +0 -68
- eventsourcing/examples/searchabletimestamps/__init__.py +0 -0
- eventsourcing/examples/searchabletimestamps/application.py +0 -32
- eventsourcing/examples/searchabletimestamps/persistence.py +0 -20
- eventsourcing/examples/searchabletimestamps/postgres.py +0 -110
- eventsourcing/examples/searchabletimestamps/sqlite.py +0 -99
- eventsourcing/examples/searchabletimestamps/test_searchabletimestamps.py +0 -94
- eventsourcing/examples/test_invoice.py +0 -176
- eventsourcing/examples/test_parking_lot.py +0 -206
- eventsourcing/tests/application_tests/__init__.py +0 -0
- eventsourcing/tests/application_tests/test_application_with_automatic_snapshotting.py +0 -55
- eventsourcing/tests/application_tests/test_application_with_popo.py +0 -22
- eventsourcing/tests/application_tests/test_application_with_postgres.py +0 -75
- eventsourcing/tests/application_tests/test_application_with_sqlite.py +0 -72
- eventsourcing/tests/application_tests/test_cache.py +0 -134
- eventsourcing/tests/application_tests/test_event_sourced_log.py +0 -162
- eventsourcing/tests/application_tests/test_notificationlog.py +0 -232
- eventsourcing/tests/application_tests/test_notificationlogreader.py +0 -126
- eventsourcing/tests/application_tests/test_processapplication.py +0 -110
- eventsourcing/tests/application_tests/test_processingpolicy.py +0 -109
- eventsourcing/tests/application_tests/test_repository.py +0 -504
- eventsourcing/tests/application_tests/test_snapshotting.py +0 -68
- eventsourcing/tests/application_tests/test_upcasting.py +0 -459
- eventsourcing/tests/docs_tests/__init__.py +0 -0
- eventsourcing/tests/docs_tests/test_docs.py +0 -293
- eventsourcing/tests/domain_tests/__init__.py +0 -0
- eventsourcing/tests/domain_tests/test_aggregate.py +0 -1200
- eventsourcing/tests/domain_tests/test_aggregate_decorators.py +0 -1604
- eventsourcing/tests/domain_tests/test_domainevent.py +0 -80
- eventsourcing/tests/interface_tests/__init__.py +0 -0
- eventsourcing/tests/interface_tests/test_remotenotificationlog.py +0 -258
- eventsourcing/tests/persistence_tests/__init__.py +0 -0
- eventsourcing/tests/persistence_tests/test_aes.py +0 -93
- eventsourcing/tests/persistence_tests/test_connection_pool.py +0 -722
- eventsourcing/tests/persistence_tests/test_eventstore.py +0 -72
- eventsourcing/tests/persistence_tests/test_infrastructure_factory.py +0 -21
- eventsourcing/tests/persistence_tests/test_mapper.py +0 -113
- eventsourcing/tests/persistence_tests/test_noninterleaving_notification_ids.py +0 -69
- eventsourcing/tests/persistence_tests/test_popo.py +0 -124
- eventsourcing/tests/persistence_tests/test_postgres.py +0 -1120
- eventsourcing/tests/persistence_tests/test_sqlite.py +0 -348
- eventsourcing/tests/persistence_tests/test_transcoder.py +0 -44
- eventsourcing/tests/system_tests/__init__.py +0 -0
- eventsourcing/tests/system_tests/test_runner.py +0 -935
- eventsourcing/tests/system_tests/test_system.py +0 -284
- eventsourcing/tests/utils_tests/__init__.py +0 -0
- eventsourcing/tests/utils_tests/test_utils.py +0 -226
- eventsourcing-9.3.3.dist-info/RECORD +0 -145
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/AUTHORS +0 -0
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/LICENSE +0 -0
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/WHEEL +0 -0
eventsourcing/application.py
CHANGED
|
@@ -599,18 +599,6 @@ class ProcessingEvent:
|
|
|
599
599
|
self.collect_events(*aggregates, **kwargs)
|
|
600
600
|
|
|
601
601
|
|
|
602
|
-
class RecordingEvent:
|
|
603
|
-
def __init__(
|
|
604
|
-
self,
|
|
605
|
-
application_name: str,
|
|
606
|
-
recordings: List[Recording],
|
|
607
|
-
previous_max_notification_id: int | None,
|
|
608
|
-
):
|
|
609
|
-
self.application_name = application_name
|
|
610
|
-
self.recordings = recordings
|
|
611
|
-
self.previous_max_notification_id = previous_max_notification_id
|
|
612
|
-
|
|
613
|
-
|
|
614
602
|
class Application:
|
|
615
603
|
"""
|
|
616
604
|
Base class for event-sourced applications.
|
|
@@ -659,9 +647,6 @@ class Application:
|
|
|
659
647
|
self._repository = self.construct_repository()
|
|
660
648
|
self._notification_log = self.construct_notification_log()
|
|
661
649
|
self.closing = Event()
|
|
662
|
-
self.previous_max_notification_id: int | None = (
|
|
663
|
-
self.recorder.max_notification_id()
|
|
664
|
-
)
|
|
665
650
|
|
|
666
651
|
@property
|
|
667
652
|
def repository(self) -> Repository:
|
eventsourcing/system.py
CHANGED
|
@@ -30,11 +30,10 @@ from eventsourcing.application import (
|
|
|
30
30
|
Application,
|
|
31
31
|
NotificationLog,
|
|
32
32
|
ProcessingEvent,
|
|
33
|
-
RecordingEvent,
|
|
34
33
|
Section,
|
|
35
34
|
TApplication,
|
|
36
35
|
)
|
|
37
|
-
from eventsourcing.domain import DomainEventProtocol
|
|
36
|
+
from eventsourcing.domain import DomainEventProtocol, MutableOrImmutableAggregate
|
|
38
37
|
from eventsourcing.persistence import (
|
|
39
38
|
IntegrityError,
|
|
40
39
|
Mapper,
|
|
@@ -46,6 +45,20 @@ from eventsourcing.persistence import (
|
|
|
46
45
|
from eventsourcing.utils import EnvType, get_topic, resolve_topic
|
|
47
46
|
|
|
48
47
|
ProcessingJob = Tuple[DomainEventProtocol, Tracking]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class RecordingEvent:
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
application_name: str,
|
|
54
|
+
recordings: List[Recording],
|
|
55
|
+
previous_max_notification_id: int | None,
|
|
56
|
+
):
|
|
57
|
+
self.application_name = application_name
|
|
58
|
+
self.recordings = recordings
|
|
59
|
+
self.previous_max_notification_id = previous_max_notification_id
|
|
60
|
+
|
|
61
|
+
|
|
49
62
|
ConvertingJob = Optional[Union[RecordingEvent, List[Notification]]]
|
|
50
63
|
|
|
51
64
|
|
|
@@ -230,6 +243,7 @@ class Leader(Application):
|
|
|
230
243
|
|
|
231
244
|
def __init__(self, env: EnvType | None = None) -> None:
|
|
232
245
|
super().__init__(env)
|
|
246
|
+
self.previous_max_notification_id: int | None = None
|
|
233
247
|
self.followers: List[RecordingEventReceiver] = []
|
|
234
248
|
|
|
235
249
|
def lead(self, follower: RecordingEventReceiver) -> None:
|
|
@@ -238,6 +252,15 @@ class Leader(Application):
|
|
|
238
252
|
"""
|
|
239
253
|
self.followers.append(follower)
|
|
240
254
|
|
|
255
|
+
def save(
|
|
256
|
+
self,
|
|
257
|
+
*objs: MutableOrImmutableAggregate | DomainEventProtocol | None,
|
|
258
|
+
**kwargs: Any,
|
|
259
|
+
) -> List[Recording]:
|
|
260
|
+
if self.previous_max_notification_id is None:
|
|
261
|
+
self.previous_max_notification_id = self.recorder.max_notification_id()
|
|
262
|
+
return super().save(*objs, **kwargs)
|
|
263
|
+
|
|
241
264
|
def _notify(self, recordings: List[Recording]) -> None:
|
|
242
265
|
"""
|
|
243
266
|
Calls :func:`receive_recording_event` on each follower
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
eventsourcing/__init__.py,sha256=st2H3shrhTk5rqoUeZHUW8XD9iOX9tGGtQFWr2HGYmo,26
|
|
2
|
+
eventsourcing/application.py,sha256=AwTpzqByZT4b6tS6HIFQkH3m9Xx2tchBN-j7jAq63wc,35868
|
|
3
|
+
eventsourcing/cipher.py,sha256=NJcVfZdSlCER6xryM4zVoY3cmKstF-iSSniB4jmpaKg,3200
|
|
4
|
+
eventsourcing/compressor.py,sha256=IdvrJUB9B2td871oifInv4lGXmHwYL9d69MbHHCr7uI,421
|
|
5
|
+
eventsourcing/dispatch.py,sha256=yYSpT-jqc6l_wTdqEnfPJJfvsZN2Ta8g2anrVPWIcqQ,1412
|
|
6
|
+
eventsourcing/domain.py,sha256=rvm4Sv2MmLcha8_5wqJ13AjmqyWvuzkquYsexUaePIg,57264
|
|
7
|
+
eventsourcing/interface.py,sha256=KzDWLeIkREf-TAFl5AFHtKJwJFA-IthqMKClFkUFqdc,4676
|
|
8
|
+
eventsourcing/persistence.py,sha256=TJseAtsWwdC33XLvcoyHdgwTv6s6KsvQS8XLKIq472s,37672
|
|
9
|
+
eventsourcing/popo.py,sha256=AApSGneHuXa8yHOWdDfsFTMVDI-9ivEpuKTX1BSOXr8,6547
|
|
10
|
+
eventsourcing/postgres.py,sha256=NatfdA-YQQPB1qWsQg23Uhyy6R-hcGNOJHJ1bKmFuKQ,29772
|
|
11
|
+
eventsourcing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
eventsourcing/sqlite.py,sha256=jz7SZk26Gcsjw88KL1xnr4-1tStTW16f1NR6TjMsZnQ,18466
|
|
13
|
+
eventsourcing/system.py,sha256=tRn-KpSxcrpJ3x8BtpD9hxYSvk8IHBIPSfTMKPLVT60,46307
|
|
14
|
+
eventsourcing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
eventsourcing/tests/application.py,sha256=wBanhWzAZvL3fYxCFe5cloN-Up7uLw6KcdRQ2dhKVAg,17586
|
|
16
|
+
eventsourcing/tests/domain.py,sha256=lHSlY6jIoSeqlcPSbrrozEPUJGvJ8bgPrznlmzTxn2w,3254
|
|
17
|
+
eventsourcing/tests/persistence.py,sha256=giLWEZDdViXY8_SHCJerJ0sYVbJUP3UkLKaEbel9sqs,45777
|
|
18
|
+
eventsourcing/tests/postgres_utils.py,sha256=xymcGYasUXeZTBenkHz-ykD8HtrFjVM1Z7-qRrH6OQk,1364
|
|
19
|
+
eventsourcing/utils.py,sha256=PIWDvoGiKCXsNbR5DirkoJ_svopg80SoH37bqxOcjkU,8247
|
|
20
|
+
eventsourcing-9.3.5.dist-info/AUTHORS,sha256=8aHOM4UbNZcKlD-cHpFRcM6RWyCqtwtxRev6DeUgVRs,137
|
|
21
|
+
eventsourcing-9.3.5.dist-info/LICENSE,sha256=bSE_F-T6cQPmMY5LuJC27km_pGB1XCVuUFx1uY0Nueg,1512
|
|
22
|
+
eventsourcing-9.3.5.dist-info/METADATA,sha256=yLNXm5rtENkOWB7kK63T2ohN1w7FuraebWFlk-Nxfho,9734
|
|
23
|
+
eventsourcing-9.3.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
24
|
+
eventsourcing-9.3.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict
|
|
4
|
-
|
|
5
|
-
from eventsourcing.application import Application
|
|
6
|
-
from eventsourcing.examples.aggregate1.domainmodel import Dog
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING: # pragma: nocover
|
|
9
|
-
from uuid import UUID
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class DogSchool(Application):
|
|
13
|
-
is_snapshotting_enabled = True
|
|
14
|
-
|
|
15
|
-
def register_dog(self, name: str) -> UUID:
|
|
16
|
-
dog = Dog(name)
|
|
17
|
-
self.save(dog)
|
|
18
|
-
return dog.id
|
|
19
|
-
|
|
20
|
-
def add_trick(self, dog_id: UUID, trick: str) -> None:
|
|
21
|
-
dog: Dog = self.repository.get(dog_id)
|
|
22
|
-
dog.add_trick(trick)
|
|
23
|
-
self.save(dog)
|
|
24
|
-
|
|
25
|
-
def get_dog(self, dog_id: UUID) -> Dict[str, Any]:
|
|
26
|
-
dog: Dog = self.repository.get(dog_id)
|
|
27
|
-
return {"name": dog.name, "tricks": tuple(dog.tricks)}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import List
|
|
4
|
-
|
|
5
|
-
from eventsourcing.domain import Aggregate, event
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Dog(Aggregate):
|
|
9
|
-
@event("Registered")
|
|
10
|
-
def __init__(self, name: str) -> None:
|
|
11
|
-
self.name = name
|
|
12
|
-
self.tricks: List[str] = []
|
|
13
|
-
|
|
14
|
-
@event("TrickAdded")
|
|
15
|
-
def add_trick(self, trick: str) -> None:
|
|
16
|
-
self.tricks.append(trick)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from unittest import TestCase
|
|
4
|
-
|
|
5
|
-
from eventsourcing.examples.aggregate1.application import DogSchool
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestDogSchool(TestCase):
|
|
9
|
-
def test_dog_school(self) -> None:
|
|
10
|
-
# Construct application object.
|
|
11
|
-
school = DogSchool()
|
|
12
|
-
|
|
13
|
-
# Evolve application state.
|
|
14
|
-
dog_id = school.register_dog("Fido")
|
|
15
|
-
school.add_trick(dog_id, "roll over")
|
|
16
|
-
school.add_trick(dog_id, "play dead")
|
|
17
|
-
|
|
18
|
-
# Query application state.
|
|
19
|
-
dog = school.get_dog(dog_id)
|
|
20
|
-
assert dog["name"] == "Fido"
|
|
21
|
-
assert dog["tricks"] == ("roll over", "play dead")
|
|
22
|
-
|
|
23
|
-
# Select notifications.
|
|
24
|
-
notifications = school.notification_log.select(start=1, limit=10)
|
|
25
|
-
assert len(notifications) == 3
|
|
26
|
-
|
|
27
|
-
# Take snapshot.
|
|
28
|
-
school.take_snapshot(dog_id, version=3)
|
|
29
|
-
dog = school.get_dog(dog_id)
|
|
30
|
-
assert dog["name"] == "Fido"
|
|
31
|
-
assert dog["tricks"] == ("roll over", "play dead")
|
|
32
|
-
|
|
33
|
-
# Continue with snapshotted aggregate.
|
|
34
|
-
school.add_trick(dog_id, "fetch ball")
|
|
35
|
-
dog = school.get_dog(dog_id)
|
|
36
|
-
assert dog["name"] == "Fido"
|
|
37
|
-
assert dog["tricks"] == ("roll over", "play dead", "fetch ball")
|
|
File without changes
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict
|
|
4
|
-
|
|
5
|
-
from eventsourcing.application import Application
|
|
6
|
-
from eventsourcing.examples.aggregate2.domainmodel import Dog
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING: # pragma: nocover
|
|
9
|
-
from uuid import UUID
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class DogSchool(Application):
|
|
13
|
-
is_snapshotting_enabled = True
|
|
14
|
-
|
|
15
|
-
def register_dog(self, name: str) -> UUID:
|
|
16
|
-
dog = Dog(name)
|
|
17
|
-
self.save(dog)
|
|
18
|
-
return dog.id
|
|
19
|
-
|
|
20
|
-
def add_trick(self, dog_id: UUID, trick: str) -> None:
|
|
21
|
-
dog: Dog = self.repository.get(dog_id)
|
|
22
|
-
dog.add_trick(trick)
|
|
23
|
-
self.save(dog)
|
|
24
|
-
|
|
25
|
-
def get_dog(self, dog_id: UUID) -> Dict[str, Any]:
|
|
26
|
-
dog: Dog = self.repository.get(dog_id)
|
|
27
|
-
return {"name": dog.name, "tricks": tuple(dog.tricks)}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import List
|
|
4
|
-
|
|
5
|
-
from eventsourcing.domain import Aggregate, event
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Dog(Aggregate):
|
|
9
|
-
class Registered(Aggregate.Created):
|
|
10
|
-
name: str
|
|
11
|
-
|
|
12
|
-
class TrickAdded(Aggregate.Event):
|
|
13
|
-
trick: str
|
|
14
|
-
|
|
15
|
-
@event(Registered)
|
|
16
|
-
def __init__(self, name: str) -> None:
|
|
17
|
-
self.name = name
|
|
18
|
-
self.tricks: List[str] = []
|
|
19
|
-
|
|
20
|
-
@event(TrickAdded)
|
|
21
|
-
def add_trick(self, trick: str) -> None:
|
|
22
|
-
self.tricks.append(trick)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from unittest import TestCase
|
|
4
|
-
|
|
5
|
-
from eventsourcing.examples.aggregate2.application import DogSchool
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestDogSchool(TestCase):
|
|
9
|
-
def test_dog_school(self) -> None:
|
|
10
|
-
# Construct application object.
|
|
11
|
-
school = DogSchool()
|
|
12
|
-
|
|
13
|
-
# Evolve application state.
|
|
14
|
-
dog_id = school.register_dog("Fido")
|
|
15
|
-
school.add_trick(dog_id, "roll over")
|
|
16
|
-
school.add_trick(dog_id, "play dead")
|
|
17
|
-
|
|
18
|
-
# Query application state.
|
|
19
|
-
dog = school.get_dog(dog_id)
|
|
20
|
-
assert dog["name"] == "Fido"
|
|
21
|
-
assert dog["tricks"] == ("roll over", "play dead")
|
|
22
|
-
|
|
23
|
-
# Select notifications.
|
|
24
|
-
notifications = school.notification_log.select(start=1, limit=10)
|
|
25
|
-
assert len(notifications) == 3
|
|
26
|
-
|
|
27
|
-
# Take snapshot.
|
|
28
|
-
school.take_snapshot(dog_id, version=3)
|
|
29
|
-
dog = school.get_dog(dog_id)
|
|
30
|
-
assert dog["name"] == "Fido"
|
|
31
|
-
assert dog["tricks"] == ("roll over", "play dead")
|
|
32
|
-
|
|
33
|
-
# Continue with snapshotted aggregate.
|
|
34
|
-
school.add_trick(dog_id, "fetch ball")
|
|
35
|
-
dog = school.get_dog(dog_id)
|
|
36
|
-
assert dog["name"] == "Fido"
|
|
37
|
-
assert dog["tricks"] == ("roll over", "play dead", "fetch ball")
|
|
File without changes
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict
|
|
4
|
-
|
|
5
|
-
from eventsourcing.application import Application
|
|
6
|
-
from eventsourcing.examples.aggregate3.domainmodel import Dog
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING: # pragma: nocover
|
|
9
|
-
from uuid import UUID
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class DogSchool(Application):
|
|
13
|
-
is_snapshotting_enabled = True
|
|
14
|
-
|
|
15
|
-
def register_dog(self, name: str) -> UUID:
|
|
16
|
-
dog = Dog.register(name=name)
|
|
17
|
-
self.save(dog)
|
|
18
|
-
return dog.id
|
|
19
|
-
|
|
20
|
-
def add_trick(self, dog_id: UUID, trick: str) -> None:
|
|
21
|
-
dog: Dog = self.repository.get(dog_id)
|
|
22
|
-
dog.add_trick(trick)
|
|
23
|
-
self.save(dog)
|
|
24
|
-
|
|
25
|
-
def get_dog(self, dog_id: UUID) -> Dict[str, Any]:
|
|
26
|
-
dog: Dog = self.repository.get(dog_id)
|
|
27
|
-
return {"name": dog.name, "tricks": tuple(dog.tricks)}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import List, cast
|
|
4
|
-
|
|
5
|
-
from eventsourcing.dispatch import singledispatchmethod
|
|
6
|
-
from eventsourcing.domain import Aggregate
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Dog(Aggregate):
|
|
10
|
-
class Event(Aggregate.Event):
|
|
11
|
-
def apply(self, aggregate: Aggregate) -> None:
|
|
12
|
-
cast(Dog, aggregate).apply(self)
|
|
13
|
-
|
|
14
|
-
class Registered(Event, Aggregate.Created):
|
|
15
|
-
name: str
|
|
16
|
-
|
|
17
|
-
class TrickAdded(Event):
|
|
18
|
-
trick: str
|
|
19
|
-
|
|
20
|
-
@classmethod
|
|
21
|
-
def register(cls, name: str) -> Dog:
|
|
22
|
-
return cls._create(cls.Registered, name=name)
|
|
23
|
-
|
|
24
|
-
def add_trick(self, trick: str) -> None:
|
|
25
|
-
self.trigger_event(self.TrickAdded, trick=trick)
|
|
26
|
-
|
|
27
|
-
@singledispatchmethod
|
|
28
|
-
def apply(self, event: Event) -> None:
|
|
29
|
-
"""Applies event to aggregate."""
|
|
30
|
-
|
|
31
|
-
@apply.register
|
|
32
|
-
def _(self, event: Dog.Registered) -> None:
|
|
33
|
-
self.name = event.name
|
|
34
|
-
self.tricks: List[str] = []
|
|
35
|
-
|
|
36
|
-
@apply.register
|
|
37
|
-
def _(self, event: Dog.TrickAdded) -> None:
|
|
38
|
-
self.tricks.append(event.trick)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from unittest import TestCase
|
|
4
|
-
|
|
5
|
-
from eventsourcing.examples.aggregate3.application import DogSchool
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestDogSchool(TestCase):
|
|
9
|
-
def test_dog_school(self) -> None:
|
|
10
|
-
# Construct application object.
|
|
11
|
-
school = DogSchool()
|
|
12
|
-
|
|
13
|
-
# Evolve application state.
|
|
14
|
-
dog_id = school.register_dog("Fido")
|
|
15
|
-
school.add_trick(dog_id, "roll over")
|
|
16
|
-
school.add_trick(dog_id, "play dead")
|
|
17
|
-
|
|
18
|
-
# Query application state.
|
|
19
|
-
dog = school.get_dog(dog_id)
|
|
20
|
-
assert dog["name"] == "Fido"
|
|
21
|
-
assert dog["tricks"] == ("roll over", "play dead")
|
|
22
|
-
|
|
23
|
-
# Select notifications.
|
|
24
|
-
notifications = school.notification_log.select(start=1, limit=10)
|
|
25
|
-
assert len(notifications) == 3
|
|
26
|
-
|
|
27
|
-
# Take snapshot.
|
|
28
|
-
school.take_snapshot(dog_id, version=3)
|
|
29
|
-
dog = school.get_dog(dog_id)
|
|
30
|
-
assert dog["name"] == "Fido"
|
|
31
|
-
assert dog["tricks"] == ("roll over", "play dead")
|
|
32
|
-
|
|
33
|
-
# Continue with snapshotted aggregate.
|
|
34
|
-
school.add_trick(dog_id, "fetch ball")
|
|
35
|
-
dog = school.get_dog(dog_id)
|
|
36
|
-
assert dog["name"] == "Fido"
|
|
37
|
-
assert dog["tricks"] == ("roll over", "play dead", "fetch ball")
|
|
File without changes
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict
|
|
4
|
-
|
|
5
|
-
from eventsourcing.application import Application
|
|
6
|
-
from eventsourcing.examples.aggregate4.domainmodel import Dog
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING: # pragma: nocover
|
|
9
|
-
from uuid import UUID
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class DogSchool(Application):
|
|
13
|
-
is_snapshotting_enabled = True
|
|
14
|
-
|
|
15
|
-
def register_dog(self, name: str) -> UUID:
|
|
16
|
-
dog = Dog.register(name)
|
|
17
|
-
self.save(dog)
|
|
18
|
-
return dog.id
|
|
19
|
-
|
|
20
|
-
def add_trick(self, dog_id: UUID, trick: str) -> None:
|
|
21
|
-
dog = self.repository.get(dog_id, projector_func=Dog.projector)
|
|
22
|
-
dog.add_trick(trick)
|
|
23
|
-
self.save(dog)
|
|
24
|
-
|
|
25
|
-
def get_dog(self, dog_id: UUID) -> Dict[str, Any]:
|
|
26
|
-
dog = self.repository.get(dog_id, projector_func=Dog.projector)
|
|
27
|
-
return {"name": dog.name, "tricks": tuple(dog.tricks)}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from datetime import datetime, timezone
|
|
5
|
-
from typing import Any, Iterable, List, Type, TypeVar, cast
|
|
6
|
-
from uuid import UUID, uuid4
|
|
7
|
-
|
|
8
|
-
from eventsourcing.dispatch import singledispatchmethod
|
|
9
|
-
from eventsourcing.domain import Snapshot
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass(frozen=True)
|
|
13
|
-
class DomainEvent:
|
|
14
|
-
originator_version: int
|
|
15
|
-
originator_id: UUID
|
|
16
|
-
timestamp: datetime
|
|
17
|
-
|
|
18
|
-
@staticmethod
|
|
19
|
-
def create_timestamp() -> datetime:
|
|
20
|
-
return datetime.now(tz=timezone.utc)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
TAggregate = TypeVar("TAggregate", bound="Aggregate")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class Aggregate:
|
|
27
|
-
id: UUID
|
|
28
|
-
version: int
|
|
29
|
-
created_on: datetime
|
|
30
|
-
_pending_events: List[DomainEvent]
|
|
31
|
-
|
|
32
|
-
def __init__(self, event: DomainEvent):
|
|
33
|
-
self.id = event.originator_id
|
|
34
|
-
self.version = event.originator_version
|
|
35
|
-
self.created_on = event.timestamp
|
|
36
|
-
|
|
37
|
-
def trigger_event(
|
|
38
|
-
self,
|
|
39
|
-
event_class: Type[DomainEvent],
|
|
40
|
-
**kwargs: Any,
|
|
41
|
-
) -> None:
|
|
42
|
-
kwargs = kwargs.copy()
|
|
43
|
-
kwargs.update(
|
|
44
|
-
originator_id=self.id,
|
|
45
|
-
originator_version=self.version + 1,
|
|
46
|
-
timestamp=event_class.create_timestamp(),
|
|
47
|
-
)
|
|
48
|
-
new_event = event_class(**kwargs)
|
|
49
|
-
self._apply(new_event)
|
|
50
|
-
self._pending_events.append(new_event)
|
|
51
|
-
|
|
52
|
-
@singledispatchmethod
|
|
53
|
-
def _apply(self, event: DomainEvent) -> None:
|
|
54
|
-
"""Applies event to aggregate."""
|
|
55
|
-
|
|
56
|
-
def collect_events(self) -> List[DomainEvent]:
|
|
57
|
-
events, self._pending_events = self._pending_events, []
|
|
58
|
-
return events
|
|
59
|
-
|
|
60
|
-
@classmethod
|
|
61
|
-
def projector(
|
|
62
|
-
cls: Type[TAggregate],
|
|
63
|
-
_: TAggregate | None,
|
|
64
|
-
events: Iterable[DomainEvent],
|
|
65
|
-
) -> TAggregate | None:
|
|
66
|
-
aggregate: TAggregate = object.__new__(cls)
|
|
67
|
-
aggregate._pending_events = []
|
|
68
|
-
for event in events:
|
|
69
|
-
aggregate._apply(event)
|
|
70
|
-
return aggregate
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
class Dog(Aggregate):
|
|
74
|
-
@dataclass(frozen=True)
|
|
75
|
-
class Registered(DomainEvent):
|
|
76
|
-
name: str
|
|
77
|
-
|
|
78
|
-
@dataclass(frozen=True)
|
|
79
|
-
class TrickAdded(DomainEvent):
|
|
80
|
-
trick: str
|
|
81
|
-
|
|
82
|
-
@classmethod
|
|
83
|
-
def register(cls, name: str) -> Dog:
|
|
84
|
-
event = cls.Registered(
|
|
85
|
-
originator_id=uuid4(),
|
|
86
|
-
originator_version=1,
|
|
87
|
-
timestamp=DomainEvent.create_timestamp(),
|
|
88
|
-
name=name,
|
|
89
|
-
)
|
|
90
|
-
dog = cast(Dog, cls.projector(None, [event]))
|
|
91
|
-
dog._pending_events.append(event)
|
|
92
|
-
return dog
|
|
93
|
-
|
|
94
|
-
def add_trick(self, trick: str) -> None:
|
|
95
|
-
self.trigger_event(self.TrickAdded, trick=trick)
|
|
96
|
-
|
|
97
|
-
@singledispatchmethod
|
|
98
|
-
def _apply(self, event: DomainEvent) -> None:
|
|
99
|
-
"""Applies event to aggregate."""
|
|
100
|
-
|
|
101
|
-
@_apply.register(Registered)
|
|
102
|
-
def _(self, event: Registered) -> None:
|
|
103
|
-
super().__init__(event)
|
|
104
|
-
self.name = event.name
|
|
105
|
-
self.tricks: List[str] = []
|
|
106
|
-
|
|
107
|
-
@_apply.register(TrickAdded)
|
|
108
|
-
def _(self, event: TrickAdded) -> None:
|
|
109
|
-
self.tricks.append(event.trick)
|
|
110
|
-
self.version = event.originator_version
|
|
111
|
-
|
|
112
|
-
@_apply.register(Snapshot)
|
|
113
|
-
def _(self, event: Snapshot) -> None:
|
|
114
|
-
self.__dict__.update(event.state)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from unittest import TestCase
|
|
4
|
-
|
|
5
|
-
from eventsourcing.examples.aggregate4.application import DogSchool
|
|
6
|
-
from eventsourcing.examples.aggregate4.domainmodel import Dog
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TestDogSchool(TestCase):
|
|
10
|
-
def test_dog_school(self) -> None:
|
|
11
|
-
# Construct application object.
|
|
12
|
-
school = DogSchool()
|
|
13
|
-
|
|
14
|
-
# Evolve application state.
|
|
15
|
-
dog_id = school.register_dog("Fido")
|
|
16
|
-
school.add_trick(dog_id, "roll over")
|
|
17
|
-
school.add_trick(dog_id, "play dead")
|
|
18
|
-
|
|
19
|
-
# Query application state.
|
|
20
|
-
dog = school.get_dog(dog_id)
|
|
21
|
-
assert dog["name"] == "Fido"
|
|
22
|
-
assert dog["tricks"] == ("roll over", "play dead")
|
|
23
|
-
|
|
24
|
-
# Select notifications.
|
|
25
|
-
notifications = school.notification_log.select(start=1, limit=10)
|
|
26
|
-
assert len(notifications) == 3
|
|
27
|
-
|
|
28
|
-
# Take snapshot.
|
|
29
|
-
school.take_snapshot(dog_id, version=3, projector_func=Dog.projector)
|
|
30
|
-
dog = school.get_dog(dog_id)
|
|
31
|
-
assert dog["name"] == "Fido"
|
|
32
|
-
assert dog["tricks"] == ("roll over", "play dead")
|
|
33
|
-
|
|
34
|
-
# Continue with snapshotted aggregate.
|
|
35
|
-
school.add_trick(dog_id, "fetch ball")
|
|
36
|
-
dog = school.get_dog(dog_id)
|
|
37
|
-
assert dog["name"] == "Fido"
|
|
38
|
-
assert dog["tricks"] == ("roll over", "play dead", "fetch ball")
|
|
File without changes
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict
|
|
4
|
-
|
|
5
|
-
from eventsourcing.application import Application
|
|
6
|
-
from eventsourcing.examples.aggregate5.domainmodel import Dog
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING: # pragma: nocover
|
|
9
|
-
from uuid import UUID
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class DogSchool(Application):
|
|
13
|
-
is_snapshotting_enabled = True
|
|
14
|
-
|
|
15
|
-
def register_dog(self, name: str) -> UUID:
|
|
16
|
-
dog, event = Dog.register(name)
|
|
17
|
-
self.save(event)
|
|
18
|
-
return dog.id
|
|
19
|
-
|
|
20
|
-
def add_trick(self, dog_id: UUID, trick: str) -> None:
|
|
21
|
-
dog = self.repository.get(dog_id, projector_func=Dog.projector)
|
|
22
|
-
dog, event = dog.add_trick(trick)
|
|
23
|
-
self.save(event)
|
|
24
|
-
|
|
25
|
-
def get_dog(self, dog_id: UUID) -> Dict[str, Any]:
|
|
26
|
-
dog = self.repository.get(dog_id, projector_func=Dog.projector)
|
|
27
|
-
return {"name": dog.name, "tricks": dog.tricks}
|