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.

Files changed (129) hide show
  1. eventsourcing/application.py +0 -15
  2. eventsourcing/system.py +25 -2
  3. {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/METADATA +1 -1
  4. eventsourcing-9.3.5.dist-info/RECORD +24 -0
  5. eventsourcing/examples/__init__.py +0 -0
  6. eventsourcing/examples/aggregate1/__init__.py +0 -0
  7. eventsourcing/examples/aggregate1/application.py +0 -27
  8. eventsourcing/examples/aggregate1/domainmodel.py +0 -16
  9. eventsourcing/examples/aggregate1/test_application.py +0 -37
  10. eventsourcing/examples/aggregate2/__init__.py +0 -0
  11. eventsourcing/examples/aggregate2/application.py +0 -27
  12. eventsourcing/examples/aggregate2/domainmodel.py +0 -22
  13. eventsourcing/examples/aggregate2/test_application.py +0 -37
  14. eventsourcing/examples/aggregate3/__init__.py +0 -0
  15. eventsourcing/examples/aggregate3/application.py +0 -27
  16. eventsourcing/examples/aggregate3/domainmodel.py +0 -38
  17. eventsourcing/examples/aggregate3/test_application.py +0 -37
  18. eventsourcing/examples/aggregate4/__init__.py +0 -0
  19. eventsourcing/examples/aggregate4/application.py +0 -27
  20. eventsourcing/examples/aggregate4/domainmodel.py +0 -114
  21. eventsourcing/examples/aggregate4/test_application.py +0 -38
  22. eventsourcing/examples/aggregate5/__init__.py +0 -0
  23. eventsourcing/examples/aggregate5/application.py +0 -27
  24. eventsourcing/examples/aggregate5/domainmodel.py +0 -131
  25. eventsourcing/examples/aggregate5/test_application.py +0 -38
  26. eventsourcing/examples/aggregate6/__init__.py +0 -0
  27. eventsourcing/examples/aggregate6/application.py +0 -30
  28. eventsourcing/examples/aggregate6/domainmodel.py +0 -123
  29. eventsourcing/examples/aggregate6/test_application.py +0 -38
  30. eventsourcing/examples/aggregate6a/__init__.py +0 -0
  31. eventsourcing/examples/aggregate6a/application.py +0 -40
  32. eventsourcing/examples/aggregate6a/domainmodel.py +0 -149
  33. eventsourcing/examples/aggregate6a/test_application.py +0 -45
  34. eventsourcing/examples/aggregate7/__init__.py +0 -0
  35. eventsourcing/examples/aggregate7/application.py +0 -53
  36. eventsourcing/examples/aggregate7/domainmodel.py +0 -142
  37. eventsourcing/examples/aggregate7/persistence.py +0 -57
  38. eventsourcing/examples/aggregate7/test_application.py +0 -45
  39. eventsourcing/examples/aggregate7/test_compression_and_encryption.py +0 -45
  40. eventsourcing/examples/aggregate7/test_snapshotting_intervals.py +0 -67
  41. eventsourcing/examples/aggregate7a/__init__.py +0 -0
  42. eventsourcing/examples/aggregate7a/application.py +0 -56
  43. eventsourcing/examples/aggregate7a/domainmodel.py +0 -168
  44. eventsourcing/examples/aggregate7a/test_application.py +0 -46
  45. eventsourcing/examples/aggregate7a/test_compression_and_encryption.py +0 -45
  46. eventsourcing/examples/aggregate8/__init__.py +0 -0
  47. eventsourcing/examples/aggregate8/application.py +0 -47
  48. eventsourcing/examples/aggregate8/domainmodel.py +0 -71
  49. eventsourcing/examples/aggregate8/persistence.py +0 -57
  50. eventsourcing/examples/aggregate8/test_application.py +0 -44
  51. eventsourcing/examples/aggregate8/test_compression_and_encryption.py +0 -44
  52. eventsourcing/examples/aggregate8/test_snapshotting_intervals.py +0 -38
  53. eventsourcing/examples/bankaccounts/__init__.py +0 -0
  54. eventsourcing/examples/bankaccounts/application.py +0 -70
  55. eventsourcing/examples/bankaccounts/domainmodel.py +0 -56
  56. eventsourcing/examples/bankaccounts/test.py +0 -173
  57. eventsourcing/examples/cargoshipping/__init__.py +0 -0
  58. eventsourcing/examples/cargoshipping/application.py +0 -126
  59. eventsourcing/examples/cargoshipping/domainmodel.py +0 -330
  60. eventsourcing/examples/cargoshipping/interface.py +0 -143
  61. eventsourcing/examples/cargoshipping/test.py +0 -231
  62. eventsourcing/examples/contentmanagement/__init__.py +0 -0
  63. eventsourcing/examples/contentmanagement/application.py +0 -118
  64. eventsourcing/examples/contentmanagement/domainmodel.py +0 -69
  65. eventsourcing/examples/contentmanagement/test.py +0 -180
  66. eventsourcing/examples/contentmanagement/utils.py +0 -26
  67. eventsourcing/examples/contentmanagementsystem/__init__.py +0 -0
  68. eventsourcing/examples/contentmanagementsystem/application.py +0 -54
  69. eventsourcing/examples/contentmanagementsystem/postgres.py +0 -17
  70. eventsourcing/examples/contentmanagementsystem/sqlite.py +0 -17
  71. eventsourcing/examples/contentmanagementsystem/system.py +0 -14
  72. eventsourcing/examples/contentmanagementsystem/test_system.py +0 -180
  73. eventsourcing/examples/searchablecontent/__init__.py +0 -0
  74. eventsourcing/examples/searchablecontent/application.py +0 -45
  75. eventsourcing/examples/searchablecontent/persistence.py +0 -23
  76. eventsourcing/examples/searchablecontent/postgres.py +0 -118
  77. eventsourcing/examples/searchablecontent/sqlite.py +0 -136
  78. eventsourcing/examples/searchablecontent/test_application.py +0 -110
  79. eventsourcing/examples/searchablecontent/test_recorder.py +0 -68
  80. eventsourcing/examples/searchabletimestamps/__init__.py +0 -0
  81. eventsourcing/examples/searchabletimestamps/application.py +0 -32
  82. eventsourcing/examples/searchabletimestamps/persistence.py +0 -20
  83. eventsourcing/examples/searchabletimestamps/postgres.py +0 -110
  84. eventsourcing/examples/searchabletimestamps/sqlite.py +0 -99
  85. eventsourcing/examples/searchabletimestamps/test_searchabletimestamps.py +0 -94
  86. eventsourcing/examples/test_invoice.py +0 -176
  87. eventsourcing/examples/test_parking_lot.py +0 -206
  88. eventsourcing/tests/application_tests/__init__.py +0 -0
  89. eventsourcing/tests/application_tests/test_application_with_automatic_snapshotting.py +0 -55
  90. eventsourcing/tests/application_tests/test_application_with_popo.py +0 -22
  91. eventsourcing/tests/application_tests/test_application_with_postgres.py +0 -75
  92. eventsourcing/tests/application_tests/test_application_with_sqlite.py +0 -72
  93. eventsourcing/tests/application_tests/test_cache.py +0 -134
  94. eventsourcing/tests/application_tests/test_event_sourced_log.py +0 -162
  95. eventsourcing/tests/application_tests/test_notificationlog.py +0 -232
  96. eventsourcing/tests/application_tests/test_notificationlogreader.py +0 -126
  97. eventsourcing/tests/application_tests/test_processapplication.py +0 -110
  98. eventsourcing/tests/application_tests/test_processingpolicy.py +0 -109
  99. eventsourcing/tests/application_tests/test_repository.py +0 -504
  100. eventsourcing/tests/application_tests/test_snapshotting.py +0 -68
  101. eventsourcing/tests/application_tests/test_upcasting.py +0 -459
  102. eventsourcing/tests/docs_tests/__init__.py +0 -0
  103. eventsourcing/tests/docs_tests/test_docs.py +0 -293
  104. eventsourcing/tests/domain_tests/__init__.py +0 -0
  105. eventsourcing/tests/domain_tests/test_aggregate.py +0 -1200
  106. eventsourcing/tests/domain_tests/test_aggregate_decorators.py +0 -1604
  107. eventsourcing/tests/domain_tests/test_domainevent.py +0 -80
  108. eventsourcing/tests/interface_tests/__init__.py +0 -0
  109. eventsourcing/tests/interface_tests/test_remotenotificationlog.py +0 -258
  110. eventsourcing/tests/persistence_tests/__init__.py +0 -0
  111. eventsourcing/tests/persistence_tests/test_aes.py +0 -93
  112. eventsourcing/tests/persistence_tests/test_connection_pool.py +0 -722
  113. eventsourcing/tests/persistence_tests/test_eventstore.py +0 -72
  114. eventsourcing/tests/persistence_tests/test_infrastructure_factory.py +0 -21
  115. eventsourcing/tests/persistence_tests/test_mapper.py +0 -113
  116. eventsourcing/tests/persistence_tests/test_noninterleaving_notification_ids.py +0 -69
  117. eventsourcing/tests/persistence_tests/test_popo.py +0 -124
  118. eventsourcing/tests/persistence_tests/test_postgres.py +0 -1120
  119. eventsourcing/tests/persistence_tests/test_sqlite.py +0 -348
  120. eventsourcing/tests/persistence_tests/test_transcoder.py +0 -44
  121. eventsourcing/tests/system_tests/__init__.py +0 -0
  122. eventsourcing/tests/system_tests/test_runner.py +0 -935
  123. eventsourcing/tests/system_tests/test_system.py +0 -284
  124. eventsourcing/tests/utils_tests/__init__.py +0 -0
  125. eventsourcing/tests/utils_tests/test_utils.py +0 -226
  126. eventsourcing-9.3.3.dist-info/RECORD +0 -145
  127. {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/AUTHORS +0 -0
  128. {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/LICENSE +0 -0
  129. {eventsourcing-9.3.3.dist-info → eventsourcing-9.3.5.dist-info}/WHEEL +0 -0
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: eventsourcing
3
- Version: 9.3.3
3
+ Version: 9.3.5
4
4
  Summary: Event sourcing in Python
5
5
  Home-page: https://github.com/pyeventsourcing/eventsourcing
6
6
  License: BSD 3-Clause
@@ -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}