python-ddd-framework 0.3.2__py3-none-any.whl → 0.4.0__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 (334) 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 +96 -291
  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 +126 -7
  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/module.py +107 -1
  25. python_ddd_framework/background_jobs/catalog.py +59 -42
  26. python_ddd_framework/background_jobs/contracts.py +16 -19
  27. python_ddd_framework/background_jobs/pgqueuer/enqueue.py +4 -0
  28. python_ddd_framework/background_jobs/pgqueuer/module.py +2 -1
  29. python_ddd_framework/background_jobs/pgqueuer/runtime.py +7 -7
  30. python_ddd_framework/background_workers/catalog.py +23 -36
  31. python_ddd_framework/background_workers/contracts.py +3 -0
  32. python_ddd_framework/background_workers/execution.py +1 -1
  33. python_ddd_framework/background_workers/runtime.py +38 -1
  34. python_ddd_framework/caching/__init__.py +7 -6
  35. python_ddd_framework/caching/contracts.py +65 -67
  36. python_ddd_framework/caching/errors.py +0 -7
  37. python_ddd_framework/caching/module.py +18 -0
  38. python_ddd_framework/caching/naming.py +29 -0
  39. python_ddd_framework/caching/options.py +38 -0
  40. python_ddd_framework/caching/unit_of_work.py +46 -0
  41. python_ddd_framework/cli/__init__.py +31 -6
  42. python_ddd_framework/cli/project.py +68 -6
  43. python_ddd_framework/cli/runtime.py +1 -1
  44. python_ddd_framework/developer_kit/generation.py +75 -22
  45. python_ddd_framework/developer_kit/project_metadata.py +2 -4
  46. python_ddd_framework/developer_kit/templates/basic/cookiecutter.json +4 -0
  47. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/README.md +45 -0
  48. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/__init__.py.jinja +0 -0
  49. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/__init__.py.jinja +0 -0
  50. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/conversion_service.py.jinja +7 -0
  51. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/module.py.jinja +38 -0
  52. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/__init__.py.jinja +0 -0
  53. python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/default_conversion_service.py.jinja +10 -0
  54. python_ddd_framework/developer_kit/templates/module/cookiecutter.json +1 -1
  55. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md +41 -49
  56. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/__init__.py.jinja +1 -0
  57. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/__init__.py.jinja +1 -0
  58. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/handler.py.jinja +45 -0
  59. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/payload.py.jinja +8 -0
  60. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/__init__.py.jinja +1 -0
  61. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/handler.py.jinja +35 -0
  62. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/payload.py.jinja +5 -0
  63. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/schedule.py.jinja +15 -0
  64. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/__init__.py.jinja +1 -0
  65. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_maintenance_worker.py.jinja +28 -0
  66. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_statistics_worker.py.jinja +26 -0
  67. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/__init__.py.jinja +1 -0
  68. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/order_cache.py.jinja +9 -0
  69. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/statistics_cache.py.jinja +9 -0
  70. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/__init__.py.jinja +1 -0
  71. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/order_changed_handler.py.jinja +23 -0
  72. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/__init__.py.jinja +1 -0
  73. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/{integration.py.jinja → hosted_services/order_integration_service.py.jinja} +8 -2
  74. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/order_observation_handler.py.jinja +16 -0
  75. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/__init__.py.jinja +1 -0
  76. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/order_reporting_service.py.jinja +16 -0
  77. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/__init__.py.jinja +1 -0
  78. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/order_timing_interceptor.py.jinja +17 -0
  79. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja +105 -10
  80. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options/__init__.py.jinja +1 -0
  81. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/{options.py.jinja → options/order_options.py.jinja} +1 -1
  82. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/__init__.py.jinja +1 -0
  83. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_approval_service.py.jinja +65 -0
  84. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_management_service.py.jinja +35 -0
  85. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_query_service.py.jinja +41 -0
  86. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/__init__.py.jinja +1 -0
  87. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/approval_setting_observer.py.jinja +17 -0
  88. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/__init__.py.jinja +1 -0
  89. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/approve_order.py.jinja +6 -0
  90. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/create_order.py.jinja +7 -0
  91. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/__init__.py.jinja +1 -0
  92. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/order_reporting_service.py.jinja +7 -0
  93. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/module.py.jinja +39 -2
  94. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/__init__.py.jinja +1 -0
  95. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_approval_service.py.jinja +13 -0
  96. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_management_service.py.jinja +10 -0
  97. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_query_service.py.jinja +12 -0
  98. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/__init__.py.jinja +1 -0
  99. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_statistics_snapshot.py.jinja +9 -0
  100. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_view.py.jinja +12 -0
  101. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/entities/__init__.py.jinja +1 -0
  102. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{orders.py.jinja → entities/order.py.jinja} +10 -18
  103. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/__init__.py.jinja +1 -0
  104. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/order_changed.py.jinja +13 -0
  105. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/module.py.jinja +42 -3
  106. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/__init__.py.jinja +1 -0
  107. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/order_repository.py.jinja +17 -0
  108. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/__init__.py.jinja +1 -0
  109. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/order_seed_contributor.py.jinja +22 -0
  110. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/__init__.py.jinja +1 -0
  111. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/order_approval_service.py.jinja +23 -0
  112. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/settings/__init__.py.jinja +1 -0
  113. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{settings.py.jinja → settings/approval_settings.py.jinja} +3 -2
  114. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/__init__.py.jinja +1 -0
  115. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/order_title.py.jinja +10 -0
  116. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/__init__.py.jinja +1 -0
  117. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/order_constants.py.jinja +3 -0
  118. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/__init__.py.jinja +1 -0
  119. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/order_status.py.jinja +6 -0
  120. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/__init__.py.jinja +1 -0
  121. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/order_errors.py.jinja +11 -0
  122. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/__init__.py.jinja +1 -0
  123. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_messages.py.jinja +26 -0
  124. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_observation.py.jinja +13 -0
  125. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/module.py.jinja +40 -1
  126. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/__init__.py.jinja +1 -0
  127. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/{permissions.py.jinja → permissions/order_permission_provider.py.jinja} +6 -2
  128. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/order_permissions.py.jinja +7 -0
  129. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/__init__.py.jinja +1 -0
  130. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/money.py.jinja +18 -0
  131. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/__init__.py.jinja +1 -0
  132. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/export_filter.py.jinja +15 -0
  133. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/__init__.py.jinja +1 -0
  134. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/refresh_orders.py.jinja +5 -0
  135. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/module.py.jinja +85 -7
  136. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/routers/__init__.py.jinja +1 -0
  137. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/{files.py.jinja → routers/order_files.py.jinja} +23 -20
  138. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/__init__.py.jinja +1 -0
  139. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/order_socket.py.jinja +44 -0
  140. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/migrations/__init__.py.jinja +1 -1
  141. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/__init__.py.jinja +1 -7
  142. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/base.py.jinja +7 -0
  143. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/{orders.py.jinja → order_model.py.jinja} +7 -3
  144. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/module.py.jinja +42 -5
  145. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/order_repository.py.jinja +79 -0
  146. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/__init__.py.jinja +1 -0
  147. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/test_domain.py.jinja +4 -3
  148. python_ddd_framework/developer_kit/templates/project/cookiecutter.json +1 -1
  149. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md +16 -3
  150. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +29 -22
  151. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{app.development.yaml.jinja → backend/app.development.yaml.jinja} +2 -0
  152. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{pyproject.toml.jinja → backend/pyproject.toml.jinja} +1 -1
  153. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/main.py.jinja +2 -0
  154. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/module.py.jinja +40 -3
  155. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md +40 -10
  156. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md +134 -23
  157. python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/scripts/.gitkeep +0 -0
  158. python_ddd_framework/distributed_lock/contracts.py +4 -1
  159. python_ddd_framework/domain/__init__.py +2 -0
  160. python_ddd_framework/domain/services.py +11 -0
  161. python_ddd_framework/errors/business.py +10 -1
  162. python_ddd_framework/errors/lifecycle.py +3 -31
  163. python_ddd_framework/events/__init__.py +5 -2
  164. python_ddd_framework/events/aggregate.py +3 -3
  165. python_ddd_framework/events/catalog.py +12 -11
  166. python_ddd_framework/events/contracts.py +6 -1
  167. python_ddd_framework/events/contribution.py +1 -52
  168. python_ddd_framework/events/entities.py +23 -0
  169. python_ddd_framework/events/module.py +97 -0
  170. python_ddd_framework/events/runtime.py +80 -29
  171. python_ddd_framework/events/subscription.py +36 -0
  172. python_ddd_framework/events/unit_of_work.py +25 -0
  173. python_ddd_framework/extensions/__init__.py +22 -0
  174. python_ddd_framework/extensions/catalog.py +198 -0
  175. python_ddd_framework/extensions/contracts.py +85 -0
  176. python_ddd_framework/extensions/models.py +142 -0
  177. python_ddd_framework/extensions/module.py +21 -0
  178. python_ddd_framework/fastapi/__init__.py +2 -0
  179. python_ddd_framework/fastapi/action.py +1 -2
  180. python_ddd_framework/fastapi/adapter.py +16 -0
  181. python_ddd_framework/fastapi/application_services.py +43 -25
  182. python_ddd_framework/fastapi/background.py +2 -1
  183. python_ddd_framework/fastapi/contracts.py +26 -8
  184. python_ddd_framework/fastapi/health.py +1 -1
  185. python_ddd_framework/fastapi/module.py +14 -0
  186. python_ddd_framework/fastapi/parameters.py +3 -18
  187. python_ddd_framework/fastapi/realtime/connection.py +10 -7
  188. python_ddd_framework/fastapi/realtime/module.py +3 -0
  189. python_ddd_framework/fastapi/realtime/runtime.py +10 -24
  190. python_ddd_framework/fastapi/request_context.py +2 -1
  191. python_ddd_framework/fastapi/routing.py +2 -1
  192. python_ddd_framework/fastapi/server.py +8 -0
  193. python_ddd_framework/fastapi/settings.py +2 -1
  194. python_ddd_framework/hosted_services/bridge.py +20 -1
  195. python_ddd_framework/hosted_services/catalog.py +6 -16
  196. python_ddd_framework/hosted_services/contracts.py +4 -0
  197. python_ddd_framework/hosted_services/declarations.py +15 -0
  198. python_ddd_framework/hosted_services/module.py +69 -0
  199. python_ddd_framework/hosted_services/runtime.py +30 -1
  200. python_ddd_framework/identity/__init__.py +24 -0
  201. python_ddd_framework/identity/application.py +38 -90
  202. python_ddd_framework/identity/contracts.py +181 -16
  203. python_ddd_framework/identity/http_api.py +79 -2
  204. python_ddd_framework/identity/management.py +166 -0
  205. python_ddd_framework/identity/module.py +12 -4
  206. python_ddd_framework/identity/services.py +29 -16
  207. python_ddd_framework/identity/sqlalchemy/migrations/0003_extra_properties.py +26 -0
  208. python_ddd_framework/identity/sqlalchemy/migrations/0004_concurrency_version.py +29 -0
  209. python_ddd_framework/identity/sqlalchemy/models.py +21 -2
  210. python_ddd_framework/identity/sqlalchemy/module.py +11 -2
  211. python_ddd_framework/identity/sqlalchemy/repository.py +458 -0
  212. python_ddd_framework/identity/sqlalchemy/session_security.py +88 -0
  213. python_ddd_framework/identity/sqlalchemy/stores.py +69 -290
  214. python_ddd_framework/identity/tokens.py +2 -5
  215. python_ddd_framework/{application_services/validation.py → invocation/arguments.py} +8 -6
  216. python_ddd_framework/invocation/contribution.py +61 -0
  217. python_ddd_framework/invocation/dispatcher.py +291 -0
  218. python_ddd_framework/invocation/entries.py +2 -25
  219. python_ddd_framework/invocation/entrypoints.py +126 -0
  220. python_ddd_framework/{application_services → invocation}/execution.py +8 -5
  221. python_ddd_framework/invocation/function_runtime.py +3 -1
  222. python_ddd_framework/invocation/interception.py +113 -42
  223. python_ddd_framework/{application_services → invocation}/interceptors.py +2 -2
  224. python_ddd_framework/invocation/managed_proxy.py +116 -0
  225. python_ddd_framework/invocation/managed_services.py +250 -0
  226. python_ddd_framework/invocation/methods.py +345 -0
  227. python_ddd_framework/invocation/module.py +113 -0
  228. python_ddd_framework/{application_services → invocation}/policies.py +8 -1
  229. python_ddd_framework/invocation/validation.py +25 -0
  230. python_ddd_framework/lifecycle/__init__.py +2 -0
  231. python_ddd_framework/lifecycle/composition.py +5 -2
  232. python_ddd_framework/lifecycle/participants.py +33 -0
  233. python_ddd_framework/messaging/__init__.py +25 -0
  234. python_ddd_framework/messaging/channel.py +205 -0
  235. python_ddd_framework/messaging/contracts.py +69 -0
  236. python_ddd_framework/messaging/module.py +101 -0
  237. python_ddd_framework/messaging/options.py +13 -0
  238. python_ddd_framework/messaging/receipt.py +27 -0
  239. python_ddd_framework/messaging/runtime.py +124 -0
  240. python_ddd_framework/modularity/__init__.py +11 -1
  241. python_ddd_framework/modularity/contracts.py +47 -16
  242. python_ddd_framework/modularity/discovery.py +21 -2
  243. python_ddd_framework/modularity/graph.py +45 -1
  244. python_ddd_framework/modularity/registry.py +83 -3
  245. python_ddd_framework/notifications/__init__.py +2 -0
  246. python_ddd_framework/notifications/catalog.py +3 -2
  247. python_ddd_framework/notifications/declarations.py +18 -0
  248. python_ddd_framework/notifications/module.py +21 -0
  249. python_ddd_framework/observability/logging.py +3 -0
  250. python_ddd_framework/observability/tracing.py +3 -0
  251. python_ddd_framework/options/contribution.py +12 -0
  252. python_ddd_framework/options/registry.py +44 -11
  253. python_ddd_framework/realtime/__init__.py +2 -1
  254. python_ddd_framework/realtime/contracts.py +3 -7
  255. python_ddd_framework/realtime/messages.py +26 -1
  256. python_ddd_framework/redis/cache.py +243 -0
  257. python_ddd_framework/redis/cache_scripts.py +40 -0
  258. python_ddd_framework/redis/distributed_lock.py +10 -2
  259. python_ddd_framework/redis/module.py +13 -4
  260. python_ddd_framework/redis/notifications.py +5 -2
  261. python_ddd_framework/redis/runtime.py +1 -79
  262. python_ddd_framework/seeding/__init__.py +13 -0
  263. python_ddd_framework/seeding/contracts.py +45 -0
  264. python_ddd_framework/seeding/module.py +73 -0
  265. python_ddd_framework/seeding/runtime.py +63 -0
  266. python_ddd_framework/services/arbitration.py +23 -0
  267. python_ddd_framework/services/binding.py +2 -3
  268. python_ddd_framework/services/composition.py +108 -0
  269. python_ddd_framework/services/contribution.py +44 -54
  270. python_ddd_framework/services/convention.py +53 -181
  271. python_ddd_framework/services/fixed_lifetime.py +12 -15
  272. python_ddd_framework/services/framework_provider.py +3 -158
  273. python_ddd_framework/services/native_graph.py +35 -60
  274. python_ddd_framework/services/provider.py +59 -4
  275. python_ddd_framework/services/repository.py +6 -0
  276. python_ddd_framework/services/runtime.py +129 -215
  277. python_ddd_framework/settings/catalog.py +5 -4
  278. python_ddd_framework/settings/declarations.py +18 -0
  279. python_ddd_framework/settings/definitions.py +5 -0
  280. python_ddd_framework/settings/manager.py +1 -1
  281. python_ddd_framework/settings/module.py +50 -1
  282. python_ddd_framework/settings/notifications.py +4 -2
  283. python_ddd_framework/settings/refresh_module.py +9 -2
  284. python_ddd_framework/settings/sqlalchemy/module.py +2 -1
  285. python_ddd_framework/sqlalchemy/__init__.py +12 -0
  286. python_ddd_framework/sqlalchemy/alembic_runtime/env.py +1 -0
  287. python_ddd_framework/sqlalchemy/auditing.py +7 -1
  288. python_ddd_framework/sqlalchemy/extensions.py +186 -0
  289. python_ddd_framework/sqlalchemy/metadata.py +110 -6
  290. python_ddd_framework/sqlalchemy/migration.py +61 -8
  291. python_ddd_framework/sqlalchemy/migration_operations.py +86 -0
  292. python_ddd_framework/sqlalchemy/module.py +14 -11
  293. python_ddd_framework/sqlalchemy/module_migration.py +71 -3
  294. python_ddd_framework/sqlalchemy/repository.py +217 -13
  295. python_ddd_framework/sqlalchemy/session_provider.py +14 -1
  296. python_ddd_framework/testing/runtime.py +5 -4
  297. python_ddd_framework/unit_of_work/__init__.py +2 -1
  298. python_ddd_framework/unit_of_work/contracts.py +6 -7
  299. python_ddd_framework/unit_of_work/errors.py +11 -0
  300. python_ddd_framework/unit_of_work/lifecycle.py +19 -0
  301. python_ddd_framework/unit_of_work/manager.py +86 -32
  302. python_ddd_framework/unit_of_work/module.py +61 -0
  303. python_ddd_framework/unit_of_work/options.py +7 -1
  304. python_ddd_framework/validation/__init__.py +1 -0
  305. python_ddd_framework/validation/models.py +37 -0
  306. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/METADATA +139 -44
  307. python_ddd_framework-0.4.0.dist-info/RECORD +488 -0
  308. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/WHEEL +1 -1
  309. python_ddd_framework/application_services/seeding.py +0 -72
  310. python_ddd_framework/caching/catalog.py +0 -73
  311. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/cache.py.jinja +0 -10
  312. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/events.py.jinja +0 -24
  313. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/orders.py.jinja +0 -83
  314. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/tasks.py.jinja +0 -84
  315. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/orders.py.jinja +0 -35
  316. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repository.py.jinja +0 -14
  317. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding.py.jinja +0 -19
  318. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/definitions.py.jinja +0 -24
  319. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/realtime.py.jinja +0 -36
  320. python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/orders.py.jinja +0 -49
  321. python_ddd_framework-0.3.2.dist-info/RECORD +0 -355
  322. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.dockerignore → backend/.dockerignore} +0 -0
  323. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.gitignore → backend/.gitignore} +0 -0
  324. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.python-version → backend/.python-version} +0 -0
  325. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{Dockerfile → backend/Dockerfile} +0 -0
  326. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.dev.yaml.jinja → backend/compose.dev.yaml.jinja} +0 -0
  327. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.production.yaml.jinja → backend/compose.production.yaml.jinja} +0 -0
  328. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/__init__.py.jinja +0 -0
  329. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/conftest.py.jinja +0 -0
  330. /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/host/test_http.py.jinja +0 -0
  331. /python_ddd_framework/{application_services → invocation}/signature.py +0 -0
  332. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/entry_points.txt +0 -0
  333. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/licenses/LICENSE +0 -0
  334. {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.4.0.dist-info}/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +0 -0
@@ -0,0 +1,23 @@
1
+ """审批设置属于跨对象的领域策略;订单自身仍拥有状态转换规则。"""
2
+
3
+ from uuid import UUID
4
+
5
+ from modules.{{ cookiecutter.module_name }}.domain.entities.order import Order
6
+ from modules.{{ cookiecutter.module_name }}.domain.settings.approval_settings import APPROVAL_ENABLED
7
+ from modules.{{ cookiecutter.module_name }}.domain_shared.errors.order_errors import APPROVAL_DISABLED
8
+
9
+ from python_ddd_framework import DomainService, ValidationEnabled
10
+ from python_ddd_framework.errors import BusinessError
11
+ from python_ddd_framework.settings import SettingProvider
12
+
13
+
14
+ class OrderApprovalService(DomainService, ValidationEnabled):
15
+ def __init__(self, settings: SettingProvider) -> None:
16
+ self._settings = settings
17
+
18
+ async def approve(self, order: Order, *, user_id: UUID | None = None) -> None:
19
+ # 业务扩展:依赖设置、仓储或多个聚合的规则写在领域服务;不接收 HTTP DTO。
20
+ # 聚合仍保证不能重复审批;保存和提交由调用方完成,失败才能整体回滚。
21
+ if not await self._settings.get(APPROVAL_ENABLED):
22
+ raise BusinessError(APPROVAL_DISABLED)
23
+ order.approve(user_id=user_id)
@@ -1,16 +1,17 @@
1
1
  """设置默认值属于领域,业务通过 SettingProvider 获取冻结读取视图。"""
2
2
 
3
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import MODULE_NAME
4
+
3
5
  from python_ddd_framework.settings import (
4
6
  SettingDefinition,
5
7
  SettingDefinitionContext,
6
8
  SettingDefinitionProvider,
7
9
  )
8
10
 
9
- from ..domain_shared.definitions import MODULE_NAME
10
-
11
11
  APPROVAL_ENABLED = SettingDefinition(f"{MODULE_NAME}.approval_enabled", bool, default=True)
12
12
 
13
13
 
14
14
  class {{ cookiecutter.class_prefix }}SettingDefinitionProvider(SettingDefinitionProvider):
15
15
  def define(self, context: SettingDefinitionContext) -> None:
16
+ # 定义扩展:在这里发布本模块的动态业务设置;类型/默认值归 SettingDefinition,读取走 SettingProvider。
16
17
  context.add(APPROVAL_ENABLED)
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+
3
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import TITLE_MAX_LENGTH
4
+ from pydantic import Field
5
+
6
+ from python_ddd_framework import ValueObject
7
+
8
+
9
+ class OrderTitle(ValueObject):
10
+ value: str = Field(min_length=1, max_length=TITLE_MAX_LENGTH)
@@ -0,0 +1,3 @@
1
+ MODULE_NAME = "{{ cookiecutter.module_name }}"
2
+
3
+ TITLE_MAX_LENGTH = 120
@@ -0,0 +1,6 @@
1
+ from enum import Enum
2
+
3
+
4
+ class OrderStatus(str, Enum):
5
+ PENDING = "pending"
6
+ APPROVED = "approved"
@@ -0,0 +1,11 @@
1
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import MODULE_NAME
2
+
3
+ from python_ddd_framework.errors import BusinessErrorDefinition
4
+
5
+ ALREADY_APPROVED = BusinessErrorDefinition(
6
+ f"{MODULE_NAME}.already_approved", "Order is already approved"
7
+ )
8
+
9
+ APPROVAL_DISABLED = BusinessErrorDefinition(
10
+ f"{MODULE_NAME}.approval_disabled", "Order approval is disabled"
11
+ )
@@ -0,0 +1,26 @@
1
+ from uuid import UUID
2
+
3
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import MODULE_NAME
4
+ from modules.{{ cookiecutter.module_name }}.domain_shared.enums.order_status import OrderStatus
5
+ from pydantic import BaseModel, ConfigDict
6
+
7
+ from python_ddd_framework.realtime import RealtimeMessage
8
+
9
+
10
+ class OrderChangedMessage(RealtimeMessage):
11
+ message_type = f"{MODULE_NAME}.changed.v1"
12
+ id: UUID
13
+ status: OrderStatus
14
+
15
+
16
+ class OrderSnapshotItem(BaseModel):
17
+ model_config = ConfigDict(frozen=True)
18
+ id: UUID
19
+ title: str
20
+ status: OrderStatus
21
+ version: int
22
+
23
+
24
+ class OrderSnapshotMessage(RealtimeMessage):
25
+ message_type = f"{MODULE_NAME}.snapshot.v1"
26
+ orders: tuple[OrderSnapshotItem, ...]
@@ -0,0 +1,13 @@
1
+ """外部集成的可覆盖快照;不承载命令、结果或领域事件。"""
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class OrderObservation:
8
+ source: str
9
+ count: int
10
+
11
+
12
+ def observation_identity(message: OrderObservation) -> str:
13
+ return message.source
@@ -1,7 +1,46 @@
1
1
  """共享层只发布业务常量与权限,不引用基础设施。"""
2
2
 
3
- from python_ddd_framework import AppModule
3
+ from python_ddd_framework import (
4
+ AppModule,
5
+ AuthorizationModule,
6
+ ConfigureContext,
7
+ InitializeContext,
8
+ PostConfigureContext,
9
+ PostInitializeContext,
10
+ PreConfigureContext,
11
+ PreInitializeContext,
12
+ ShutdownContext,
13
+ )
4
14
 
5
15
 
6
16
  class {{ cookiecutter.class_prefix }}DomainSharedModule(AppModule):
17
+ dependencies = (AuthorizationModule,)
7
18
  scan_packages = (__package__,)
19
+
20
+ def pre_configure(self, context: PreConfigureContext) -> None:
21
+ # 贡献影响后续注册的 PreOptions;此阶段不启动资源。
22
+ pass
23
+
24
+ def configure(self, context: ConfigureContext) -> None:
25
+ # 注册服务和普通 Options;业务执行留在所属能力。
26
+ pass
27
+
28
+ def post_configure(self, context: PostConfigureContext) -> None:
29
+ # 读取已冻结 PreOptions,补充 Options 或覆盖已有服务注册。
30
+ pass
31
+
32
+ async def pre_initialize(self, context: PreInitializeContext) -> None:
33
+ # 异步初始化前置条件;依赖模块在同一阶段先完成。
34
+ pass
35
+
36
+ async def initialize(self, context: InitializeContext) -> None:
37
+ # 初始化本模块资源;不手工启动框架管理的 Worker 或 HostedService。
38
+ pass
39
+
40
+ async def post_initialize(self, context: PostInitializeContext) -> None:
41
+ # 完成初始化收尾;应用此时尚未对外就绪。
42
+ pass
43
+
44
+ async def shutdown(self, context: ShutdownContext) -> None:
45
+ # 逆依赖关闭本模块拥有的资源;返回前等待清理结束。
46
+ pass
@@ -1,15 +1,19 @@
1
1
  """权限名称由共享定义拥有;Provider 仅发布定义。"""
2
2
 
3
+ from modules.{{ cookiecutter.module_name }}.domain_shared.permissions.order_permissions import (
4
+ ORDERS_READ,
5
+ ORDERS_WRITE,
6
+ )
7
+
3
8
  from python_ddd_framework import (
4
9
  PermissionDefinition,
5
10
  PermissionDefinitionContext,
6
11
  PermissionDefinitionProvider,
7
12
  )
8
13
 
9
- from .definitions import ORDERS_READ, ORDERS_WRITE
10
-
11
14
 
12
15
  class {{ cookiecutter.class_prefix }}PermissionDefinitionProvider(PermissionDefinitionProvider):
13
16
  def define(self, context: PermissionDefinitionContext) -> None:
17
+ # 定义扩展:在这里发布自己的业务权限,名称统一放在共享定义;实际授权检查声明在服务/端点上。
14
18
  context.add(PermissionDefinition(ORDERS_READ, "Read orders"))
15
19
  context.add(PermissionDefinition(ORDERS_WRITE, "Create and approve orders"))
@@ -0,0 +1,7 @@
1
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import MODULE_NAME
2
+
3
+ from python_ddd_framework import PermissionName
4
+
5
+ ORDERS_READ = PermissionName(f"{MODULE_NAME}.read")
6
+
7
+ ORDERS_WRITE = PermissionName(f"{MODULE_NAME}.write")
@@ -0,0 +1 @@
1
+ """跨层共享的不可变领域值;不依赖 Domain 实现或 DTO。"""
@@ -0,0 +1,18 @@
1
+ """Shared immutable monetary value for domain objects and public DTOs."""
2
+
3
+ from decimal import Decimal
4
+
5
+ from pydantic import Field, field_validator
6
+
7
+ from python_ddd_framework import ValueObject
8
+
9
+
10
+ class Money(ValueObject):
11
+ amount: Decimal = Field(ge=0, max_digits=12, decimal_places=2)
12
+ currency: str = Field(default="CNY", pattern=r"^[A-Z]{3}$")
13
+
14
+ @field_validator("amount")
15
+ @classmethod
16
+ def normalize_amount(cls, value: Decimal) -> Decimal:
17
+ # 与两位小数的持久价格一致,避免写入前后公开 DTO 表达不同。
18
+ return value.quantize(Decimal("0.01"))
@@ -0,0 +1,15 @@
1
+ import logging
2
+ from collections.abc import Awaitable, Callable
3
+
4
+ from python_ddd_framework.fastapi import ActionFilterContext
5
+
6
+
7
+ class ExportFilter:
8
+ async def invoke(
9
+ self, context: ActionFilterContext, next_action: Callable[[], Awaitable[object]]
10
+ ) -> object:
11
+ # 管线扩展:在这里添加端点执行前/后的 HTTP 处理;领域规则仍由调用的应用服务/聚合负责。
12
+ # Filter 的作用域只覆盖已绑定参数到响应准备;不假定流传输已经结束。
13
+ result = await next_action()
14
+ logging.getLogger(__name__).info("File response prepared")
15
+ return result
@@ -0,0 +1,5 @@
1
+ from pydantic import BaseModel, ConfigDict
2
+
3
+
4
+ class RefreshOrders(BaseModel):
5
+ model_config = ConfigDict(frozen=True, extra="forbid")
@@ -1,25 +1,103 @@
1
1
  """自动服务暴露与手写路由共用正式认证/REQUEST 管线。"""
2
2
 
3
3
  from dishka import Scope
4
- from python_ddd_framework import AppModule, ConfigureContext
4
+ from modules.{{ cookiecutter.module_name }}.application_contracts.module import (
5
+ {{ cookiecutter.class_prefix }}ApplicationContractsModule,
6
+ )
7
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_approval_service import (
8
+ {{ cookiecutter.class_prefix }}ApprovalApplicationService,
9
+ )
10
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_management_service import (
11
+ {{ cookiecutter.class_prefix }}ManagementApplicationService,
12
+ )
13
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_query_service import (
14
+ {{ cookiecutter.class_prefix }}QueryApplicationService,
15
+ )
16
+ from modules.{{ cookiecutter.module_name }}.http_api.filters.export_filter import ExportFilter
17
+ from modules.{{ cookiecutter.module_name }}.http_api.routers.order_files import router as files_router
18
+ from modules.{{ cookiecutter.module_name }}.http_api.websockets.order_socket import router as realtime_router
19
+
20
+ from python_ddd_framework import (
21
+ AppModule,
22
+ ConfigureContext,
23
+ InitializeContext,
24
+ PostConfigureContext,
25
+ PostInitializeContext,
26
+ PreConfigureContext,
27
+ PreInitializeContext,
28
+ ShutdownContext,
29
+ )
5
30
  from python_ddd_framework.fastapi import (
31
+ ApplicationServiceRouteOverride,
6
32
  FastApiRealtimeModule,
7
33
  contributes_routers,
8
34
  exposes_application_services,
9
35
  )
10
36
 
11
- from ..application_contracts.module import {{ cookiecutter.class_prefix }}ApplicationContractsModule
12
- from .files import ExportFilter
13
- from .files import router as files_router
14
- from .realtime import router as realtime_router
15
-
16
37
 
17
38
  @contributes_routers(files_router, realtime_router)
18
39
  @exposes_application_services(
19
40
  {{ cookiecutter.class_prefix }}ApplicationContractsModule,
41
+ overrides=(
42
+ ApplicationServiceRouteOverride(
43
+ service={{ cookiecutter.class_prefix }}QueryApplicationService,
44
+ method={{ cookiecutter.class_prefix }}QueryApplicationService.get,
45
+ path="{{ cookiecutter.http_service_path }}/{id}",
46
+ operation_id="{{ cookiecutter.http_operation_prefix }}get",
47
+ ),
48
+ ApplicationServiceRouteOverride(
49
+ service={{ cookiecutter.class_prefix }}QueryApplicationService,
50
+ method={{ cookiecutter.class_prefix }}QueryApplicationService.get_list,
51
+ path="{{ cookiecutter.http_service_path }}",
52
+ operation_id="{{ cookiecutter.http_operation_prefix }}get_list",
53
+ ),
54
+ ApplicationServiceRouteOverride(
55
+ service={{ cookiecutter.class_prefix }}ManagementApplicationService,
56
+ method={{ cookiecutter.class_prefix }}ManagementApplicationService.create,
57
+ path="{{ cookiecutter.http_service_path }}",
58
+ operation_id="{{ cookiecutter.http_operation_prefix }}create",
59
+ ),
60
+ ApplicationServiceRouteOverride(
61
+ service={{ cookiecutter.class_prefix }}ApprovalApplicationService,
62
+ method={{ cookiecutter.class_prefix }}ApprovalApplicationService.approve,
63
+ path="{{ cookiecutter.http_service_path }}/{id}/approve",
64
+ operation_id="{{ cookiecutter.http_operation_prefix }}approve",
65
+ ),
66
+ ApplicationServiceRouteOverride(
67
+ service={{ cookiecutter.class_prefix }}ApprovalApplicationService,
68
+ method={{ cookiecutter.class_prefix }}ApprovalApplicationService.queue_approval,
69
+ path="{{ cookiecutter.http_service_path }}/{id}/queue-approval",
70
+ operation_id="{{ cookiecutter.http_operation_prefix }}queue_approval",
71
+ ),
72
+ ),
20
73
  )
21
74
  class {{ cookiecutter.class_prefix }}HttpApiModule(AppModule):
22
- dependencies = ({{ cookiecutter.class_prefix }}ApplicationContractsModule, FastApiRealtimeModule)
75
+ dependencies = (FastApiRealtimeModule,)
76
+
77
+ def pre_configure(self, context: PreConfigureContext) -> None:
78
+ # 贡献影响后续注册的 PreOptions;此阶段不启动资源。
79
+ pass
23
80
 
24
81
  def configure(self, context: ConfigureContext) -> None:
82
+ # 组装扩展:在这里注册 HTTP 层自己的 Filter 等依赖;新路由通过上方 Module 声明接入。
25
83
  context.services.add_scoped(ExportFilter, scope=Scope.REQUEST)
84
+
85
+ def post_configure(self, context: PostConfigureContext) -> None:
86
+ # 读取已冻结 PreOptions,补充 Options 或覆盖已有服务注册。
87
+ pass
88
+
89
+ async def pre_initialize(self, context: PreInitializeContext) -> None:
90
+ # 异步初始化前置条件;依赖模块在同一阶段先完成。
91
+ pass
92
+
93
+ async def initialize(self, context: InitializeContext) -> None:
94
+ # 初始化本模块资源;不手工启动框架管理的 Worker 或 HostedService。
95
+ pass
96
+
97
+ async def post_initialize(self, context: PostInitializeContext) -> None:
98
+ # 完成初始化收尾;应用此时尚未对外就绪。
99
+ pass
100
+
101
+ async def shutdown(self, context: ShutdownContext) -> None:
102
+ # 逆依赖关闭本模块拥有的资源;返回前等待清理结束。
103
+ pass
@@ -1,40 +1,40 @@
1
- """原生上传与流响应;业务查询在返回响应前完成,不跨网络发送持有事务。"""
2
-
3
1
  import asyncio
4
2
  import hashlib
5
3
  import logging
6
- from collections.abc import AsyncIterator, Awaitable, Callable
4
+ from collections.abc import AsyncIterator
7
5
  from typing import Annotated
8
6
  from uuid import UUID
9
7
 
10
8
  from fastapi import File, Form, UploadFile
11
- from python_ddd_framework.authorization import authorize
12
- from python_ddd_framework.fastapi import ActionFilterContext, HttpRouter, service_filter
9
+ from modules.{{ cookiecutter.module_name }}.application_contracts.inputs.create_order import CreateOrder
10
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_management_service import (
11
+ {{ cookiecutter.class_prefix }}ManagementApplicationService,
12
+ )
13
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_query_service import (
14
+ {{ cookiecutter.class_prefix }}QueryApplicationService,
15
+ )
16
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import MODULE_NAME
17
+ from modules.{{ cookiecutter.module_name }}.domain_shared.permissions.order_permissions import (
18
+ ORDERS_READ,
19
+ ORDERS_WRITE,
20
+ )
21
+ from modules.{{ cookiecutter.module_name }}.http_api.filters.export_filter import ExportFilter
13
22
  from starlette.responses import Response, StreamingResponse
14
23
 
15
- from ..application_contracts.orders import CreateOrder, {{ cookiecutter.class_prefix }}ApplicationService
16
- from ..domain_shared.definitions import MODULE_NAME, ORDERS_READ, ORDERS_WRITE
24
+ from python_ddd_framework.authorization import authorize
25
+ from python_ddd_framework.fastapi import HttpRouter, service_filter
17
26
 
18
27
  router = HttpRouter(use_api_prefix=True, prefix=f"/{MODULE_NAME}/files", permissions=(ORDERS_READ,))
19
28
 
20
29
 
21
- class ExportFilter:
22
- async def invoke(
23
- self, context: ActionFilterContext, next_action: Callable[[], Awaitable[object]]
24
- ) -> object:
25
- # Filter 的作用域只覆盖已绑定参数到响应准备;不假定流传输已经结束。
26
- result = await next_action()
27
- logging.getLogger(__name__).info("File response prepared")
28
- return result
29
-
30
-
31
30
  @router.post("/upload", status_code=201)
32
31
  @authorize(ORDERS_WRITE)
33
32
  async def upload(
34
- service: {{ cookiecutter.class_prefix }}ApplicationService,
33
+ service: {{ cookiecutter.class_prefix }}ManagementApplicationService,
35
34
  title: Annotated[str, Form()],
36
35
  file: Annotated[UploadFile, File()],
37
36
  ) -> dict[str, object]:
37
+ # 接口扩展:在这里解析上传内容并组装业务命令,交给应用服务处理;当前只演示创建订单及计算摘要。
38
38
  # File/Form 使用框架读取期限额及原生 parser;UploadFile 资源由 REQUEST 关闭。
39
39
  content = await file.read()
40
40
  order = await service.create(CreateOrder(title=title))
@@ -47,7 +47,8 @@ async def upload(
47
47
 
48
48
  @router.get("/{id}/download")
49
49
  @service_filter(ExportFilter)
50
- async def download(id: UUID, service: {{ cookiecutter.class_prefix }}ApplicationService) -> Response:
50
+ async def download(id: UUID, service: {{ cookiecutter.class_prefix }}QueryApplicationService) -> Response:
51
+ # 接口扩展:通过服务查询已授权的数据后,在这里转换为实际下载格式、文件名和媒体类型。
51
52
  order = await service.get(id)
52
53
  return Response(
53
54
  order.model_dump_json(),
@@ -58,10 +59,12 @@ async def download(id: UUID, service: {{ cookiecutter.class_prefix }}Application
58
59
 
59
60
  @router.get("/export/stream")
60
61
  @service_filter(ExportFilter)
61
- async def export(service: {{ cookiecutter.class_prefix }}ApplicationService) -> StreamingResponse:
62
+ async def export(service: {{ cookiecutter.class_prefix }}QueryApplicationService) -> StreamingResponse:
63
+ # 接口扩展:这里准备导出数据,下面的 producer 负责编码输出;不要让业务事务跨越网络发送。
62
64
  orders = await service.get_list()
63
65
 
64
66
  async def produce() -> AsyncIterator[bytes]:
67
+ # 输出扩展:将 JSON 行替换为实际流格式;大数据分批读取须各自完成短 UoW 后再 yield。
65
68
  try:
66
69
  for order in orders:
67
70
  yield (order.model_dump_json() + "\n").encode()
@@ -0,0 +1,44 @@
1
+ from typing import Annotated
2
+
3
+ from fastapi import APIRouter, Depends
4
+ from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_query_service import (
5
+ {{ cookiecutter.class_prefix }}QueryApplicationService,
6
+ )
7
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import MODULE_NAME
8
+ from modules.{{ cookiecutter.module_name }}.domain_shared.messages.order_messages import (
9
+ OrderSnapshotItem,
10
+ OrderSnapshotMessage,
11
+ )
12
+ from modules.{{ cookiecutter.module_name }}.http_api.models.refresh_orders import RefreshOrders
13
+ from starlette.websockets import WebSocketDisconnect
14
+
15
+ from python_ddd_framework import FrameworkError
16
+ from python_ddd_framework.fastapi import WebSocketConnection, websocket_connection
17
+
18
+ router = APIRouter()
19
+
20
+
21
+ @router.websocket(f"/ws/{MODULE_NAME}")
22
+ async def orders_socket(
23
+ connection: Annotated[WebSocketConnection, Depends(websocket_connection)],
24
+ ) -> None:
25
+ # 接口扩展:这里定义“查询快照 → 发送消息 → 接收下一条命令”的交互,替换消息 DTO 和业务调用。
26
+ # 每次业务操作仍经 connection.invoke 进入授权/事务管线,不在连接期间保留 Session 或聚合实例。
27
+ try:
28
+ while True:
29
+ orders = await connection.invoke({{ cookiecutter.class_prefix }}QueryApplicationService.get_list)
30
+ await connection.send(
31
+ OrderSnapshotMessage(
32
+ orders=tuple(
33
+ OrderSnapshotItem(
34
+ id=item.id, title=item.title, status=item.status, version=item.version
35
+ )
36
+ for item in orders
37
+ )
38
+ ),
39
+ )
40
+ await connection.receive(RefreshOrders)
41
+ except WebSocketDisconnect:
42
+ return
43
+ except FrameworkError as error:
44
+ await connection.send_problem(error)
@@ -1 +1 @@
1
- """初始无业务 revision;执行 pddd db revision --module {{ cookiecutter.module_name }} 后人工审查。"""
1
+ """初始无业务 revision;执行 pddd db revision --module orders 后人工审查。"""
@@ -1,7 +1 @@
1
- """本模块模型的原生 MetaData owner;具体模型由 Module 的包声明加载。"""
2
-
3
- from sqlalchemy.orm import DeclarativeBase
4
-
5
-
6
- class {{ cookiecutter.class_prefix }}Base(DeclarativeBase):
7
- pass
1
+ """ORM 模型包;metadata owner 位于 base。"""
@@ -0,0 +1,7 @@
1
+ """Native metadata owner for models discovered by this module."""
2
+
3
+ from sqlalchemy.orm import DeclarativeBase
4
+
5
+
6
+ class {{ cookiecutter.class_prefix }}Base(DeclarativeBase):
7
+ pass
@@ -2,12 +2,16 @@
2
2
 
3
3
  from uuid import UUID
4
4
 
5
- from python_ddd_framework.sqlalchemy import AuditedEntityMixin
5
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import (
6
+ MODULE_NAME,
7
+ TITLE_MAX_LENGTH,
8
+ )
9
+ from modules.{{ cookiecutter.module_name }}.domain_shared.enums.order_status import OrderStatus
10
+ from modules.{{ cookiecutter.module_name }}.sqlalchemy.models.base import {{ cookiecutter.class_prefix }}Base
6
11
  from sqlalchemy import Integer, String, Uuid
7
12
  from sqlalchemy.orm import Mapped, mapped_column
8
13
 
9
- from ...domain_shared.definitions import MODULE_NAME, TITLE_MAX_LENGTH, OrderStatus
10
- from . import {{ cookiecutter.class_prefix }}Base
14
+ from python_ddd_framework.sqlalchemy import AuditedEntityMixin
11
15
 
12
16
 
13
17
  class OrderRow(AuditedEntityMixin, {{ cookiecutter.class_prefix }}Base):
@@ -1,16 +1,25 @@
1
1
  """provider 依赖领域契约;Host 显式选择此实现。"""
2
2
 
3
- from python_ddd_framework import AppModule
3
+ from modules.{{ cookiecutter.module_name }}.domain.module import {{ cookiecutter.class_prefix }}DomainModule
4
+ from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import MODULE_NAME
5
+ from modules.{{ cookiecutter.module_name }}.sqlalchemy.models.base import {{ cookiecutter.class_prefix }}Base
6
+
7
+ from python_ddd_framework import (
8
+ AppModule,
9
+ ConfigureContext,
10
+ InitializeContext,
11
+ PostConfigureContext,
12
+ PostInitializeContext,
13
+ PreConfigureContext,
14
+ PreInitializeContext,
15
+ ShutdownContext,
16
+ )
4
17
  from python_ddd_framework.sqlalchemy import (
5
18
  SqlAlchemyModelRegistration,
6
19
  SqlAlchemyPersistenceModule,
7
20
  contributes_sqlalchemy_models,
8
21
  )
9
22
 
10
- from ..domain.module import {{ cookiecutter.class_prefix }}DomainModule
11
- from ..domain_shared.definitions import MODULE_NAME
12
- from .models import {{ cookiecutter.class_prefix }}Base
13
-
14
23
 
15
24
  @contributes_sqlalchemy_models(
16
25
  SqlAlchemyModelRegistration.from_package(
@@ -24,3 +33,31 @@ class {{ cookiecutter.class_prefix }}SqlAlchemyModule(AppModule):
24
33
  dependencies = ({{ cookiecutter.class_prefix }}DomainModule, SqlAlchemyPersistenceModule)
25
34
 
26
35
  scan_packages = (__package__ + ".repositories",)
36
+
37
+ def pre_configure(self, context: PreConfigureContext) -> None:
38
+ # 贡献影响后续注册的 PreOptions;此阶段不启动资源。
39
+ pass
40
+
41
+ def configure(self, context: ConfigureContext) -> None:
42
+ # 注册服务和普通 Options;业务执行留在所属能力。
43
+ pass
44
+
45
+ def post_configure(self, context: PostConfigureContext) -> None:
46
+ # 读取已冻结 PreOptions,补充 Options 或覆盖已有服务注册。
47
+ pass
48
+
49
+ async def pre_initialize(self, context: PreInitializeContext) -> None:
50
+ # 异步初始化前置条件;依赖模块在同一阶段先完成。
51
+ pass
52
+
53
+ async def initialize(self, context: InitializeContext) -> None:
54
+ # 初始化本模块资源;不手工启动框架管理的 Worker 或 HostedService。
55
+ pass
56
+
57
+ async def post_initialize(self, context: PostInitializeContext) -> None:
58
+ # 完成初始化收尾;应用此时尚未对外就绪。
59
+ pass
60
+
61
+ async def shutdown(self, context: ShutdownContext) -> None:
62
+ # 逆依赖关闭本模块拥有的资源;返回前等待清理结束。
63
+ pass