python-event-sourcery 0.6.2a1__tar.gz → 0.7.0__tar.gz

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.
Files changed (308) hide show
  1. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/AGENTS.md +1 -1
  2. python_event_sourcery-0.7.0/CHANGELOG.md +73 -0
  3. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/PKG-INFO +4 -2
  4. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/README.md +1 -1
  5. python_event_sourcery-0.7.0/docs/adr/20260827-async-api.md +133 -0
  6. python_event_sourcery-0.7.0/docs/code/recipes/integrate/kurrent_async_01.py +15 -0
  7. python_event_sourcery-0.7.0/docs/code/test_get_started.py +67 -0
  8. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/code/test_recipes.py +336 -4
  9. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/get_started.md +32 -10
  10. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/recipes/data_privacy.md +35 -17
  11. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/recipes/event_sourcing.md +30 -12
  12. python_event_sourcery-0.7.0/docs/recipes/integrate.md +98 -0
  13. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/recipes/multitenancy.md +17 -7
  14. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/recipes/outbox.md +58 -24
  15. python_event_sourcery-0.7.0/docs/recipes/saving_events.md +129 -0
  16. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/recipes/snapshots.md +41 -16
  17. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/recipes/subscriptions.md +58 -26
  18. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/backends/in_memory.md +3 -0
  19. python_event_sourcery-0.7.0/docs/reference/backends/kurrentdb.md +17 -0
  20. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/backends/sqlalchemy.md +15 -0
  21. python_event_sourcery-0.7.0/docs/reference/event_sourcing/AsyncRepository.md +2 -0
  22. python_event_sourcery-0.7.0/docs/reference/event_store/AsyncEventStore.md +1 -0
  23. python_event_sourcery-0.7.0/docs/reference/event_store/backend/AsyncBackend.md +1 -0
  24. python_event_sourcery-0.7.0/docs/reference/event_store/backend/AsyncTransactionalBackend.md +1 -0
  25. python_event_sourcery-0.7.0/docs/reference/event_store/encryption/AsyncEncryption.md +2 -0
  26. python_event_sourcery-0.7.0/docs/reference/event_store/encryption/AsyncNoKeyStorageStrategy.md +2 -0
  27. python_event_sourcery-0.7.0/docs/reference/event_store/interfaces/AsyncEncryptionKeyStorageStrategy.md +2 -0
  28. python_event_sourcery-0.7.0/docs/reference/event_store/interfaces/AsyncOutboxStorageStrategy.md +1 -0
  29. python_event_sourcery-0.7.0/docs/reference/event_store/interfaces/AsyncStorageStrategy.md +1 -0
  30. python_event_sourcery-0.7.0/docs/reference/event_store/interfaces/AsyncSubscriptionStrategy.md +1 -0
  31. python_event_sourcery-0.7.0/docs/reference/event_store/outbox/AsyncNoOutboxStorageStrategy.md +1 -0
  32. python_event_sourcery-0.7.0/docs/reference/event_store/outbox/AsyncOutbox.md +1 -0
  33. python_event_sourcery-0.7.0/docs/reference/event_store/subscription/AsyncBuildPhase.md +1 -0
  34. python_event_sourcery-0.7.0/docs/reference/event_store/subscription/AsyncFilterPhase.md +1 -0
  35. python_event_sourcery-0.7.0/docs/reference/event_store/subscription/AsyncPositionPhase.md +1 -0
  36. python_event_sourcery-0.7.0/docs/reference/event_store/subscription/AsyncSubscriptionBuilder.md +1 -0
  37. python_event_sourcery-0.7.0/docs/reference/read_model/AsyncCursorsDao.md +2 -0
  38. python_event_sourcery-0.7.0/docs/reference/read_model/AsyncProjector.md +2 -0
  39. python_event_sourcery-0.7.0/docs/reference/read_model/AsyncReadModel.md +2 -0
  40. python_event_sourcery-0.7.0/docs/reference/read_model/CursorsDao.md +2 -0
  41. python_event_sourcery-0.7.0/docs/reference/read_model/Projector.md +2 -0
  42. python_event_sourcery-0.7.0/docs/reference/read_model/ReadModel.md +2 -0
  43. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/__init__.py +10 -0
  44. python_event_sourcery-0.7.0/event_sourcery/_event_store/_async/backend.py +154 -0
  45. python_event_sourcery-0.7.0/event_sourcery/_event_store/_async/dispatcher.py +39 -0
  46. python_event_sourcery-0.7.0/event_sourcery/_event_store/_async/encryption.py +222 -0
  47. python_event_sourcery-0.7.0/event_sourcery/_event_store/_async/event_store.py +268 -0
  48. python_event_sourcery-0.7.0/event_sourcery/_event_store/_async/in_memory.py +347 -0
  49. python_event_sourcery-0.7.0/event_sourcery/_event_store/_async/outbox.py +99 -0
  50. python_event_sourcery-0.7.0/event_sourcery/_event_store/_async/serde.py +70 -0
  51. python_event_sourcery-0.7.0/event_sourcery/_event_store/_async/subscription.py +202 -0
  52. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/backend.py +28 -23
  53. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/event/encryption.py +56 -14
  54. python_event_sourcery-0.7.0/event_sourcery/_event_store/event/serde.py +116 -0
  55. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/event_store.py +23 -16
  56. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/in_memory.py +56 -33
  57. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/subscription/builder.py +3 -16
  58. python_event_sourcery-0.7.0/event_sourcery/_event_store/subscription/helpers.py +15 -0
  59. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/subscription/in_transaction.py +29 -4
  60. python_event_sourcery-0.7.0/event_sourcery/async_/__init__.py +13 -0
  61. python_event_sourcery-0.7.0/event_sourcery/async_/backend.py +20 -0
  62. python_event_sourcery-0.7.0/event_sourcery/async_/encryption.py +9 -0
  63. python_event_sourcery-0.7.0/event_sourcery/async_/interfaces.py +13 -0
  64. python_event_sourcery-0.7.0/event_sourcery/async_/outbox.py +11 -0
  65. python_event_sourcery-0.7.0/event_sourcery/async_/subscription.py +13 -0
  66. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/encryption.py +4 -0
  67. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/event_sourcing/__init__.py +2 -0
  68. python_event_sourcery-0.7.0/event_sourcery/event_sourcing/repository_async.py +86 -0
  69. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/read_model/__init__.py +8 -0
  70. python_event_sourcery-0.7.0/event_sourcery/read_model/projector_async.py +94 -0
  71. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_django/dto.py +2 -0
  72. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_django/event_store.py +13 -1
  73. python_event_sourcery-0.7.0/event_sourcery_django/migrations/0002_remove_event_ix_events_stream_id_version_and_more.py +67 -0
  74. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_django/models.py +1 -5
  75. python_event_sourcery-0.7.0/event_sourcery_kurrentdb/async_/__init__.py +96 -0
  76. python_event_sourcery-0.7.0/event_sourcery_kurrentdb/async_/event_store.py +133 -0
  77. python_event_sourcery-0.7.0/event_sourcery_kurrentdb/async_/outbox.py +141 -0
  78. python_event_sourcery-0.7.0/event_sourcery_kurrentdb/async_/subscription.py +97 -0
  79. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_kurrentdb/event_store.py +28 -38
  80. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_kurrentdb/outbox.py +2 -0
  81. python_event_sourcery-0.7.0/event_sourcery_sqlalchemy/async_/__init__.py +125 -0
  82. python_event_sourcery-0.7.0/event_sourcery_sqlalchemy/async_/cursors_dao.py +94 -0
  83. python_event_sourcery-0.7.0/event_sourcery_sqlalchemy/async_/event_store.py +297 -0
  84. python_event_sourcery-0.7.0/event_sourcery_sqlalchemy/async_/outbox.py +106 -0
  85. python_event_sourcery-0.7.0/event_sourcery_sqlalchemy/async_/subscription.py +243 -0
  86. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/event_store.py +26 -3
  87. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/models/__init__.py +2 -3
  88. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/outbox.py +2 -0
  89. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/pyproject.toml +6 -2
  90. python_event_sourcery-0.7.0/tests/adapter.py +345 -0
  91. python_event_sourcery-0.7.0/tests/backend/in_memory_async.py +14 -0
  92. python_event_sourcery-0.7.0/tests/backend/kurrentdb_async.py +40 -0
  93. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/backend/sqlalchemy.py +3 -1
  94. python_event_sourcery-0.7.0/tests/backend/sqlalchemy_async.py +185 -0
  95. python_event_sourcery-0.7.0/tests/backend/test_django_migrations.py +71 -0
  96. python_event_sourcery-0.7.0/tests/backend/test_kurrentdb.py +42 -0
  97. python_event_sourcery-0.7.0/tests/backend/test_kurrentdb_async.py +97 -0
  98. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/backend/test_sqlalchemy.py +60 -0
  99. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/bdd.py +12 -15
  100. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/conftest.py +8 -8
  101. python_event_sourcery-0.7.0/tests/event_sourcing/conftest.py +34 -0
  102. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_sourcing/test_aggregate.py +4 -3
  103. python_event_sourcery-0.7.0/tests/event_store/async_only/test_encryption_async.py +146 -0
  104. python_event_sourcery-0.7.0/tests/event_store/async_only/test_in_memory.py +196 -0
  105. python_event_sourcery-0.7.0/tests/event_store/async_only/test_kurrentdb.py +221 -0
  106. python_event_sourcery-0.7.0/tests/event_store/async_only/test_sqlalchemy.py +328 -0
  107. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/conftest.py +19 -3
  108. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/event/test_privacy.py +14 -4
  109. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/event/test_registry.py +4 -3
  110. python_event_sourcery-0.7.0/tests/event_store/outbox/conftest.py +145 -0
  111. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/outbox/test_filters.py +4 -4
  112. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/outbox/test_multitenancy.py +4 -3
  113. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/outbox/test_outbox.py +31 -7
  114. python_event_sourcery-0.7.0/tests/event_store/subscriptions/__init__.py +0 -0
  115. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/subscriptions/other_client.py +15 -4
  116. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/subscriptions/test_from_position.py +5 -5
  117. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/subscriptions/test_handling_uncommitted_messages_from_other_clients.py +47 -7
  118. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/subscriptions/test_in_transaction_subscription.py +33 -7
  119. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/subscriptions/test_subscriptions.py +3 -2
  120. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/subscriptions/test_to_category.py +3 -2
  121. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/subscriptions/test_to_event_types.py +3 -2
  122. python_event_sourcery-0.7.0/tests/event_store/test_concurrency_control.py +79 -0
  123. python_event_sourcery-0.7.0/tests/event_store/test_multitenancy.py +100 -0
  124. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/test_named_streams.py +13 -4
  125. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/test_no_outbox.py +3 -2
  126. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/test_no_versioning.py +2 -2
  127. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/test_save_retrieval.py +4 -3
  128. python_event_sourcery-0.7.0/tests/protocols.py +86 -0
  129. python_event_sourcery-0.7.0/tests/read_model/__init__.py +0 -0
  130. python_event_sourcery-0.7.0/tests/read_model/conftest.py +105 -0
  131. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/read_model/test_projector.py +17 -12
  132. python_event_sourcery-0.7.0/tests/read_model/test_projector_async.py +231 -0
  133. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/uv.lock +226 -8
  134. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/zensical.toml +25 -0
  135. python_event_sourcery-0.6.2a1/CHANGELOG.md +0 -38
  136. python_event_sourcery-0.6.2a1/docs/code/test_get_started.py +0 -27
  137. python_event_sourcery-0.6.2a1/docs/recipes/integrate.md +0 -70
  138. python_event_sourcery-0.6.2a1/docs/recipes/saving_events.md +0 -77
  139. python_event_sourcery-0.6.2a1/docs/reference/backends/kurrentdb.md +0 -5
  140. python_event_sourcery-0.6.2a1/event_sourcery/_event_store/event/serde.py +0 -85
  141. python_event_sourcery-0.6.2a1/tests/event_sourcing/conftest.py +0 -10
  142. python_event_sourcery-0.6.2a1/tests/event_store/outbox/conftest.py +0 -83
  143. python_event_sourcery-0.6.2a1/tests/event_store/test_concurrency_control.py +0 -42
  144. python_event_sourcery-0.6.2a1/tests/event_store/test_multitenancy.py +0 -44
  145. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/.adr-dir +0 -0
  146. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/.github/workflows/bump-version-and-publish.yml +0 -0
  147. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/.github/workflows/deploy-docs.yml +0 -0
  148. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/.github/workflows/main.yml +0 -0
  149. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/.github/workflows/publish-release.yml +0 -0
  150. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/.gitignore +0 -0
  151. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/CONTRIBUTING.md +0 -0
  152. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/LICENSE +0 -0
  153. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/compose.yml +0 -0
  154. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/django_manage.py +0 -0
  155. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20220730-record-architecture-decisions.md +0 -0
  156. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20230903-explicit-init-arguments-for-sqlalchemy-s-models.md +0 -0
  157. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20231027_1-packaging-of-the-project.md +0 -0
  158. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20231027_2-leave-implementation-of-synchronous-dispatcher-to-users-of-the-library.md +0 -0
  159. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20231029-in-memory-event-store.md +0 -0
  160. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20231230-in_transaction_subscription.md +0 -0
  161. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20240726-dependencies-and-distribution.md +0 -0
  162. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20241007-multitenancy.md +0 -0
  163. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20250411-packaging-of-the-project-revised.md +0 -0
  164. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20250729-privacy-data-encryption-and-shredding.md +0 -0
  165. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20251004-packaging-of-the-event_store-package.md +0 -0
  166. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20260314-aggregate-metadata.md +0 -0
  167. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/adr/20260926-typed-event-context-components-and-enrichers.md +0 -0
  168. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/code/__init__.py +0 -0
  169. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/code/recipes/__init__.py +0 -0
  170. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/code/recipes/integrate/__init__.py +0 -0
  171. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/code/recipes/integrate/kurrent_01.py +0 -0
  172. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/code/test_context_enrichers.py +0 -0
  173. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/code/test_otel.py +0 -0
  174. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/concepts.md +0 -0
  175. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/index.md +0 -0
  176. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/recipes/context_enrichment.md +0 -0
  177. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/recipes/defining_events.md +0 -0
  178. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/recipes/opentelemetry.md +0 -0
  179. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/backends/django.md +0 -0
  180. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_sourcing/Aggregate.md +0 -0
  181. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_sourcing/Repository.md +0 -0
  182. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_sourcing/WrappedAggregate.md +0 -0
  183. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/DEFAULT_TENANT.md +0 -0
  184. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/EventStore.md +0 -0
  185. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/NO_VERSIONING.md +0 -0
  186. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/StreamCategory.md +0 -0
  187. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/StreamId.md +0 -0
  188. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/StreamUUID.md +0 -0
  189. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/TenantId.md +0 -0
  190. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/backend/Backend.md +0 -0
  191. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/backend/TransactionalBackend.md +0 -0
  192. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/backend/not_configured.md +0 -0
  193. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/backend/singleton.md +0 -0
  194. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/encryption/DataSubject.md +0 -0
  195. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/encryption/Encrypted.md +0 -0
  196. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/encryption/Encryption.md +0 -0
  197. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/encryption/NoEncryptionStrategy.md +0 -0
  198. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/encryption/NoKeyStorageStrategy.md +0 -0
  199. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/Context.md +0 -0
  200. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/ContextComponent.md +0 -0
  201. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/Entry.md +0 -0
  202. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/Event.md +0 -0
  203. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/EventContextEnricher.md +0 -0
  204. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/EventCorrelation.md +0 -0
  205. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/Position.md +0 -0
  206. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/RawEvent.md +0 -0
  207. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/Recorded.md +0 -0
  208. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/RecordedRaw.md +0 -0
  209. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/event/WrappedEvent.md +0 -0
  210. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/exceptions.md +0 -0
  211. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/in_transaction/Dispatcher.md +0 -0
  212. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/in_transaction/Listener.md +0 -0
  213. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/in_transaction/Listeners.md +0 -0
  214. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/interfaces/EncryptionKeyStorageStrategy.md +0 -0
  215. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/interfaces/EncryptionStrategy.md +0 -0
  216. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/interfaces/OutboxFiltererStrategy.md +0 -0
  217. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/interfaces/OutboxStorageStrategy.md +0 -0
  218. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/interfaces/StorageStrategy.md +0 -0
  219. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/interfaces/SubscriptionStrategy.md +0 -0
  220. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/outbox/NoOutboxStorageStrategy.md +0 -0
  221. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/outbox/Outbox.md +0 -0
  222. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/outbox/no_filter.md +0 -0
  223. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/subscription/BuildPhase.md +0 -0
  224. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/subscription/FilterPhase.md +0 -0
  225. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/subscription/PositionPhase.md +0 -0
  226. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/event_store/subscription/SubscriptionBuilder.md +0 -0
  227. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/otel/OpenTelemetryCarrier.md +0 -0
  228. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/otel/OpenTelemetryContextEnricher.md +0 -0
  229. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/otel/get_parent_context.md +0 -0
  230. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/reference/otel/use_parent_context.md +0 -0
  231. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/res/eda_example.plantuml +0 -0
  232. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/docs/res/eda_example.png +0 -0
  233. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/__init__.py +0 -0
  234. {python_event_sourcery-0.6.2a1/event_sourcery/_event_store/event → python_event_sourcery-0.7.0/event_sourcery/_event_store/_async}/__init__.py +0 -0
  235. {python_event_sourcery-0.6.2a1/event_sourcery/_event_store/subscription → python_event_sourcery-0.7.0/event_sourcery/_event_store/event}/__init__.py +0 -0
  236. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/event/dto.py +0 -0
  237. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/event/registry.py +0 -0
  238. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/outbox.py +0 -0
  239. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/stream_id.py +0 -0
  240. {python_event_sourcery-0.6.2a1/event_sourcery_django/migrations → python_event_sourcery-0.7.0/event_sourcery/_event_store/subscription}/__init__.py +0 -0
  241. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/subscription/interfaces.py +0 -0
  242. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/tenant_id.py +0 -0
  243. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/_event_store/versioning.py +0 -0
  244. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/backend.py +0 -0
  245. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/event.py +0 -0
  246. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/event_sourcing/aggregate.py +0 -0
  247. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/event_sourcing/repository.py +0 -0
  248. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/exceptions.py +0 -0
  249. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/in_transaction.py +0 -0
  250. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/interfaces.py +0 -0
  251. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/outbox.py +0 -0
  252. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/py.typed +0 -0
  253. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/read_model/cursors_dao.py +0 -0
  254. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/read_model/projector.py +0 -0
  255. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery/subscription.py +0 -0
  256. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_django/__init__.py +0 -0
  257. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_django/migrations/0001_initial.py +0 -0
  258. {python_event_sourcery-0.6.2a1/tests → python_event_sourcery-0.7.0/event_sourcery_django/migrations}/__init__.py +0 -0
  259. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_django/outbox.py +0 -0
  260. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_django/py.typed +0 -0
  261. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_django/subscription.py +0 -0
  262. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_kurrentdb/__init__.py +0 -0
  263. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_kurrentdb/dto.py +0 -0
  264. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_kurrentdb/py.typed +0 -0
  265. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_kurrentdb/stream.py +0 -0
  266. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_kurrentdb/subscription.py +0 -0
  267. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_otel/__init__.py +0 -0
  268. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_otel/_carrier.py +0 -0
  269. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_otel/_context.py +0 -0
  270. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_otel/_enricher.py +0 -0
  271. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_otel/py.typed +0 -0
  272. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/__init__.py +0 -0
  273. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/cursors_dao.py +0 -0
  274. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/dto.py +0 -0
  275. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/guid.py +0 -0
  276. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/jsonb.py +0 -0
  277. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/models/base.py +0 -0
  278. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/models/default.py +0 -0
  279. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/py.typed +0 -0
  280. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/event_sourcery_sqlalchemy/subscription.py +0 -0
  281. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/AGENTS.md +0 -0
  282. {python_event_sourcery-0.6.2a1/tests/backend → python_event_sourcery-0.7.0/tests}/__init__.py +0 -0
  283. {python_event_sourcery-0.6.2a1/tests/event_sourcing → python_event_sourcery-0.7.0/tests/backend}/__init__.py +0 -0
  284. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/backend/django.py +0 -0
  285. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/backend/in_memory.py +0 -0
  286. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/backend/kurrentdb.py +0 -0
  287. {python_event_sourcery-0.6.2a1/tests/event_store → python_event_sourcery-0.7.0/tests/event_sourcing}/__init__.py +0 -0
  288. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_sourcing/light_switch.py +0 -0
  289. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_sourcing/test_wrapped_aggregate.py +0 -0
  290. {python_event_sourcery-0.6.2a1/tests/event_store/event → python_event_sourcery-0.7.0/tests/event_store}/__init__.py +0 -0
  291. {python_event_sourcery-0.6.2a1/tests/event_store/outbox → python_event_sourcery-0.7.0/tests/event_store/async_only}/__init__.py +0 -0
  292. {python_event_sourcery-0.6.2a1/tests/event_store/subscriptions → python_event_sourcery-0.7.0/tests/event_store/event}/__init__.py +0 -0
  293. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/event/test_dto.py +0 -0
  294. {python_event_sourcery-0.6.2a1/tests/read_model → python_event_sourcery-0.7.0/tests/event_store/outbox}/__init__.py +0 -0
  295. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/subscriptions/test_batch.py +0 -0
  296. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/test_category.py +0 -0
  297. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/test_context_enrichment.py +0 -0
  298. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/test_removing_stream.py +0 -0
  299. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/test_snapshots.py +0 -0
  300. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/event_store/test_stream_id.py +0 -0
  301. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/factories.py +0 -0
  302. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/mark.py +0 -0
  303. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/matchers.py +0 -0
  304. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/otel/AGENTS.md +0 -0
  305. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/otel/bdd.py +0 -0
  306. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/otel/conftest.py +0 -0
  307. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/otel/test_context_propagation.py +0 -0
  308. {python_event_sourcery-0.6.2a1 → python_event_sourcery-0.7.0}/tests/otel/test_parent_context.py +0 -0
@@ -5,7 +5,7 @@ Event sourcing library for Python with pluggable backends.
5
5
  ## Layout
6
6
 
7
7
  - `event_sourcery/` — core library
8
- - `event_sourcery_sqlalchemy/`, `event_sourcery_django/`, `event_sourcery_kurrentdb/` — backends (optional extras: `sqlalchemy`, `django`, `kurrentdb`)
8
+ - `event_sourcery_sqlalchemy/`, `event_sourcery_django/`, `event_sourcery_kurrentdb/` — backends (optional extras: `sqlalchemy`, `sqlalchemy-async`, `django`, `kurrentdb`)
9
9
  - `tests/` — shared test suite run against every backend
10
10
  - `docs/` — documentation (zensical)
11
11
 
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ ## 0.7.0
4
+ ### Added
5
+ - Async API mirroring the sync one: `AsyncEventStore`, `AsyncBackend`, `AsyncOutbox`,
6
+ `AsyncRepository`, `AsyncProjector`, `AsyncEncryption` and friends.
7
+ - Async backends: in-memory, `AsyncSQLAlchemyBackend` (new `sqlalchemy-async` extra)
8
+ and `AsyncKurrentDBBackend`.
9
+
10
+ ### Changed
11
+ - **Breaking:** `expected_version` is enforced on every append. The default `0` now
12
+ means "stream must not exist", so appending to an existing stream requires its
13
+ current version (or `NO_VERSIONING`); a non-zero version on a new stream is rejected.
14
+ Previously both cases were silently accepted.
15
+ - **Breaking:** KurrentDB checks `expected_version` server-side, closing the race
16
+ between version check and append.
17
+ - **Breaking (Django):** migration `0002` adds a unique constraint on
18
+ `(stream, version)`. Duplicates, possible under the previous lenient versioning,
19
+ make it stop with a list of affected streams; nothing is changed until they are
20
+ resolved. Find them with:
21
+ `SELECT stream_id, version FROM event_sourcery_django_event
22
+ GROUP BY stream_id, version HAVING COUNT(*) > 1;`
23
+ Resolve per stream as your domain requires: renumber the events (and the stream's
24
+ `version`) in insertion order, or delete the duplicates. Building the index blocks
25
+ writes to the events table for its duration.
26
+
27
+ ### Fixes
28
+ - Tenant isolation: in-memory streams are scoped per tenant; SQLAlchemy
29
+ `delete_stream`, `save_snapshot` and versioned appends no longer touch other
30
+ tenants' streams with the same ID.
31
+ - Outbox preserves stream category (SQLAlchemy, Django).
32
+ - KurrentDB outbox acknowledges events rejected by the filter instead of leaving
33
+ them to be redelivered.
34
+ - In-memory backend no longer persists events whose deserialization fails;
35
+ in-transaction dispatcher deserializes only events with matching listeners.
36
+ - `event_sourcery_sqlalchemy` imports on SQLAlchemy 2.1.
37
+
38
+ ## 0.6.1
39
+ ### Fixes
40
+ - Include the `py.typed` marker in the `event_sourcery_otel` package.
41
+
42
+ ## 0.6.0
43
+ ### Added
44
+ - Typed, independently owned event context components with lazy access through `Context.get()`.
45
+ - Context enrichers configured through `Backend.with_context_enrichers()`.
46
+ - Optional `event_sourcery_otel` package for OpenTelemetry context propagation and parent-context activation.
47
+
48
+ ### Changed
49
+ - **Breaking:** New `Context` values reject arbitrary root fields; add new context data with typed components.
50
+
51
+ ## 0.5.2
52
+ ### Changed
53
+ - **Breaking:** `Repository.aggregate()` yields `WrappedAggregate` instead of `Aggregate` directly
54
+
55
+ ## 0.5.1
56
+ ### Added
57
+ - In transaction subscription to category
58
+ - Custom context support in `Repository.aggregate()` via optional `context` parameter
59
+ - `WrappedEvent.get_context()` method for typed context retrieval
60
+ ### Fixes
61
+ - event_sourcery_sqlalchemy.configure_models annotation for sqlalchemy.orm.DeclarativeBase
62
+
63
+ ## 0.5.0
64
+ ### Added
65
+ - Event definitions via the `Event` base class (Pydantic) with built-in metadata and tracing IDs.
66
+ - Event Store API for appending/loading streams with optimistic concurrency and optional `NO_VERSIONING`.
67
+ - Stream snapshots to optimize long event streams.
68
+ - Outbox pattern with retries and optional filters, plus synchronous in-process subscriptions.
69
+ - Subscription builder with single-event and batch iterators for asynchronous processing.
70
+ - Multi-tenancy with tenant-scoped stores and explicit visibility rules.
71
+ - Crypto-shredding and field-level encryption via `Encrypted` and `DataSubject` markers.
72
+ - Event sourcing primitives: `Aggregate` and `Repository` base classes.
73
+ - Backend integrations for SQLAlchemy, Django, KurrentDB (EventStoreDB), and in-memory storage.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-event-sourcery
3
- Version: 0.6.2a1
3
+ Version: 0.7.0
4
4
  Summary: A library for event-based systems in Python.
5
5
  Author-email: Sebastian Buczyński <nnplaya@gmail.com>, Łukasz Żukowski <lukasz.zukowski@gmail.com>
6
6
  License-Expression: MIT
@@ -17,6 +17,8 @@ Provides-Extra: otel
17
17
  Requires-Dist: opentelemetry-api<2,>=1; extra == 'otel'
18
18
  Provides-Extra: sqlalchemy
19
19
  Requires-Dist: sqlalchemy>=2.0; extra == 'sqlalchemy'
20
+ Provides-Extra: sqlalchemy-async
21
+ Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'sqlalchemy-async'
20
22
  Description-Content-Type: text/markdown
21
23
 
22
24
 
@@ -91,7 +93,7 @@ from event_sourcery import (
91
93
 
92
94
  # Set up your DB and application as you would normally do
93
95
  engine = create_engine(
94
- "postgresql://event_sourcery:event_sourcery@localhost:5432/event_sourcery"
96
+ "postgresql+psycopg://event_sourcery:event_sourcery@localhost:5432/event_sourcery"
95
97
  )
96
98
 
97
99
 
@@ -70,7 +70,7 @@ from event_sourcery import (
70
70
 
71
71
  # Set up your DB and application as you would normally do
72
72
  engine = create_engine(
73
- "postgresql://event_sourcery:event_sourcery@localhost:5432/event_sourcery"
73
+ "postgresql+psycopg://event_sourcery:event_sourcery@localhost:5432/event_sourcery"
74
74
  )
75
75
 
76
76
 
@@ -0,0 +1,133 @@
1
+ # Async API
2
+
3
+ Date: 2026-08-27
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ Applications built on async frameworks (FastAPI, aiohttp, async workers) need
12
+ an event store that does not block the event loop. We want to add async support
13
+ without breaking or degrading the existing synchronous API, which remains fully
14
+ supported.
15
+
16
+ ## Decision
17
+
18
+ **Parallel async classes.** Every component gets an async counterpart with the
19
+ `Async` prefix: `AsyncEventStore`, `AsyncBackend`, `AsyncOutbox`,
20
+ `AsyncSubscriptionBuilder`, `AsyncStorageStrategy`, etc. Synchronous classes are
21
+ untouched. We deliberately rejected `a`-prefixed methods on shared classes to
22
+ keep contracts explicit and make accidental sync/async mixing impossible.
23
+
24
+ **Public layout mirrors the sync one.** Implementation lives in the private
25
+ `event_sourcery._event_store._async` subpackage; public proxy imports live in
26
+ `event_sourcery.async_` (following the packaging ADR). Root `event_sourcery`
27
+ exports the four main symbols, same as sync: `AsyncEventStore`, `AsyncBackend`,
28
+ `AsyncOutbox`, `AsyncTransactionalBackend`.
29
+
30
+ **Asyncio-only.** The public async contract targets asyncio. No trio/anyio
31
+ support is promised.
32
+
33
+ **Deliberate API deviations from sync:**
34
+
35
+ - `position` is a coroutine method — `await store.position()` — because
36
+ fetching the position performs I/O in real backends. Same applies to
37
+ `AsyncStorageStrategy.current_position`.
38
+ - Subscriptions return `AsyncIterator`; outbox entries are async context
39
+ managers; outbox publishers are async callables.
40
+ - **In-transaction listeners remain synchronous** even on async backends. They
41
+ are dispatched inside the append transaction, where bridging to async
42
+ callables would either block the loop or break ordering guarantees.
43
+ - KurrentDB outbox creates its persistent subscription lazily (sync variant
44
+ creates it eagerly in `with_outbox()`, which cannot await). Because the
45
+ subscription is anchored at creation time (`from_end=True`), the async
46
+ backend ensures creation before the first append that uses the outbox —
47
+ creating it only on the first outbox run would permanently skip events
48
+ appended earlier.
49
+ - SQLAlchemy async strategies avoid ORM relationship arms
50
+ (`stream.events.extend(...)`, `stream.snapshots.append(...)`) and lazy
51
+ attribute access: both trigger lazy loads, which are synchronous I/O and
52
+ raise `MissingGreenlet` under asyncio. Instead, foreign keys are assigned
53
+ directly (`entry._db_stream_id = stream.id`, producing identical INSERT
54
+ statements) and query results eager-load streams with `selectinload`.
55
+ For the same reason, the stream row found by `_ensure_stream` is passed to
56
+ `insert_events` directly instead of being looked up again in
57
+ `session.info["strong_set"]` — after a commit, the session expires ORM
58
+ instances and reading attributes of an expired instance would refresh
59
+ them lazily.
60
+ - A single `AsyncSession` cannot interleave concurrent operations. Async
61
+ SQLAlchemy usage follows the same rule as the sync one: one session (and
62
+ thus one configured backend) per concurrent unit of work.
63
+ - The async SQLAlchemy backend ships as a new `sqlalchemy-async` extra
64
+ (`SQLAlchemy[asyncio]`). Async drivers (`asyncpg`, `aiosqlite`) are not
65
+ installed by the library — the same policy as the sync variant, which
66
+ installs no database drivers either.
67
+
68
+ **Testing strategy.** The shared test suite is not duplicated: async backends
69
+ are added as fixtures next to their sync counterparts, and a sync facade
70
+ (`tests/adapter.py`) drains coroutines on a dedicated event loop, so every
71
+ existing BDD-style test exercises both APIs. A small set of true-async tests
72
+ verifies concurrency semantics directly. Coverage gate stays at 100%.
73
+
74
+ ## Consequences
75
+
76
+ - Sync and async APIs evolve in parallel; every new feature must land in both.
77
+ - Docs show sync and async snippets side by side (tabs); docs tests use the
78
+ in-memory backend only, so no docker services are needed.
79
+ - Async variants of SQLAlchemy and KurrentDB backends follow in subsequent
80
+ milestones; Django async is currently out of scope.
81
+
82
+ ## Amendment 2026-08-27: SQLAlchemy backend milestone
83
+
84
+ The SQLAlchemy milestone validated the decisions above against a real
85
+ relational backend. Consequences specific to it:
86
+
87
+ - The mirrored test suite runs against `sqlalchemy_async_sqlite` and
88
+ `sqlalchemy_async_postgres` fixtures through the sync facade. Test
89
+ transactions are never committed, so fixture teardown closes all tracked
90
+ sessions before dropping tables (mirroring the sync suite's
91
+ `close_all_sessions()`) — otherwise `DROP TABLE` deadlocks against locks
92
+ held by open transactions.
93
+ - In-transaction listeners fired inside the append transaction work unchanged:
94
+ the async dispatcher awaits deserialization before invoking synchronous listeners.
95
+
96
+ ## Amendment 2026-08-28: parity wrap-up
97
+
98
+ Milestone 4 closed the remaining gaps, with two notable designs:
99
+
100
+ - **Async encryption pipeline.** `AsyncEncryption` /
101
+ `AsyncEncryptionKeyStorageStrategy` mirror their sync counterparts;
102
+ `AsyncSerde` exclusively awaits an `AsyncEncryption`.
103
+ `AsyncBackend.with_encryption` accepts only async key storage. The async
104
+ in-transaction dispatcher awaits deserialization, then invokes synchronous
105
+ listeners. Sync backends retain their separate synchronous pipeline.
106
+ - **In-transaction dispatcher only deserializes on matching listeners.**
107
+ `Dispatcher` resolves event types lazily via `EventRegistry`, which keeps
108
+ the (possibly stubbed) serde untouched when no listener is registered.
109
+ - `AsyncRepository`, `AsyncProjector`, `AsyncCursorsDao`,
110
+ `AsyncReadModel`, `AsyncSqlAlchemyCursorsDao` complete the feature parity.
111
+
112
+ ### Parity audit (sync → async)
113
+
114
+ | Sync | Async (or documented exclusion) |
115
+ |---|---|
116
+ | `Backend` / `TransactionalBackend` | `AsyncBackend` / `AsyncTransactionalBackend` |
117
+ | `EventStore` | `AsyncEventStore` |
118
+ | `Outbox` (`run`) | `AsyncOutbox` (`run`) |
119
+ | `StorageStrategy` | `AsyncStorageStrategy` |
120
+ | `SubscriptionStrategy` | `AsyncSubscriptionStrategy` |
121
+ | `OutboxStorageStrategy` | `AsyncOutboxStorageStrategy` |
122
+ | `PositionPhase` / `FilterPhase` / `BuildPhase` | async counterparts |
123
+ | `SubscriptionBuilder` | `AsyncSubscriptionBuilder` |
124
+ | `NoKeyStorageStrategy` | `AsyncNoKeyStorageStrategy` |
125
+ | `Encryption` | `AsyncEncryption` (shred via `ashred`) |
126
+ | `EncryptionKeyStorageStrategy` | `AsyncEncryptionKeyStorageStrategy` |
127
+ | `Repository` | `AsyncRepository` |
128
+ | `CursorsDao` | `AsyncCursorsDao` |
129
+ | `Projector` | `AsyncProjector` |
130
+ | `ReadModel` (callable) | `AsyncReadModel` (async callable) |
131
+ | `SqlAlchemyCursorsDao` | `AsyncSqlAlchemyCursorsDao` |
132
+ | `InMemoryBackend` / `InMemoryKeyStorage` | `Async*` counterparts |
133
+ | `In-transaction listeners` | remain sync (ADR §2.4), dispatched under lazy resolution |
@@ -0,0 +1,15 @@
1
+ import asyncio
2
+
3
+ from kurrentdbclient import AsyncKurrentDBClient
4
+
5
+ from event_sourcery import StreamId
6
+ from event_sourcery_kurrentdb.async_ import AsyncKurrentDBBackend
7
+
8
+
9
+ async def main() -> None:
10
+ client = AsyncKurrentDBClient(uri="kurrentdb://localhost:2113?Tls=false")
11
+ backend = AsyncKurrentDBBackend().configure(client)
12
+ await backend.event_store.load_stream(StreamId()) # test if connection works
13
+
14
+
15
+ asyncio.run(main())
@@ -0,0 +1,67 @@
1
+ def test_get_started():
2
+ # --8<-- [start:get_started_01]
3
+ from event_sourcery import StreamId
4
+ from event_sourcery.backend import InMemoryBackend
5
+ from event_sourcery.event import Event
6
+
7
+ class InvoicePaid(Event):
8
+ invoice_number: str
9
+
10
+ backend = InMemoryBackend()
11
+ event_store = backend.event_store
12
+
13
+ event_store.append(
14
+ InvoicePaid(invoice_number="1003"),
15
+ stream_id=StreamId(name="invoices/1003"),
16
+ )
17
+ # --8<-- [end:get_started_01]
18
+ # --8<-- [start:get_started_02]
19
+ recorded_events = event_store.load_stream(StreamId(name="invoices/1003"))
20
+ for recorded in recorded_events:
21
+ print(recorded.event, recorded.version)
22
+ # --8<-- [end:get_started_02]
23
+
24
+ events = event_store.load_stream(StreamId(name="invoices/1003"))
25
+ assert len(events) == 1
26
+ assert events[0].event.invoice_number == "1003"
27
+ assert events[0].version == 1
28
+
29
+
30
+ def test_get_started_async():
31
+ # --8<-- [start:get_started_async_01]
32
+ import asyncio
33
+
34
+ from event_sourcery import StreamId
35
+ from event_sourcery.async_.backend import AsyncInMemoryBackend
36
+ from event_sourcery.event import Event
37
+
38
+ class InvoicePaid(Event):
39
+ invoice_number: str
40
+
41
+ backend = AsyncInMemoryBackend()
42
+ event_store = backend.event_store
43
+
44
+ async def main() -> None:
45
+ await event_store.append(
46
+ InvoicePaid(invoice_number="1003"),
47
+ stream_id=StreamId(name="invoices/1003"),
48
+ )
49
+
50
+ asyncio.run(main())
51
+
52
+ # --8<-- [end:get_started_async_01]
53
+ # --8<-- [start:get_started_async_02]
54
+ async def read() -> None:
55
+ recorded_events = await event_store.load_stream(
56
+ StreamId(name="invoices/1003"),
57
+ )
58
+ for recorded in recorded_events:
59
+ print(recorded.event, recorded.version)
60
+
61
+ asyncio.run(read())
62
+ # --8<-- [end:get_started_async_02]
63
+
64
+ events = asyncio.run(event_store.load_stream(StreamId(name="invoices/1003")))
65
+ assert len(events) == 1
66
+ assert events[0].event.invoice_number == "1003"
67
+ assert events[0].version == 1