python-ddd-framework 0.3.2__py3-none-any.whl → 0.5.1__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.
Files changed (337) hide show
  1. python_ddd_framework/__init__.py +139 -21
  2. python_ddd_framework/application/builder.py +23 -78
  3. python_ddd_framework/application/composition.py +4 -22
  4. python_ddd_framework/application/runtime.py +120 -288
  5. python_ddd_framework/application/state.py +7 -75
  6. python_ddd_framework/application_services/__init__.py +5 -21
  7. python_ddd_framework/{services/application_bindings.py → application_services/bindings.py} +52 -9
  8. python_ddd_framework/application_services/catalog.py +36 -288
  9. python_ddd_framework/application_services/contracts.py +14 -13
  10. python_ddd_framework/application_services/dispatcher.py +19 -269
  11. python_ddd_framework/application_services/invocation.py +197 -15
  12. python_ddd_framework/application_services/module.py +168 -0
  13. python_ddd_framework/application_services/pagination.py +21 -0
  14. python_ddd_framework/auditing/control.py +1 -1
  15. python_ddd_framework/auditing/module.py +15 -0
  16. python_ddd_framework/authorization/__init__.py +2 -0
  17. python_ddd_framework/authorization/contracts.py +7 -2
  18. python_ddd_framework/authorization/definitions.py +5 -0
  19. python_ddd_framework/authorization/module.py +40 -0
  20. python_ddd_framework/authorization/options.py +7 -0
  21. python_ddd_framework/background_execution/child.py +13 -3
  22. python_ddd_framework/background_execution/declarations.py +45 -0
  23. python_ddd_framework/background_execution/lifecycle.py +12 -0
  24. python_ddd_framework/background_execution/local.py +20 -1
  25. python_ddd_framework/background_execution/module.py +107 -1
  26. python_ddd_framework/background_execution/processes.py +11 -1
  27. python_ddd_framework/background_jobs/catalog.py +59 -42
  28. python_ddd_framework/background_jobs/contracts.py +16 -19
  29. python_ddd_framework/background_jobs/pgqueuer/enqueue.py +4 -0
  30. python_ddd_framework/background_jobs/pgqueuer/module.py +2 -1
  31. python_ddd_framework/background_jobs/pgqueuer/runtime.py +7 -7
  32. python_ddd_framework/background_workers/catalog.py +23 -36
  33. python_ddd_framework/background_workers/contracts.py +3 -0
  34. python_ddd_framework/background_workers/execution.py +1 -1
  35. python_ddd_framework/background_workers/runtime.py +47 -1
  36. python_ddd_framework/caching/__init__.py +7 -6
  37. python_ddd_framework/caching/contracts.py +65 -67
  38. python_ddd_framework/caching/errors.py +0 -7
  39. python_ddd_framework/caching/module.py +18 -0
  40. python_ddd_framework/caching/naming.py +29 -0
  41. python_ddd_framework/caching/options.py +38 -0
  42. python_ddd_framework/caching/unit_of_work.py +46 -0
  43. python_ddd_framework/cli/__init__.py +31 -6
  44. python_ddd_framework/cli/project.py +68 -6
  45. python_ddd_framework/cli/runtime.py +1 -1
  46. python_ddd_framework/developer_kit/generation.py +75 -22
  47. python_ddd_framework/developer_kit/project_metadata.py +2 -4
  48. python_ddd_framework/developer_kit/templates/basic/cookiecutter.json +4 -0
  49. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/README.md +45 -0
  50. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/__init__.py.jinja +0 -0
  51. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/__init__.py.jinja +0 -0
  52. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/conversion_service.py.jinja +7 -0
  53. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/module.py.jinja +38 -0
  54. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/__init__.py.jinja +0 -0
  55. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/default_conversion_service.py.jinja +10 -0
  56. python_ddd_framework/developer_kit/templates/module/cookiecutter.json +1 -1
  57. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md +42 -49
  58. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/__init__.py.jinja +1 -0
  59. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/__init__.py.jinja +1 -0
  60. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/handler.py.jinja +45 -0
  61. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/payload.py.jinja +8 -0
  62. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/__init__.py.jinja +1 -0
  63. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/handler.py.jinja +35 -0
  64. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/payload.py.jinja +5 -0
  65. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/schedule.py.jinja +15 -0
  66. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/__init__.py.jinja +1 -0
  67. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_maintenance_worker.py.jinja +28 -0
  68. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_statistics_worker.py.jinja +26 -0
  69. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/__init__.py.jinja +1 -0
  70. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/order_cache.py.jinja +9 -0
  71. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/statistics_cache.py.jinja +9 -0
  72. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/__init__.py.jinja +1 -0
  73. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/order_changed_handler.py.jinja +23 -0
  74. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/__init__.py.jinja +1 -0
  75. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/order_integration_service.py.jinja +71 -0
  76. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/order_observation_handler.py.jinja +16 -0
  77. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/__init__.py.jinja +1 -0
  78. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/order_reporting_service.py.jinja +16 -0
  79. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/__init__.py.jinja +1 -0
  80. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/order_timing_interceptor.py.jinja +17 -0
  81. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja +105 -10
  82. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options/__init__.py.jinja +1 -0
  83. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/{options.py.jinja → options/order_options.py.jinja} +1 -1
  84. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/__init__.py.jinja +1 -0
  85. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_approval_service.py.jinja +65 -0
  86. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_management_service.py.jinja +35 -0
  87. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_query_service.py.jinja +41 -0
  88. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/__init__.py.jinja +1 -0
  89. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/approval_setting_observer.py.jinja +17 -0
  90. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/__init__.py.jinja +1 -0
  91. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/approve_order.py.jinja +6 -0
  92. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/create_order.py.jinja +7 -0
  93. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/__init__.py.jinja +1 -0
  94. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/order_reporting_service.py.jinja +7 -0
  95. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/module.py.jinja +39 -2
  96. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/__init__.py.jinja +1 -0
  97. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_approval_service.py.jinja +13 -0
  98. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_management_service.py.jinja +10 -0
  99. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_query_service.py.jinja +12 -0
  100. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/__init__.py.jinja +1 -0
  101. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_statistics_snapshot.py.jinja +9 -0
  102. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_view.py.jinja +12 -0
  103. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/entities/__init__.py.jinja +1 -0
  104. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{orders.py.jinja → entities/order.py.jinja} +10 -18
  105. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/__init__.py.jinja +1 -0
  106. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/order_changed.py.jinja +13 -0
  107. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/module.py.jinja +42 -3
  108. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/__init__.py.jinja +1 -0
  109. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/order_repository.py.jinja +17 -0
  110. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/__init__.py.jinja +1 -0
  111. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/order_seed_contributor.py.jinja +22 -0
  112. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/__init__.py.jinja +1 -0
  113. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/order_approval_service.py.jinja +23 -0
  114. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/settings/__init__.py.jinja +1 -0
  115. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{settings.py.jinja → settings/approval_settings.py.jinja} +3 -2
  116. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/__init__.py.jinja +1 -0
  117. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/order_title.py.jinja +10 -0
  118. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/__init__.py.jinja +1 -0
  119. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/order_constants.py.jinja +3 -0
  120. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/__init__.py.jinja +1 -0
  121. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/order_status.py.jinja +6 -0
  122. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/__init__.py.jinja +1 -0
  123. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/order_errors.py.jinja +11 -0
  124. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/__init__.py.jinja +1 -0
  125. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_messages.py.jinja +26 -0
  126. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_observation.py.jinja +13 -0
  127. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/module.py.jinja +40 -1
  128. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/__init__.py.jinja +1 -0
  129. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/{permissions.py.jinja → permissions/order_permission_provider.py.jinja} +6 -2
  130. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/order_permissions.py.jinja +7 -0
  131. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/__init__.py.jinja +1 -0
  132. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/money.py.jinja +18 -0
  133. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/__init__.py.jinja +1 -0
  134. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/export_filter.py.jinja +15 -0
  135. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/__init__.py.jinja +1 -0
  136. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/refresh_orders.py.jinja +5 -0
  137. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/module.py.jinja +85 -7
  138. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/routers/__init__.py.jinja +1 -0
  139. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/{files.py.jinja → routers/order_files.py.jinja} +23 -20
  140. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/__init__.py.jinja +1 -0
  141. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/order_socket.py.jinja +44 -0
  142. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/migrations/__init__.py.jinja +1 -1
  143. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/__init__.py.jinja +1 -7
  144. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/base.py.jinja +7 -0
  145. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/{orders.py.jinja → order_model.py.jinja} +7 -3
  146. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/module.py.jinja +42 -5
  147. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/order_repository.py.jinja +79 -0
  148. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/__init__.py.jinja +1 -0
  149. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/test_domain.py.jinja +4 -3
  150. python_ddd_framework/developer_kit/templates/project/cookiecutter.json +1 -1
  151. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md +23 -3
  152. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +29 -22
  153. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{app.development.yaml.jinja → backend/app.development.yaml.jinja} +2 -0
  154. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{pyproject.toml.jinja → backend/pyproject.toml.jinja} +1 -1
  155. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/main.py.jinja +2 -0
  156. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/module.py.jinja +40 -3
  157. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md +42 -11
  158. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md +144 -23
  159. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/scripts/.gitkeep +0 -0
  160. python_ddd_framework/distributed_lock/contracts.py +4 -1
  161. python_ddd_framework/domain/__init__.py +2 -0
  162. python_ddd_framework/domain/services.py +11 -0
  163. python_ddd_framework/errors/business.py +10 -1
  164. python_ddd_framework/errors/lifecycle.py +3 -31
  165. python_ddd_framework/events/__init__.py +5 -2
  166. python_ddd_framework/events/aggregate.py +3 -3
  167. python_ddd_framework/events/catalog.py +12 -11
  168. python_ddd_framework/events/contracts.py +6 -1
  169. python_ddd_framework/events/contribution.py +1 -52
  170. python_ddd_framework/events/entities.py +23 -0
  171. python_ddd_framework/events/module.py +97 -0
  172. python_ddd_framework/events/runtime.py +80 -29
  173. python_ddd_framework/events/subscription.py +36 -0
  174. python_ddd_framework/events/unit_of_work.py +25 -0
  175. python_ddd_framework/extensions/__init__.py +22 -0
  176. python_ddd_framework/extensions/catalog.py +198 -0
  177. python_ddd_framework/extensions/contracts.py +85 -0
  178. python_ddd_framework/extensions/models.py +142 -0
  179. python_ddd_framework/extensions/module.py +21 -0
  180. python_ddd_framework/fastapi/__init__.py +2 -0
  181. python_ddd_framework/fastapi/action.py +1 -2
  182. python_ddd_framework/fastapi/adapter.py +16 -0
  183. python_ddd_framework/fastapi/application_services.py +43 -25
  184. python_ddd_framework/fastapi/background.py +2 -1
  185. python_ddd_framework/fastapi/contracts.py +26 -8
  186. python_ddd_framework/fastapi/health.py +1 -1
  187. python_ddd_framework/fastapi/module.py +14 -0
  188. python_ddd_framework/fastapi/parameters.py +3 -18
  189. python_ddd_framework/fastapi/realtime/connection.py +10 -7
  190. python_ddd_framework/fastapi/realtime/module.py +3 -0
  191. python_ddd_framework/fastapi/realtime/runtime.py +10 -24
  192. python_ddd_framework/fastapi/request_context.py +2 -1
  193. python_ddd_framework/fastapi/routing.py +2 -1
  194. python_ddd_framework/fastapi/server.py +8 -0
  195. python_ddd_framework/fastapi/settings.py +2 -1
  196. python_ddd_framework/hosted_services/bridge.py +95 -17
  197. python_ddd_framework/hosted_services/catalog.py +6 -16
  198. python_ddd_framework/hosted_services/contracts.py +9 -1
  199. python_ddd_framework/hosted_services/declarations.py +15 -0
  200. python_ddd_framework/hosted_services/module.py +69 -0
  201. python_ddd_framework/hosted_services/runtime.py +101 -18
  202. python_ddd_framework/identity/__init__.py +24 -0
  203. python_ddd_framework/identity/application.py +38 -90
  204. python_ddd_framework/identity/contracts.py +181 -16
  205. python_ddd_framework/identity/http_api.py +79 -2
  206. python_ddd_framework/identity/management.py +166 -0
  207. python_ddd_framework/identity/module.py +12 -4
  208. python_ddd_framework/identity/services.py +29 -16
  209. python_ddd_framework/identity/sqlalchemy/migrations/0003_extra_properties.py +26 -0
  210. python_ddd_framework/identity/sqlalchemy/migrations/0004_concurrency_version.py +29 -0
  211. python_ddd_framework/identity/sqlalchemy/models.py +21 -2
  212. python_ddd_framework/identity/sqlalchemy/module.py +11 -2
  213. python_ddd_framework/identity/sqlalchemy/repository.py +458 -0
  214. python_ddd_framework/identity/sqlalchemy/session_security.py +88 -0
  215. python_ddd_framework/identity/sqlalchemy/stores.py +69 -290
  216. python_ddd_framework/identity/tokens.py +2 -5
  217. python_ddd_framework/{application_services/validation.py → invocation/arguments.py} +8 -6
  218. python_ddd_framework/invocation/contribution.py +61 -0
  219. python_ddd_framework/invocation/dispatcher.py +291 -0
  220. python_ddd_framework/invocation/entries.py +2 -25
  221. python_ddd_framework/invocation/entrypoints.py +126 -0
  222. python_ddd_framework/{application_services → invocation}/execution.py +8 -5
  223. python_ddd_framework/invocation/function_runtime.py +3 -1
  224. python_ddd_framework/invocation/interception.py +113 -42
  225. python_ddd_framework/{application_services → invocation}/interceptors.py +2 -2
  226. python_ddd_framework/invocation/managed_proxy.py +116 -0
  227. python_ddd_framework/invocation/managed_services.py +250 -0
  228. python_ddd_framework/invocation/methods.py +345 -0
  229. python_ddd_framework/invocation/module.py +113 -0
  230. python_ddd_framework/{application_services → invocation}/policies.py +8 -1
  231. python_ddd_framework/invocation/validation.py +25 -0
  232. python_ddd_framework/lifecycle/__init__.py +2 -0
  233. python_ddd_framework/lifecycle/composition.py +5 -2
  234. python_ddd_framework/lifecycle/participants.py +37 -0
  235. python_ddd_framework/messaging/__init__.py +25 -0
  236. python_ddd_framework/messaging/channel.py +232 -0
  237. python_ddd_framework/messaging/contracts.py +78 -0
  238. python_ddd_framework/messaging/module.py +114 -0
  239. python_ddd_framework/messaging/options.py +13 -0
  240. python_ddd_framework/messaging/receipt.py +27 -0
  241. python_ddd_framework/messaging/runtime.py +139 -0
  242. python_ddd_framework/modularity/__init__.py +11 -1
  243. python_ddd_framework/modularity/contracts.py +47 -16
  244. python_ddd_framework/modularity/discovery.py +21 -2
  245. python_ddd_framework/modularity/graph.py +45 -1
  246. python_ddd_framework/modularity/registry.py +83 -3
  247. python_ddd_framework/notifications/__init__.py +2 -0
  248. python_ddd_framework/notifications/catalog.py +3 -2
  249. python_ddd_framework/notifications/declarations.py +18 -0
  250. python_ddd_framework/notifications/module.py +21 -0
  251. python_ddd_framework/observability/logging.py +3 -0
  252. python_ddd_framework/observability/tracing.py +3 -0
  253. python_ddd_framework/options/contribution.py +12 -0
  254. python_ddd_framework/options/registry.py +44 -11
  255. python_ddd_framework/realtime/__init__.py +2 -1
  256. python_ddd_framework/realtime/contracts.py +3 -7
  257. python_ddd_framework/realtime/messages.py +26 -1
  258. python_ddd_framework/redis/cache.py +243 -0
  259. python_ddd_framework/redis/cache_scripts.py +40 -0
  260. python_ddd_framework/redis/distributed_lock.py +10 -2
  261. python_ddd_framework/redis/module.py +13 -4
  262. python_ddd_framework/redis/notifications.py +5 -2
  263. python_ddd_framework/redis/runtime.py +1 -79
  264. python_ddd_framework/seeding/__init__.py +13 -0
  265. python_ddd_framework/seeding/contracts.py +45 -0
  266. python_ddd_framework/seeding/module.py +73 -0
  267. python_ddd_framework/seeding/runtime.py +63 -0
  268. python_ddd_framework/services/arbitration.py +23 -0
  269. python_ddd_framework/services/binding.py +2 -3
  270. python_ddd_framework/services/composition.py +108 -0
  271. python_ddd_framework/services/contribution.py +44 -54
  272. python_ddd_framework/services/convention.py +53 -181
  273. python_ddd_framework/services/fixed_lifetime.py +12 -15
  274. python_ddd_framework/services/framework_provider.py +3 -158
  275. python_ddd_framework/services/native_graph.py +35 -60
  276. python_ddd_framework/services/provider.py +59 -4
  277. python_ddd_framework/services/repository.py +6 -0
  278. python_ddd_framework/services/runtime.py +129 -215
  279. python_ddd_framework/settings/catalog.py +5 -4
  280. python_ddd_framework/settings/declarations.py +18 -0
  281. python_ddd_framework/settings/definitions.py +5 -0
  282. python_ddd_framework/settings/manager.py +1 -1
  283. python_ddd_framework/settings/module.py +50 -1
  284. python_ddd_framework/settings/notifications.py +4 -2
  285. python_ddd_framework/settings/refresh_module.py +9 -2
  286. python_ddd_framework/settings/sqlalchemy/module.py +2 -1
  287. python_ddd_framework/sqlalchemy/__init__.py +12 -0
  288. python_ddd_framework/sqlalchemy/alembic_runtime/env.py +1 -0
  289. python_ddd_framework/sqlalchemy/auditing.py +7 -1
  290. python_ddd_framework/sqlalchemy/extensions.py +186 -0
  291. python_ddd_framework/sqlalchemy/metadata.py +110 -6
  292. python_ddd_framework/sqlalchemy/migration.py +61 -8
  293. python_ddd_framework/sqlalchemy/migration_operations.py +86 -0
  294. python_ddd_framework/sqlalchemy/module.py +14 -11
  295. python_ddd_framework/sqlalchemy/module_migration.py +71 -3
  296. python_ddd_framework/sqlalchemy/repository.py +217 -13
  297. python_ddd_framework/sqlalchemy/session_provider.py +14 -1
  298. python_ddd_framework/testing/runtime.py +5 -4
  299. python_ddd_framework/unit_of_work/__init__.py +2 -1
  300. python_ddd_framework/unit_of_work/contracts.py +6 -7
  301. python_ddd_framework/unit_of_work/errors.py +11 -0
  302. python_ddd_framework/unit_of_work/lifecycle.py +19 -0
  303. python_ddd_framework/unit_of_work/manager.py +86 -32
  304. python_ddd_framework/unit_of_work/module.py +61 -0
  305. python_ddd_framework/unit_of_work/options.py +7 -1
  306. python_ddd_framework/validation/__init__.py +1 -0
  307. python_ddd_framework/validation/models.py +37 -0
  308. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/METADATA +147 -44
  309. python_ddd_framework-0.5.1.dist-info/RECORD +488 -0
  310. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/WHEEL +1 -1
  311. python_ddd_framework/application_services/seeding.py +0 -72
  312. python_ddd_framework/caching/catalog.py +0 -73
  313. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/cache.py.jinja +0 -10
  314. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/events.py.jinja +0 -24
  315. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration.py.jinja +0 -41
  316. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/orders.py.jinja +0 -83
  317. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/tasks.py.jinja +0 -84
  318. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/orders.py.jinja +0 -35
  319. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repository.py.jinja +0 -14
  320. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding.py.jinja +0 -19
  321. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/definitions.py.jinja +0 -24
  322. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/realtime.py.jinja +0 -36
  323. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/orders.py.jinja +0 -49
  324. python_ddd_framework-0.3.2.dist-info/RECORD +0 -355
  325. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.dockerignore → backend/.dockerignore} +0 -0
  326. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.gitignore → backend/.gitignore} +0 -0
  327. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.python-version → backend/.python-version} +0 -0
  328. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{Dockerfile → backend/Dockerfile} +0 -0
  329. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.dev.yaml.jinja → backend/compose.dev.yaml.jinja} +0 -0
  330. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.production.yaml.jinja → backend/compose.production.yaml.jinja} +0 -0
  331. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/__init__.py.jinja +0 -0
  332. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/conftest.py.jinja +0 -0
  333. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/host/test_http.py.jinja +0 -0
  334. /python_ddd_framework/{application_services → invocation}/signature.py +0 -0
  335. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/entry_points.txt +0 -0
  336. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/licenses/LICENSE +0 -0
  337. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +0 -0
@@ -0,0 +1,17 @@
1
+ """Record invocation timing through the framework's interceptor extension."""
2
+
3
+ import logging
4
+ from time import monotonic
5
+
6
+ from python_ddd_framework import Interceptor, MethodInvocation
7
+
8
+
9
+ class OrderTimingInterceptor(Interceptor):
10
+ async def intercept(self, invocation: MethodInvocation) -> object:
11
+ started = monotonic()
12
+ try:
13
+ return await invocation.proceed()
14
+ finally:
15
+ logging.getLogger(__name__).info(
16
+ "Order invocation completed in %.4fs", monotonic() - started
17
+ )
@@ -1,20 +1,115 @@
1
1
  """模块贡献自己的服务与任务;provider 选择留给 Host。"""
2
2
 
3
- from python_ddd_framework import AppModule, ConfigureContext
3
+ from dishka import Scope
4
+ from modules.{{ cookiecutter.module_name }}.application.background_workers.order_maintenance_worker import (
5
+ OrderMaintenanceWorker,
6
+ )
7
+ from modules.{{ cookiecutter.module_name }}.application.background_workers.order_statistics_worker import (
8
+ OrderStatisticsWorker,
9
+ )
10
+ from modules.{{ cookiecutter.module_name }}.application.interceptors.order_timing_interceptor import (
11
+ OrderTimingInterceptor,
12
+ )
13
+ from modules.{{ cookiecutter.module_name }}.application.options.order_options import {{ cookiecutter.class_prefix }}Options
14
+ from modules.{{ cookiecutter.module_name }}.application.setting_handlers.approval_setting_observer import (
15
+ ApprovalSettingObserver,
16
+ )
17
+ from modules.{{ cookiecutter.module_name }}.application_contracts.module import (
18
+ {{ cookiecutter.class_prefix }}ApplicationContractsModule,
19
+ )
20
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_approval_service import (
21
+ {{ cookiecutter.class_prefix }}ApprovalApplicationService,
22
+ )
23
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_management_service import (
24
+ {{ cookiecutter.class_prefix }}ManagementApplicationService,
25
+ )
26
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_query_service import (
27
+ {{ cookiecutter.class_prefix }}QueryApplicationService,
28
+ )
29
+ from modules.{{ cookiecutter.module_name }}.domain.module import {{ cookiecutter.class_prefix }}DomainModule
30
+ from modules.{{ cookiecutter.module_name }}.domain.settings.approval_settings import APPROVAL_ENABLED
31
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import MODULE_NAME
4
32
 
5
- from ..application_contracts.module import {{ cookiecutter.class_prefix }}ApplicationContractsModule
6
- from ..domain.module import {{ cookiecutter.class_prefix }}DomainModule
7
- from ..domain_shared.definitions import MODULE_NAME
8
- from .cache import ORDER_CACHE
9
- from .options import {{ cookiecutter.class_prefix }}Options
10
- from .tasks import OrderMaintenanceWorker, OrderStatisticsWorker
33
+ from python_ddd_framework import (
34
+ BackgroundExecutionModule,
35
+ BackgroundWorkers,
36
+ BackgroundSchedules,
37
+ HostedServices,
38
+ LocalEventsModule,
39
+ SettingHandlers,
40
+ add_interceptor,
41
+ AppModule,
42
+ ConfigureContext,
43
+ InitializeContext,
44
+ InterceptionStage,
45
+ PostConfigureContext,
46
+ PostInitializeContext,
47
+ PreConfigureContext,
48
+ PreInitializeContext,
49
+ ShutdownContext,
50
+ )
51
+ from python_ddd_framework.caching.module import CachingModule
52
+ from python_ddd_framework.settings import SettingHandlerDefinition
11
53
 
12
54
 
13
55
  class {{ cookiecutter.class_prefix }}ApplicationModule(AppModule):
14
- dependencies = ({{ cookiecutter.class_prefix }}DomainModule, {{ cookiecutter.class_prefix }}ApplicationContractsModule)
56
+ dependencies = (
57
+ {{ cookiecutter.class_prefix }}DomainModule,
58
+ {{ cookiecutter.class_prefix }}ApplicationContractsModule,
59
+ CachingModule,
60
+ BackgroundExecutionModule,
61
+ LocalEventsModule,
62
+ )
15
63
  scan_packages = (__package__,)
16
- cache_definitions = (ORDER_CACHE,)
17
- background_workers = (OrderStatisticsWorker, OrderMaintenanceWorker)
64
+ background_workers = BackgroundWorkers((OrderStatisticsWorker, OrderMaintenanceWorker))
65
+
66
+ # 显式启用时导入 hosted_services.order_integration_service.OrderIntegrationService 并加入元组。
67
+ hosted_services = HostedServices(())
68
+ # 可选消息示例见项目 docs/development.md:选择 MessagingModule、声明 OrderObservation
69
+ # 通道并注册 hosted_services.order_observation_handler;不会自动替换线程 callback。
70
+ # 显式启用时导入 background_jobs.order_statistics.schedule.STATISTICS_SCHEDULE 并加入元组。
71
+ background_job_schedules = BackgroundSchedules(())
72
+ setting_handler_definitions = SettingHandlers((
73
+ SettingHandlerDefinition(APPROVAL_ENABLED, ApprovalSettingObserver),
74
+ ))
75
+
76
+ def pre_configure(self, context: PreConfigureContext) -> None:
77
+ # 贡献影响后续注册的 PreOptions;此阶段不启动资源。
78
+ pass
18
79
 
19
80
  def configure(self, context: ConfigureContext) -> None:
81
+ # 组装扩展:在这里声明本模块的启动 Options 和必要服务注册;业务执行代码放在应用服务中。
20
82
  context.configure({{ cookiecutter.class_prefix }}Options, section=MODULE_NAME)
83
+ context.services.add_scoped(OrderTimingInterceptor, scope=Scope.REQUEST)
84
+ add_interceptor(context,
85
+ OrderTimingInterceptor,
86
+ match=lambda method: (
87
+ method.service
88
+ in (
89
+ {{ cookiecutter.class_prefix }}QueryApplicationService,
90
+ {{ cookiecutter.class_prefix }}ManagementApplicationService,
91
+ {{ cookiecutter.class_prefix }}ApprovalApplicationService,
92
+ )
93
+ ),
94
+ before=InterceptionStage.METHOD,
95
+ )
96
+
97
+ def post_configure(self, context: PostConfigureContext) -> None:
98
+ # 读取已冻结 PreOptions,补充 Options 或覆盖已有服务注册。
99
+ pass
100
+
101
+ async def pre_initialize(self, context: PreInitializeContext) -> None:
102
+ # 异步初始化前置条件;依赖模块在同一阶段先完成。
103
+ pass
104
+
105
+ async def initialize(self, context: InitializeContext) -> None:
106
+ # 初始化本模块资源;不手工启动框架管理的 Worker 或 HostedService。
107
+ pass
108
+
109
+ async def post_initialize(self, context: PostInitializeContext) -> None:
110
+ # 完成初始化收尾;应用此时尚未对外就绪。
111
+ pass
112
+
113
+ async def shutdown(self, context: ShutdownContext) -> None:
114
+ # 逆依赖关闭本模块拥有的资源;返回前等待清理结束。
115
+ pass
@@ -1,4 +1,4 @@
1
- """typed 业务配置;从本模块 YAML 节读取,非法值在 build 失败。"""
1
+ """Typed startup options bound to the module's configuration section."""
2
2
 
3
3
  from python_ddd_framework import BaseOptions
4
4
 
@@ -0,0 +1,65 @@
1
+ import logging
2
+ from uuid import UUID
3
+
4
+ from modules.{{ cookiecutter.module_name }}.application.background_jobs.order_approval.handler import (
5
+ ApprovalJob,
6
+ )
7
+ from modules.{{ cookiecutter.module_name }}.application.background_jobs.order_approval.payload import (
8
+ ApprovalPayload,
9
+ )
10
+ from modules.{{ cookiecutter.module_name }}.application_contracts.inputs.approve_order import ApproveOrder
11
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_approval_service import (
12
+ {{ cookiecutter.class_prefix }}ApprovalApplicationService,
13
+ )
14
+ from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
15
+ from modules.{{ cookiecutter.module_name }}.domain.repositories.order_repository import OrderRepository
16
+ from modules.{{ cookiecutter.module_name }}.domain.services.order_approval_service import OrderApprovalService
17
+ from modules.{{ cookiecutter.module_name }}.domain_shared.permissions.order_permissions import ORDERS_WRITE
18
+
19
+ from python_ddd_framework import ApplicationService, CurrentUser
20
+ from python_ddd_framework.authorization import authorize
21
+ from python_ddd_framework.background_jobs import BackgroundJobEnqueuer
22
+ from python_ddd_framework.errors.base import ResourceConflictError, ResourceNotFoundError
23
+ from python_ddd_framework.fastapi import http
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class _{{ cookiecutter.class_prefix }}ApprovalApplicationService(
29
+ ApplicationService, {{ cookiecutter.class_prefix }}ApprovalApplicationService
30
+ ):
31
+ def __init__(
32
+ self,
33
+ repository: OrderRepository,
34
+ user: CurrentUser,
35
+ jobs: BackgroundJobEnqueuer,
36
+ approval: OrderApprovalService,
37
+ ) -> None:
38
+ self._repository = repository
39
+ self._user = user
40
+ self._jobs = jobs
41
+ self._approval = approval
42
+
43
+ @authorize(ORDERS_WRITE)
44
+ async def approve(self, id: UUID, command: ApproveOrder) -> OrderView:
45
+ # 业务扩展:这里编排“读取聚合 → 核对版本 → 领域服务检查策略并执行动作 → 保存”。
46
+ # 审批只是动作示例;替换为入库等用例时保留权限/并发边界,把状态规则放进领域方法。
47
+ order = await self._repository.find(id)
48
+ if order is None:
49
+ raise ResourceNotFoundError("Order not found")
50
+ if order.version != command.expected_version:
51
+ raise ResourceConflictError("Order version changed")
52
+ await self._approval.approve(order, user_id=self._user.user_id)
53
+ await self._repository.save(order)
54
+ logger.info("Order approved")
55
+ return OrderView.model_validate(order)
56
+
57
+ @authorize(ORDERS_WRITE)
58
+ @http.post(status_code=202)
59
+ async def queue_approval(self, id: UUID) -> str:
60
+ # 业务扩展:这里校验入队条件并构造自己的任务载荷;实际后台业务写在对应 Job.execute。
61
+ # Job 和载荷类型一起替换,通过注入的入队服务调用,不自行创建消费者或数据库连接。
62
+ if await self._repository.find(id) is None:
63
+ raise ResourceNotFoundError("Order not found")
64
+ # 入队参与本方法原生事务;失败时订单业务与队列写入一起回滚。
65
+ return await self._jobs.enqueue(ApprovalJob, ApprovalPayload(order_id=id))
@@ -0,0 +1,35 @@
1
+ import logging
2
+ from uuid import uuid4
3
+
4
+ from modules.{{ cookiecutter.module_name }}.application_contracts.inputs.create_order import CreateOrder
5
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_management_service import (
6
+ {{ cookiecutter.class_prefix }}ManagementApplicationService,
7
+ )
8
+ from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
9
+ from modules.{{ cookiecutter.module_name }}.domain.entities.order import Order
10
+ from modules.{{ cookiecutter.module_name }}.domain.repositories.order_repository import OrderRepository
11
+ from modules.{{ cookiecutter.module_name }}.domain.value_objects.order_title import OrderTitle
12
+ from modules.{{ cookiecutter.module_name }}.domain_shared.permissions.order_permissions import ORDERS_WRITE
13
+
14
+ from python_ddd_framework import ApplicationService
15
+ from python_ddd_framework.authorization import authorize
16
+ from python_ddd_framework.fastapi import http
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class _{{ cookiecutter.class_prefix }}ManagementApplicationService(
22
+ ApplicationService, {{ cookiecutter.class_prefix }}ManagementApplicationService
23
+ ):
24
+ def __init__(self, repository: OrderRepository) -> None:
25
+ self._repository = repository
26
+
27
+ @authorize(ORDERS_WRITE)
28
+ @http.post(status_code=201)
29
+ async def create(self, command: CreateOrder) -> OrderView:
30
+ # 业务扩展:这里编排创建用例,把输入 DTO 转成领域对象,调用领域方法,再交给仓储保存。
31
+ # 将 Order/OrderTitle 换成自己的聚合和值对象;领域规则写进聚合,事务由正式调用管线完成。
32
+ order = Order.create(uuid4(), OrderTitle(value=command.title))
33
+ await self._repository.save(order)
34
+ logger.info("Order created")
35
+ return OrderView.model_validate(order)
@@ -0,0 +1,41 @@
1
+ import logging
2
+ from uuid import UUID
3
+
4
+ from modules.{{ cookiecutter.module_name }}.application.caching.order_cache import OrderCacheItem
5
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_query_service import (
6
+ {{ cookiecutter.class_prefix }}QueryApplicationService,
7
+ )
8
+ from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
9
+ from modules.{{ cookiecutter.module_name }}.domain.repositories.order_repository import OrderRepository
10
+ from modules.{{ cookiecutter.module_name }}.domain_shared.permissions.order_permissions import ORDERS_READ
11
+
12
+ from python_ddd_framework import ApplicationService
13
+ from python_ddd_framework.authorization import authorize
14
+ from python_ddd_framework.caching import DistributedCache
15
+ from python_ddd_framework.errors.base import ResourceNotFoundError
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class _{{ cookiecutter.class_prefix }}QueryApplicationService(ApplicationService, {{ cookiecutter.class_prefix }}QueryApplicationService):
21
+ def __init__(self, repository: OrderRepository, cache: DistributedCache[OrderCacheItem]) -> None:
22
+ self._repository = repository
23
+ self._cache = cache
24
+
25
+ @authorize(ORDERS_READ)
26
+ async def get(self, id: UUID) -> OrderView:
27
+ # 业务扩展:这里组织单条查询及 DTO 返回;保留缓存时,定义和变更后的失效逻辑要一起调整。
28
+ cached = await self._cache.get(str(id))
29
+ if cached is not None:
30
+ return cached
31
+ order = await self._repository.find(id)
32
+ if order is None:
33
+ raise ResourceNotFoundError("Order not found")
34
+ view = OrderCacheItem.model_validate(order)
35
+ await self._cache.set(str(id), view)
36
+ return view
37
+
38
+ @authorize(ORDERS_READ)
39
+ async def get_list(self) -> tuple[OrderView, ...]:
40
+ # 业务扩展:这里接收实际业务的查询条件;需要筛选/分页时同步扩展 DTO 和仓储查询契约。
41
+ return tuple(OrderView.model_validate(order) for order in await self._repository.get_list())
@@ -0,0 +1,17 @@
1
+ """Application-owned setting snapshot refreshed by the framework."""
2
+
3
+ import logging
4
+ import os
5
+
6
+ from python_ddd_framework.settings import SettingSnapshot
7
+
8
+
9
+ class ApprovalSettingObserver:
10
+ def __init__(self) -> None:
11
+ self.snapshot: SettingSnapshot[bool] | None = None
12
+
13
+ async def apply(self, snapshot: SettingSnapshot[bool]) -> None:
14
+ self.snapshot = snapshot
15
+ logging.getLogger(__name__).info(
16
+ "Approval setting applied: %s pid=%s", snapshot.value, os.getpid()
17
+ )
@@ -0,0 +1,6 @@
1
+ from pydantic import BaseModel, ConfigDict, Field
2
+
3
+
4
+ class ApproveOrder(BaseModel):
5
+ model_config = ConfigDict(frozen=True, extra="forbid")
6
+ expected_version: int = Field(ge=1)
@@ -0,0 +1,7 @@
1
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import TITLE_MAX_LENGTH
2
+ from pydantic import BaseModel, ConfigDict, Field
3
+
4
+
5
+ class CreateOrder(BaseModel):
6
+ model_config = ConfigDict(frozen=True, extra="forbid")
7
+ title: str = Field(min_length=1, max_length=TITLE_MAX_LENGTH)
@@ -0,0 +1,7 @@
1
+ from typing import Protocol
2
+
3
+ from python_ddd_framework import ApplicationServiceContract
4
+
5
+
6
+ class OrderReportingService(ApplicationServiceContract, Protocol):
7
+ async def get_count(self) -> int: ...
@@ -1,10 +1,47 @@
1
1
  """契约层不反向依赖应用实现或 provider。"""
2
2
 
3
- from python_ddd_framework import AppModule
3
+ from modules.{{ cookiecutter.module_name }}.domain_shared.module import {{ cookiecutter.class_prefix }}DomainSharedModule
4
4
 
5
- from ..domain_shared.module import {{ cookiecutter.class_prefix }}DomainSharedModule
5
+ from python_ddd_framework import (
6
+ AppModule,
7
+ ConfigureContext,
8
+ InitializeContext,
9
+ PostConfigureContext,
10
+ PostInitializeContext,
11
+ PreConfigureContext,
12
+ PreInitializeContext,
13
+ ShutdownContext,
14
+ )
6
15
 
7
16
 
8
17
  class {{ cookiecutter.class_prefix }}ApplicationContractsModule(AppModule):
9
18
  dependencies = ({{ cookiecutter.class_prefix }}DomainSharedModule,)
10
19
  scan_packages = (__package__,)
20
+
21
+ def pre_configure(self, context: PreConfigureContext) -> None:
22
+ # 贡献影响后续注册的 PreOptions;此阶段不启动资源。
23
+ pass
24
+
25
+ def configure(self, context: ConfigureContext) -> None:
26
+ # 注册服务和普通 Options;业务执行留在所属能力。
27
+ pass
28
+
29
+ def post_configure(self, context: PostConfigureContext) -> None:
30
+ # 读取已冻结 PreOptions,补充 Options 或覆盖已有服务注册。
31
+ pass
32
+
33
+ async def pre_initialize(self, context: PreInitializeContext) -> None:
34
+ # 异步初始化前置条件;依赖模块在同一阶段先完成。
35
+ pass
36
+
37
+ async def initialize(self, context: InitializeContext) -> None:
38
+ # 初始化本模块资源;不手工启动框架管理的 Worker 或 HostedService。
39
+ pass
40
+
41
+ async def post_initialize(self, context: PostInitializeContext) -> None:
42
+ # 完成初始化收尾;应用此时尚未对外就绪。
43
+ pass
44
+
45
+ async def shutdown(self, context: ShutdownContext) -> None:
46
+ # 逆依赖关闭本模块拥有的资源;返回前等待清理结束。
47
+ pass
@@ -0,0 +1,13 @@
1
+ from typing import Protocol
2
+ from uuid import UUID
3
+
4
+ from modules.{{ cookiecutter.module_name }}.application_contracts.inputs.approve_order import ApproveOrder
5
+ from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
6
+
7
+ from python_ddd_framework import ApplicationServiceContract
8
+
9
+
10
+ class {{ cookiecutter.class_prefix }}ApprovalApplicationService(ApplicationServiceContract, Protocol):
11
+ async def approve(self, id: UUID, command: ApproveOrder) -> OrderView: ...
12
+
13
+ async def queue_approval(self, id: UUID) -> str: ...
@@ -0,0 +1,10 @@
1
+ from typing import Protocol
2
+
3
+ from modules.{{ cookiecutter.module_name }}.application_contracts.inputs.create_order import CreateOrder
4
+ from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
5
+
6
+ from python_ddd_framework import ApplicationServiceContract
7
+
8
+
9
+ class {{ cookiecutter.class_prefix }}ManagementApplicationService(ApplicationServiceContract, Protocol):
10
+ async def create(self, command: CreateOrder) -> OrderView: ...
@@ -0,0 +1,12 @@
1
+ from typing import Protocol
2
+ from uuid import UUID
3
+
4
+ from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
5
+
6
+ from python_ddd_framework import ApplicationServiceContract
7
+
8
+
9
+ class {{ cookiecutter.class_prefix }}QueryApplicationService(ApplicationServiceContract, Protocol):
10
+ async def get(self, id: UUID) -> OrderView: ...
11
+
12
+ async def get_list(self) -> tuple[OrderView, ...]: ...
@@ -0,0 +1,9 @@
1
+ from datetime import datetime
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class OrderStatisticsSnapshot(BaseModel):
7
+ model_config = ConfigDict(frozen=True)
8
+ count: int = Field(ge=0)
9
+ captured_at: datetime
@@ -0,0 +1,12 @@
1
+ from uuid import UUID
2
+
3
+ from modules.{{ cookiecutter.module_name }}.domain_shared.enums.order_status import OrderStatus
4
+ from pydantic import BaseModel, ConfigDict
5
+
6
+
7
+ class OrderView(BaseModel):
8
+ model_config = ConfigDict(frozen=True, from_attributes=True)
9
+ id: UUID
10
+ title: str
11
+ status: OrderStatus
12
+ version: int
@@ -1,26 +1,14 @@
1
- """聚合拥有审批规则、版本及事件;持久化实现只负责映射。"""
2
-
3
1
  from __future__ import annotations
4
2
 
5
- from dataclasses import dataclass
6
3
  from uuid import UUID
7
4
 
8
- from pydantic import Field
9
- from python_ddd_framework import AggregateRoot, ValueObject
10
- from python_ddd_framework.errors import BusinessError
11
-
12
- from ..domain_shared.definitions import ALREADY_APPROVED, TITLE_MAX_LENGTH, OrderStatus
13
-
5
+ from modules.{{ cookiecutter.module_name }}.domain.events.order_changed import OrderChanged
6
+ from modules.{{ cookiecutter.module_name }}.domain.value_objects.order_title import OrderTitle
7
+ from modules.{{ cookiecutter.module_name }}.domain_shared.enums.order_status import OrderStatus
8
+ from modules.{{ cookiecutter.module_name }}.domain_shared.errors.order_errors import ALREADY_APPROVED
14
9
 
15
- class OrderTitle(ValueObject):
16
- value: str = Field(min_length=1, max_length=TITLE_MAX_LENGTH)
17
-
18
-
19
- @dataclass(frozen=True)
20
- class OrderChanged:
21
- order_id: UUID
22
- status: OrderStatus
23
- user_id: UUID | None = None
10
+ from python_ddd_framework import AggregateRoot
11
+ from python_ddd_framework.errors import BusinessError
24
12
 
25
13
 
26
14
  class Order(AggregateRoot[UUID]):
@@ -45,11 +33,15 @@ class Order(AggregateRoot[UUID]):
45
33
 
46
34
  @classmethod
47
35
  def create(cls, id: UUID, title: OrderTitle) -> Order:
36
+ # 业务扩展:在这里校验新聚合的创建条件、初始化实体和值对象;替换订单示例时一起调整参数。
37
+ # 领域方法只维护业务状态和事件,持久化由应用服务调用仓储完成。
48
38
  order = cls(id, title)
49
39
  order.raise_local_event(OrderChanged(id, order.status))
50
40
  return order
51
41
 
52
42
  def approve(self, *, user_id: UUID | None = None) -> None:
43
+ # 业务扩展:在这里实现自己的状态转换条件,例如库存是否足够、单据是否允许提交。
44
+ # 校验通过后再修改状态并记录对应事件;HTTP 参数绑定和调用者授权留在外层。
53
45
  if self.status is OrderStatus.APPROVED:
54
46
  raise BusinessError(ALREADY_APPROVED)
55
47
  self._status = OrderStatus.APPROVED
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from uuid import UUID
5
+
6
+ from modules.{{ cookiecutter.module_name }}.domain_shared.enums.order_status import OrderStatus
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class OrderChanged:
11
+ order_id: UUID
12
+ status: OrderStatus
13
+ user_id: UUID | None = None
@@ -1,10 +1,49 @@
1
1
  """扫描仅包含实际业务声明,模块 tests 不参与服务发现。"""
2
2
 
3
- from python_ddd_framework import AppModule
3
+ from modules.{{ cookiecutter.module_name }}.domain_shared.module import {{ cookiecutter.class_prefix }}DomainSharedModule
4
4
 
5
- from ..domain_shared.module import {{ cookiecutter.class_prefix }}DomainSharedModule
5
+ from python_ddd_framework import (
6
+ AppModule,
7
+ SettingsModule,
8
+ DataSeedingModule,
9
+ ConfigureContext,
10
+ InitializeContext,
11
+ PostConfigureContext,
12
+ PostInitializeContext,
13
+ PreConfigureContext,
14
+ PreInitializeContext,
15
+ ShutdownContext,
16
+ )
6
17
 
7
18
 
8
19
  class {{ cookiecutter.class_prefix }}DomainModule(AppModule):
9
- dependencies = ({{ cookiecutter.class_prefix }}DomainSharedModule,)
20
+ dependencies = ({{ cookiecutter.class_prefix }}DomainSharedModule, SettingsModule, DataSeedingModule)
10
21
  scan_packages = (__package__,)
22
+
23
+ def pre_configure(self, context: PreConfigureContext) -> None:
24
+ # 贡献影响后续注册的 PreOptions;此阶段不启动资源。
25
+ pass
26
+
27
+ def configure(self, context: ConfigureContext) -> None:
28
+ # 注册服务和普通 Options;业务执行留在所属能力。
29
+ pass
30
+
31
+ def post_configure(self, context: PostConfigureContext) -> None:
32
+ # 读取已冻结 PreOptions,补充 Options 或覆盖已有服务注册。
33
+ pass
34
+
35
+ async def pre_initialize(self, context: PreInitializeContext) -> None:
36
+ # 异步初始化前置条件;依赖模块在同一阶段先完成。
37
+ pass
38
+
39
+ async def initialize(self, context: InitializeContext) -> None:
40
+ # 初始化本模块资源;不手工启动框架管理的 Worker 或 HostedService。
41
+ pass
42
+
43
+ async def post_initialize(self, context: PostInitializeContext) -> None:
44
+ # 完成初始化收尾;应用此时尚未对外就绪。
45
+ pass
46
+
47
+ async def shutdown(self, context: ShutdownContext) -> None:
48
+ # 逆依赖关闭本模块拥有的资源;返回前等待清理结束。
49
+ pass
@@ -0,0 +1,17 @@
1
+ """领域需要的业务仓储边界;不暴露 Session 或容器。"""
2
+
3
+ from typing import Protocol
4
+ from uuid import UUID
5
+
6
+ from modules.{{ cookiecutter.module_name }}.domain.entities.order import Order
7
+
8
+ from python_ddd_framework import RepositoryContract
9
+
10
+
11
+ class OrderRepository(RepositoryContract, Protocol):
12
+ async def find(self, id: UUID, include_details: bool = True) -> Order | None: ...
13
+ async def get(self, id: UUID, include_details: bool = True) -> Order: ...
14
+ async def get_count(self) -> int: ...
15
+ async def get_paged_list(self, skip_count: int, max_result_count: int, sorting: str | None = None, include_details: bool = False) -> list[Order]: ...
16
+ async def get_list(self, include_details: bool = False) -> list[Order]: ...
17
+ async def save(self, order: Order) -> None: ...