eventsourcing 9.3.3__py3-none-any.whl → 9.3.4__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-9.3.3.dist-info → eventsourcing-9.3.4.dist-info}/METADATA +1 -1
- eventsourcing-9.3.4.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.4.dist-info}/AUTHORS +0 -0
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.4.dist-info}/LICENSE +0 -0
- {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.4.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
eventsourcing/__init__.py,sha256=st2H3shrhTk5rqoUeZHUW8XD9iOX9tGGtQFWr2HGYmo,26
|
|
2
|
+
eventsourcing/application.py,sha256=c5aTi86mBOiQicmqEUsqseAeL7CwDie3G9e0Y8kWLYM,36324
|
|
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=il05rAzPtKaxragIktMBOEyWGhz16VK0F7cEBlsl4_Q,45561
|
|
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.4.dist-info/AUTHORS,sha256=8aHOM4UbNZcKlD-cHpFRcM6RWyCqtwtxRev6DeUgVRs,137
|
|
21
|
+
eventsourcing-9.3.4.dist-info/LICENSE,sha256=bSE_F-T6cQPmMY5LuJC27km_pGB1XCVuUFx1uY0Nueg,1512
|
|
22
|
+
eventsourcing-9.3.4.dist-info/METADATA,sha256=zGQTtZA_1u5kZOUW7wy5a86JHQoVU4j5tJR1OaIaLFo,9734
|
|
23
|
+
eventsourcing-9.3.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
24
|
+
eventsourcing-9.3.4.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}
|
|
@@ -1,131 +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, Tuple, Type, TypeVar
|
|
6
|
-
from uuid import UUID, uuid4
|
|
7
|
-
|
|
8
|
-
from eventsourcing.dispatch import singledispatchmethod
|
|
9
|
-
from eventsourcing.domain import Snapshot # noqa: TCH001
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass(frozen=True)
|
|
13
|
-
class DomainEvent:
|
|
14
|
-
originator_id: UUID
|
|
15
|
-
originator_version: int
|
|
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
|
-
@dataclass(frozen=True)
|
|
27
|
-
class Aggregate:
|
|
28
|
-
id: UUID
|
|
29
|
-
version: int
|
|
30
|
-
created_on: datetime
|
|
31
|
-
modified_on: datetime
|
|
32
|
-
|
|
33
|
-
def trigger_event(
|
|
34
|
-
self,
|
|
35
|
-
event_class: Type[DomainEvent],
|
|
36
|
-
**kwargs: Any,
|
|
37
|
-
) -> DomainEvent:
|
|
38
|
-
kwargs = kwargs.copy()
|
|
39
|
-
kwargs.update(
|
|
40
|
-
originator_id=self.id,
|
|
41
|
-
originator_version=self.version + 1,
|
|
42
|
-
timestamp=event_class.create_timestamp(),
|
|
43
|
-
)
|
|
44
|
-
return event_class(**kwargs)
|
|
45
|
-
|
|
46
|
-
@classmethod
|
|
47
|
-
def projector(
|
|
48
|
-
cls: Type[TAggregate],
|
|
49
|
-
aggregate: TAggregate | None,
|
|
50
|
-
events: Iterable[DomainEvent],
|
|
51
|
-
) -> TAggregate | None:
|
|
52
|
-
for event in events:
|
|
53
|
-
aggregate = cls.mutate(event, aggregate)
|
|
54
|
-
return aggregate
|
|
55
|
-
|
|
56
|
-
@singledispatchmethod
|
|
57
|
-
@staticmethod
|
|
58
|
-
def mutate(event: DomainEvent, aggregate: Any) -> Any:
|
|
59
|
-
"""Mutates aggregate with event."""
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@dataclass(frozen=True)
|
|
63
|
-
class Dog(Aggregate):
|
|
64
|
-
name: str
|
|
65
|
-
tricks: Tuple[str, ...]
|
|
66
|
-
|
|
67
|
-
@dataclass(frozen=True)
|
|
68
|
-
class Registered(DomainEvent):
|
|
69
|
-
name: str
|
|
70
|
-
|
|
71
|
-
@dataclass(frozen=True)
|
|
72
|
-
class TrickAdded(DomainEvent):
|
|
73
|
-
trick: str
|
|
74
|
-
|
|
75
|
-
@staticmethod
|
|
76
|
-
def register(name: str) -> Tuple[Dog, DomainEvent]:
|
|
77
|
-
event = Dog.Registered(
|
|
78
|
-
originator_id=uuid4(),
|
|
79
|
-
originator_version=1,
|
|
80
|
-
timestamp=DomainEvent.create_timestamp(),
|
|
81
|
-
name=name,
|
|
82
|
-
)
|
|
83
|
-
dog = Dog.mutate(event, None)
|
|
84
|
-
return dog, event
|
|
85
|
-
|
|
86
|
-
def add_trick(self, trick: str) -> Tuple[Dog, DomainEvent]:
|
|
87
|
-
event = self.trigger_event(Dog.TrickAdded, trick=trick)
|
|
88
|
-
dog = Dog.mutate(event, self)
|
|
89
|
-
return dog, event
|
|
90
|
-
|
|
91
|
-
@singledispatchmethod
|
|
92
|
-
@classmethod
|
|
93
|
-
def mutate(cls, event: DomainEvent, aggregate: Dog | None) -> Dog | None:
|
|
94
|
-
"""Mutates aggregate with event."""
|
|
95
|
-
|
|
96
|
-
@mutate.register
|
|
97
|
-
@classmethod
|
|
98
|
-
def _(cls, event: Dog.Registered, _: Dog | None) -> Dog:
|
|
99
|
-
return Dog(
|
|
100
|
-
id=event.originator_id,
|
|
101
|
-
version=event.originator_version,
|
|
102
|
-
created_on=event.timestamp,
|
|
103
|
-
modified_on=event.timestamp,
|
|
104
|
-
name=event.name,
|
|
105
|
-
tricks=(),
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
@mutate.register
|
|
109
|
-
@classmethod
|
|
110
|
-
def _(cls, event: Dog.TrickAdded, aggregate: Dog | None) -> Dog:
|
|
111
|
-
assert aggregate is not None
|
|
112
|
-
return Dog(
|
|
113
|
-
id=aggregate.id,
|
|
114
|
-
version=event.originator_version,
|
|
115
|
-
created_on=aggregate.created_on,
|
|
116
|
-
modified_on=event.timestamp,
|
|
117
|
-
name=aggregate.name,
|
|
118
|
-
tricks=(*aggregate.tricks, event.trick),
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
@mutate.register
|
|
122
|
-
@classmethod
|
|
123
|
-
def _(cls, event: Snapshot, _: Dog | None) -> Dog:
|
|
124
|
-
return Dog(
|
|
125
|
-
id=event.state["id"],
|
|
126
|
-
version=event.state["version"],
|
|
127
|
-
created_on=event.state["created_on"],
|
|
128
|
-
modified_on=event.state["modified_on"],
|
|
129
|
-
name=event.state["name"],
|
|
130
|
-
tricks=tuple(event.state["tricks"]), # comes back from JSON as a list
|
|
131
|
-
)
|