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
@@ -1,9 +1,4 @@
1
- from ..invocation import ApplicationInvocationContext
2
- from .catalog import (
3
- ApplicationServiceCatalog,
4
- ApplicationServiceMethod,
5
- ApplicationServiceRegistration,
6
- )
1
+ from .catalog import ApplicationServiceCatalog, ApplicationServiceRegistration
7
2
  from .contracts import (
8
3
  ApplicationService,
9
4
  ApplicationServiceContract,
@@ -20,35 +15,24 @@ from .errors import (
20
15
  CrossTaskInvocationError,
21
16
  NestedConnectionError,
22
17
  )
23
- from .execution import MethodInvocation
24
- from .interceptors import InterceptionStage, Interceptor, InterceptorRegistration
25
- from .policies import disable_validation, enable_validation
26
- from .seeding import DataSeedContributor, DataSeeder, DataSeedError
18
+ from .pagination import PagedAndSortedResultRequestDto, PagedResult, PagedResultRequestDto
27
19
 
28
20
  __all__ = (
29
21
  "ApplicationInvocationCleanupError",
30
- "ApplicationInvocationContext",
31
22
  "ApplicationInvocationRejectedError",
32
23
  "ApplicationService",
33
24
  "ApplicationServiceCatalog",
34
25
  "ApplicationServiceContract",
35
26
  "ApplicationServiceDefinitionError",
36
- "ApplicationServiceMethod",
37
27
  "ApplicationServiceMethodError",
38
28
  "ApplicationServiceRegistration",
39
29
  "ApplicationServiceValidationError",
40
30
  "ApplicationServiceValidationIssue",
41
31
  "CrossTaskInvocationError",
42
- "DataSeedContributor",
43
- "DataSeedError",
44
- "DataSeeder",
45
32
  "IntegrationService",
46
- "InterceptionStage",
47
- "Interceptor",
48
- "InterceptorRegistration",
49
- "MethodInvocation",
50
33
  "NestedConnectionError",
34
+ "PagedAndSortedResultRequestDto",
35
+ "PagedResult",
36
+ "PagedResultRequestDto",
51
37
  "application_service",
52
- "disable_validation",
53
- "enable_validation",
54
38
  )
@@ -2,7 +2,9 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from collections.abc import Mapping
5
6
  from dataclasses import dataclass
7
+ from typing import get_origin
6
8
 
7
9
  from dishka import (
8
10
  DEFAULT_COMPONENT,
@@ -11,24 +13,26 @@ from dishka import (
11
13
  Scope,
12
14
  )
13
15
 
14
- from ..application_services import ApplicationService
15
- from ..application_services.catalog import (
16
- _APPLICATION_SERVICE_COMPONENT,
17
- _ApplicationServiceBinding,
18
- _DiscoveredApplicationService,
19
- )
20
- from ..application_services.errors import ApplicationServiceDefinitionError
21
16
  from ..diagnostics import SourceLocation
22
17
  from ..modularity import ModuleRegistry
23
- from .arbitration import (
18
+ from ..services.arbitration import (
24
19
  _ArbitrationResult,
25
20
  _provider_with_active_sources,
26
21
  _RegistrationDraft,
27
22
  )
28
- from .registration import (
23
+ from ..services.native_graph import _NativeServiceGraph
24
+ from ..services.registration import (
29
25
  RegistrationDecision,
30
26
  RegistrationKind,
31
27
  )
28
+ from .catalog import (
29
+ _APPLICATION_SERVICE_COMPONENT,
30
+ ApplicationServiceCatalog,
31
+ _ApplicationServiceBinding,
32
+ _DiscoveredApplicationService,
33
+ )
34
+ from .contracts import ApplicationService
35
+ from .errors import ApplicationServiceDefinitionError
32
36
 
33
37
 
34
38
  @dataclass(frozen=True, slots=True)
@@ -160,6 +164,45 @@ def _validate_application_service_dependencies(
160
164
  )
161
165
 
162
166
 
167
+ def _validate_application_service_bindings(
168
+ native_graph: _NativeServiceGraph,
169
+ catalog: ApplicationServiceCatalog,
170
+ proxy_factories: Mapping[DependencyKey, object],
171
+ ) -> None:
172
+ implementations = {
173
+ DependencyKey(item.contract, _APPLICATION_SERVICE_COMPONENT): item.implementation
174
+ for item in catalog.registrations
175
+ }
176
+ for row in native_graph:
177
+ factory = native_graph.effective_factory(row)
178
+ candidates = (get_origin(row.key.type_hint) or row.key.type_hint, factory.source)
179
+ is_implementation = any(
180
+ isinstance(candidate, type) and issubclass(candidate, ApplicationService)
181
+ for candidate in candidates
182
+ )
183
+ is_proxy = any(factory.source is source for source in proxy_factories.values())
184
+ if not (is_implementation or is_proxy or row.key in proxy_factories):
185
+ continue
186
+ expected_source = implementations.get(row.key) or proxy_factories.get(row.key)
187
+ expected_scope = Scope.ACTION if row.key in implementations else Scope.REQUEST
188
+ # scope 相同不代表经过管线;必须证明是本次最终实现或 owner 实际生成的代理。
189
+ # 沿 alias 识别角色,但 alias 不能额外发布私有实现或冒充正式公开身份。
190
+ if (
191
+ expected_source is None
192
+ or row.factory.source is not expected_source
193
+ or row.factory is not factory
194
+ or row.scope is not expected_scope
195
+ or not factory.cache
196
+ ):
197
+ raise ApplicationServiceDefinitionError(
198
+ symbol=str(row.key.type_hint),
199
+ reason=(
200
+ "ApplicationService must use its managed proxy "
201
+ "or private implementation binding"
202
+ ),
203
+ )
204
+
205
+
163
206
  def _runtime_providers(
164
207
  arbitration: _ArbitrationResult,
165
208
  services: tuple[_BoundApplicationService, ...],
@@ -3,58 +3,35 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import inspect
6
- from collections.abc import Awaitable, Callable, Mapping
7
- from dataclasses import dataclass, field, replace
6
+ from collections.abc import Mapping
7
+ from dataclasses import dataclass
8
8
  from types import MappingProxyType
9
- from typing import Any, cast, get_type_hints
10
9
 
11
- from pydantic import ConfigDict, validate_call
12
-
13
- from ..auditing.control import _AUDITING_ATTRIBUTE
14
- from ..authorization import PermissionCatalog, PermissionName
15
- from ..authorization.contracts import (
16
- _ALLOW_ANONYMOUS_ATTRIBUTE,
17
- _AUTHORIZATION_ATTRIBUTE,
18
- _authorization_requirement,
19
- _AuthorizationRequirement,
20
- )
21
- from ..authorization.errors import AuthorizationDefinitionError
10
+ from ..authorization import PermissionCatalog
22
11
  from ..diagnostics import SourceLocation
12
+ from ..extensions.catalog import ExtensionCatalog
13
+ from ..invocation.interceptors import _InterceptorRule
14
+ from ..invocation.methods import (
15
+ _POLICY_ATTRIBUTES,
16
+ ServiceMethod,
17
+ _compile_method,
18
+ _CompiledMethod,
19
+ _public_method_names,
20
+ )
23
21
  from ..modularity import ModuleKey
24
- from ..unit_of_work.options import _UNIT_OF_WORK_ATTRIBUTE, UnitOfWorkDeclaration, _declaration
22
+ from ..unit_of_work.options import UnitOfWorkDeclaration
25
23
  from .contracts import (
26
24
  ApplicationService,
27
25
  ApplicationServiceContract,
28
26
  IntegrationService,
29
27
  _ApplicationServiceDefinition,
30
28
  _inherited_contracts,
31
- _is_internal_contributor,
32
29
  )
33
30
  from .errors import ApplicationServiceDefinitionError, ApplicationServiceMethodError
34
- from .interceptors import InterceptionStage, InterceptorRegistration, _InterceptorRule
35
- from .policies import _VALIDATION_ATTRIBUTE, _effective_switch
36
- from .signature import _business_signature
37
31
 
38
32
  _APPLICATION_SERVICE_COMPONENT = "__python_ddd_framework_application_service_implementation__"
39
33
 
40
34
 
41
- @dataclass(frozen=True, slots=True)
42
- class ApplicationServiceMethod:
43
- name: str
44
- source: SourceLocation
45
- requires_authenticated_user: bool
46
- permissions: tuple[PermissionName, ...]
47
- allows_anonymous: bool
48
- service: type[object]
49
- implementation: type[object]
50
- method: Callable[..., Awaitable[object]]
51
- validation_enabled: bool
52
- auditing_enabled: bool
53
- policy_sources: tuple[SourceLocation, ...]
54
- interceptors: tuple[InterceptorRegistration, ...] = ()
55
- unit_of_work: UnitOfWorkDeclaration = field(default_factory=UnitOfWorkDeclaration)
56
-
57
-
58
35
  @dataclass(frozen=True, slots=True)
59
36
  class ApplicationServiceRegistration:
60
37
  contract: type[object]
@@ -66,8 +43,7 @@ class ApplicationServiceRegistration:
66
43
  implementation_source: SourceLocation
67
44
  replaced_sources: tuple[SourceLocation, ...]
68
45
  declaration_index: int
69
- methods: tuple[ApplicationServiceMethod, ...]
70
- is_public_service: bool
46
+ methods: tuple[ServiceMethod, ...]
71
47
  has_contract: bool
72
48
  is_integration_service: bool
73
49
 
@@ -90,22 +66,6 @@ class _ApplicationServiceBinding:
90
66
  replaced_sources: tuple[SourceLocation, ...]
91
67
 
92
68
 
93
- _ValidatedArguments = tuple[tuple[object, ...], dict[str, object]]
94
-
95
-
96
- @dataclass(frozen=True, slots=True)
97
- class _CompiledMethod:
98
- name: str
99
- contract_method: Callable[..., Awaitable[object]]
100
- implementation_method: Callable[..., Awaitable[object]]
101
- validator: Callable[..., Awaitable[_ValidatedArguments]]
102
- parameter_names: frozenset[str]
103
- authorization: _AuthorizationRequirement
104
- descriptor: ApplicationServiceMethod
105
- signature: inspect.Signature
106
- interceptor_steps: Mapping[InterceptionStage, tuple[InterceptorRegistration, ...]]
107
-
108
-
109
69
  @dataclass(frozen=True, slots=True)
110
70
  class _CompiledApplicationService:
111
71
  registration: ApplicationServiceRegistration
@@ -120,15 +80,14 @@ class ApplicationServiceCatalog:
120
80
  def __init__(self, services: tuple[_CompiledApplicationService, ...]) -> None:
121
81
  self._compiled = services
122
82
  self._registrations = tuple(item.registration for item in services)
123
- public_services = tuple(item for item in services if item.registration.is_public_service)
124
83
  self._by_contract: Mapping[type[object], _CompiledApplicationService] = MappingProxyType(
125
- {item.registration.contract: item for item in public_services}
84
+ {item.registration.contract: item for item in services}
126
85
  )
127
86
  methods_by_identity: dict[
128
87
  object,
129
88
  tuple[_CompiledApplicationService, _CompiledMethod],
130
89
  ] = {}
131
- for service in public_services:
90
+ for service in services:
132
91
  for method in service.methods.values():
133
92
  previous = methods_by_identity.get(method.contract_method)
134
93
  if previous is not None:
@@ -181,17 +140,22 @@ class ApplicationServiceCatalog:
181
140
  def _build_application_service_catalog(
182
141
  bindings: tuple[_ApplicationServiceBinding, ...],
183
142
  permission_catalog: PermissionCatalog,
184
- interceptor_rules: tuple[_InterceptorRule, ...] = (),
143
+ interceptor_rules: tuple[_InterceptorRule, ...],
144
+ extensions: ExtensionCatalog,
185
145
  ) -> ApplicationServiceCatalog:
186
146
  return ApplicationServiceCatalog(
187
- tuple(_compile_service(item, permission_catalog, interceptor_rules) for item in bindings)
147
+ tuple(
148
+ _compile_service(item, permission_catalog, interceptor_rules, extensions)
149
+ for item in bindings
150
+ )
188
151
  )
189
152
 
190
153
 
191
154
  def _compile_service(
192
155
  binding: _ApplicationServiceBinding,
193
156
  permission_catalog: PermissionCatalog,
194
- interceptor_rules: tuple[_InterceptorRule, ...] = (),
157
+ interceptor_rules: tuple[_InterceptorRule, ...],
158
+ extensions: ExtensionCatalog,
195
159
  ) -> _CompiledApplicationService:
196
160
  """把 contract declaration 与仲裁胜出的 implementation 编译成唯一调用事实。"""
197
161
 
@@ -199,7 +163,6 @@ def _compile_service(
199
163
  implementation = binding.implementation
200
164
  contract: type[object] = discovery.definition.contract or discovery.service_type
201
165
  has_contract = discovery.definition.contract is not None
202
- is_public_service = not _is_internal_contributor(implementation)
203
166
  is_integration = issubclass(implementation, IntegrationService)
204
167
  _validate_policy_locations(contract if has_contract else None, implementation, is_integration)
205
168
  if is_integration and not has_contract:
@@ -218,92 +181,22 @@ def _compile_service(
218
181
  contract_methods = _public_method_names(contract, is_contract=has_contract)
219
182
 
220
183
  compiled_methods: dict[str, _CompiledMethod] = {}
221
- public_methods: list[ApplicationServiceMethod] = []
184
+ public_methods: list[ServiceMethod] = []
222
185
  for name in contract_methods:
223
- contract_method = _require_async_instance_method(contract, name, role="contract")
224
- implementation_method = _require_async_instance_method(
186
+ method = _compile_method(
187
+ contract,
225
188
  implementation,
226
189
  name,
227
- role="implementation",
190
+ discovery.owner,
191
+ binding.implementation_owner,
192
+ permission_catalog,
193
+ interceptor_rules,
194
+ auditing_default=not is_integration,
195
+ unit_of_work_default=UnitOfWorkDeclaration(),
196
+ extensions=extensions,
228
197
  )
229
- contract_signature, annotations = _resolved_signature(contract_method)
230
- implementation_signature, _ = _resolved_signature(implementation_method)
231
- if contract_signature.return_annotation is inspect.Signature.empty:
232
- raise ApplicationServiceDefinitionError(
233
- symbol=f"{implementation_symbol}.{name}",
234
- reason="public service methods must declare a return annotation",
235
- )
236
- if _business_signature(contract_signature) != _business_signature(implementation_signature):
237
- raise ApplicationServiceDefinitionError(
238
- symbol=f"{implementation_symbol}.{name}",
239
- reason="implementation signature must exactly match its public contract",
240
- )
241
- try:
242
- validator = _compile_argument_validator(
243
- contract_method,
244
- contract_signature,
245
- annotations,
246
- )
247
- except Exception as original_error:
248
- raise ApplicationServiceDefinitionError(
249
- symbol=f"{contract.__module__}.{contract.__qualname__}.{name}",
250
- reason=f"Pydantic could not compile the method: {type(original_error).__name__}",
251
- ) from None
252
- source = _method_source(contract_method, discovery.owner)
253
- authorization = _authorization_requirement(implementation, implementation_method)
254
- unknown_permissions = tuple(
255
- permission
256
- for permission in authorization.permissions
257
- if permission not in permission_catalog
258
- )
259
- if unknown_permissions:
260
- raise AuthorizationDefinitionError(
261
- symbol=f"{contract.__module__}.{contract.__qualname__}.{name}",
262
- reason=(
263
- "authorization references unknown permissions: "
264
- f"{tuple(str(item) for item in unknown_permissions)!r}"
265
- ),
266
- )
267
- descriptor = ApplicationServiceMethod(
268
- name=name,
269
- source=source,
270
- requires_authenticated_user=authorization.requires_authenticated_user,
271
- permissions=authorization.permissions,
272
- allows_anonymous=authorization.allows_anonymous,
273
- service=contract,
274
- implementation=implementation,
275
- method=implementation_method,
276
- unit_of_work=_declaration(implementation, implementation_method),
277
- validation_enabled=_effective_switch(
278
- implementation, implementation_method, _VALIDATION_ATTRIBUTE, True
279
- ),
280
- auditing_enabled=_effective_switch(
281
- implementation, implementation_method, _AUDITING_ATTRIBUTE, not is_integration
282
- ),
283
- policy_sources=_policy_sources(
284
- implementation, implementation_method, binding.implementation_owner
285
- ),
286
- )
287
- descriptor = replace(
288
- descriptor, interceptors=_match_interceptors(descriptor, interceptor_rules)
289
- )
290
- compiled_methods[name] = _CompiledMethod(
291
- name=name,
292
- contract_method=contract_method,
293
- implementation_method=implementation_method,
294
- validator=validator,
295
- parameter_names=frozenset(tuple(contract_signature.parameters)[1:]),
296
- authorization=authorization,
297
- descriptor=descriptor,
298
- signature=contract_signature,
299
- interceptor_steps=MappingProxyType(
300
- {
301
- stage: tuple(item for item in descriptor.interceptors if item.before is stage)
302
- for stage in InterceptionStage
303
- }
304
- ),
305
- )
306
- public_methods.append(descriptor)
198
+ compiled_methods[name] = method
199
+ public_methods.append(method.descriptor)
307
200
 
308
201
  if not public_methods:
309
202
  raise ApplicationServiceDefinitionError(
@@ -321,7 +214,6 @@ def _compile_service(
321
214
  replaced_sources=binding.replaced_sources,
322
215
  declaration_index=discovery.declaration_index,
323
216
  methods=tuple(public_methods),
324
- is_public_service=is_public_service,
325
217
  has_contract=has_contract,
326
218
  is_integration_service=is_integration,
327
219
  )
@@ -331,15 +223,6 @@ def _compile_service(
331
223
  )
332
224
 
333
225
 
334
- _POLICY_ATTRIBUTES = (
335
- _UNIT_OF_WORK_ATTRIBUTE,
336
- _AUTHORIZATION_ATTRIBUTE,
337
- _ALLOW_ANONYMOUS_ATTRIBUTE,
338
- _VALIDATION_ATTRIBUTE,
339
- _AUDITING_ATTRIBUTE,
340
- )
341
-
342
-
343
226
  def _validate_policy_locations(
344
227
  contract: type[object] | None, implementation: type[object], integration: bool
345
228
  ) -> None:
@@ -363,138 +246,3 @@ def _validate_policy_locations(
363
246
  if root in contracts
364
247
  else "IntegrationService cannot declare user authorization",
365
248
  )
366
-
367
-
368
- def _policy_sources(
369
- implementation: type[object], method: Callable[..., Any], owner: ModuleKey
370
- ) -> tuple[SourceLocation, ...]:
371
- return tuple(
372
- _method_source(target, owner)
373
- for target in (*reversed(implementation.__mro__), method)
374
- if any(attribute in vars(target) for attribute in _POLICY_ATTRIBUTES)
375
- )
376
-
377
-
378
- def _match_interceptors(
379
- method: ApplicationServiceMethod, rules: tuple[_InterceptorRule, ...]
380
- ) -> tuple[InterceptorRegistration, ...]:
381
- matched: list[InterceptorRegistration] = []
382
- for rule in rules:
383
- try:
384
- accepted = rule.match(method)
385
- if not isinstance(accepted, bool):
386
- raise TypeError("interceptor match must return bool")
387
- except Exception as error:
388
- raise ApplicationServiceDefinitionError(
389
- symbol=rule.registration.source.symbol
390
- or rule.registration.interceptor.__qualname__,
391
- reason=(
392
- f"interceptor match failed for {rule.registration.owner}: "
393
- f"{type(error).__name__}"
394
- ),
395
- ) from None
396
- if accepted:
397
- matched.append(rule.registration)
398
- return tuple(sorted(matched, key=lambda item: tuple(InterceptionStage).index(item.before)))
399
-
400
-
401
- def _public_method_names(candidate: type[object], *, is_contract: bool) -> tuple[str, ...]:
402
- names: dict[str, None] = {}
403
- for owner in reversed(candidate.__mro__):
404
- if owner in (object, ApplicationService, ApplicationServiceContract):
405
- continue
406
- if is_contract and owner.__module__ == "typing":
407
- continue
408
- for name, value in owner.__dict__.items():
409
- if name.startswith("_"):
410
- continue
411
- if inspect.isfunction(value) or isinstance(value, (staticmethod, classmethod)):
412
- names[name] = None
413
- return tuple(names)
414
-
415
-
416
- def _require_async_instance_method(
417
- owner: type[object],
418
- name: str,
419
- *,
420
- role: str,
421
- ) -> Callable[..., Awaitable[object]]:
422
- symbol = f"{owner.__module__}.{owner.__qualname__}.{name}"
423
- descriptor = inspect.getattr_static(owner, name)
424
- if isinstance(descriptor, (staticmethod, classmethod)):
425
- raise ApplicationServiceDefinitionError(
426
- symbol=symbol,
427
- reason=f"public {role} methods must be instance methods",
428
- )
429
- method = getattr(owner, name)
430
- if not inspect.isfunction(method) or not inspect.iscoroutinefunction(method):
431
- raise ApplicationServiceDefinitionError(
432
- symbol=symbol,
433
- reason=f"public {role} methods must use async def",
434
- )
435
- parameters = tuple(inspect.signature(method).parameters.values())
436
- if not parameters or parameters[0].kind not in (
437
- inspect.Parameter.POSITIONAL_ONLY,
438
- inspect.Parameter.POSITIONAL_OR_KEYWORD,
439
- ):
440
- # Catalog 与 HTTP generator 都会剥离 receiver;缺少首个位置参数时不能伪装成实例方法。
441
- raise ApplicationServiceDefinitionError(
442
- symbol=symbol,
443
- reason=f"public {role} methods must declare an instance receiver",
444
- )
445
- return cast(Callable[..., Awaitable[object]], method)
446
-
447
-
448
- def _resolved_signature(
449
- method: Callable[..., Awaitable[object]],
450
- ) -> tuple[inspect.Signature, dict[str, object]]:
451
- annotations = get_type_hints(method, include_extras=True)
452
- signature = inspect.signature(method)
453
- parameters = tuple(
454
- parameter.replace(annotation=annotations.get(name, parameter.annotation))
455
- for name, parameter in signature.parameters.items()
456
- )
457
- resolved = signature.replace(
458
- parameters=parameters,
459
- return_annotation=annotations.get("return", signature.return_annotation),
460
- )
461
- return resolved, annotations
462
-
463
-
464
- def _compile_argument_validator(
465
- method: Callable[..., Awaitable[object]],
466
- signature: inspect.Signature,
467
- annotations: Mapping[str, object],
468
- ) -> Callable[..., Awaitable[_ValidatedArguments]]:
469
- async def capture(*args: object, **kwargs: object) -> _ValidatedArguments:
470
- # 第一个值是 validation-only self sentinel;业务 implementation 尚未解析或执行。
471
- return tuple(args[1:]), dict(kwargs)
472
-
473
- capture.__name__ = method.__name__
474
- capture.__qualname__ = method.__qualname__
475
- cast(Any, capture).__signature__ = signature
476
- capture.__annotations__ = dict(annotations)
477
- validated = validate_call(
478
- config=ConfigDict(
479
- arbitrary_types_allowed=True,
480
- validate_by_name=True,
481
- hide_input_in_errors=True,
482
- )
483
- )(capture)
484
- return cast(Callable[..., Awaitable[_ValidatedArguments]], validated)
485
-
486
-
487
- def _method_source(method: Any, owner: ModuleKey) -> SourceLocation:
488
- file_name: str | None = None
489
- line: int | None = None
490
- try:
491
- file_name = inspect.getsourcefile(method)
492
- _, line = inspect.getsourcelines(method)
493
- except (OSError, TypeError):
494
- pass
495
- return SourceLocation(
496
- owner=str(owner),
497
- file=file_name,
498
- line=line,
499
- symbol=f"{method.__module__}.{method.__qualname__}",
500
- )
@@ -4,8 +4,12 @@ from __future__ import annotations
4
4
 
5
5
  from collections.abc import Callable
6
6
  from dataclasses import dataclass
7
- from typing import TYPE_CHECKING, Protocol, TypeVar
7
+ from typing import TYPE_CHECKING, ClassVar, Protocol, TypeVar
8
8
 
9
+ from dishka import Scope
10
+
11
+ from ..modularity import ModuleRef, ModuleRequirement
12
+ from ..unit_of_work.options import _DEFAULT_CONNECTION_NAME
9
13
  from .errors import ApplicationServiceDefinitionError
10
14
 
11
15
  if TYPE_CHECKING:
@@ -15,7 +19,6 @@ _APPLICATION_SERVICE_DEFINITION_ATTRIBUTE = (
15
19
  "__python_ddd_framework_application_service_definition__"
16
20
  )
17
21
  _TApplicationService = TypeVar("_TApplicationService", bound="ApplicationService")
18
- _DEFAULT_CONNECTION_NAME = "default"
19
22
 
20
23
 
21
24
  class ApplicationServiceContract(Protocol):
@@ -25,6 +28,14 @@ class ApplicationServiceContract(Protocol):
25
28
  class ApplicationService:
26
29
  """业务实现;无独立 Contract 时,以自身类型发布受管理代理。"""
27
30
 
31
+ _fixed_service_scope: ClassVar[Scope] = Scope.ACTION
32
+ _public_service_scope: ClassVar[Scope] = Scope.REQUEST
33
+ _requires_convention: ClassVar[bool] = True
34
+
35
+ __application_services_module__ = ModuleRequirement(
36
+ ModuleRef("python_ddd_framework.application_services.module:ApplicationServicesModule")
37
+ )
38
+
28
39
  _setting_provider: SettingProvider | None = None
29
40
 
30
41
  @property
@@ -39,10 +50,6 @@ class IntegrationService(ApplicationService):
39
50
  """仅供应用内协作的服务;必须声明 Contract,不承载用户授权。"""
40
51
 
41
52
 
42
- class _InternalApplicationServiceContributor(ApplicationService):
43
- """框架内部多贡献者基类;不发布含糊的 single Protocol。"""
44
-
45
-
46
53
  @dataclass(frozen=True, slots=True)
47
54
  class _ApplicationServiceDefinition:
48
55
  contract: type[ApplicationServiceContract] | None
@@ -74,8 +81,7 @@ def application_service(
74
81
  )
75
82
  contract = provides
76
83
  if contract is None:
77
- if not _is_internal_contributor(service_type):
78
- contract = _infer_contract(service_type)
84
+ contract = _infer_contract(service_type)
79
85
  else:
80
86
  _validate_contract(
81
87
  provides,
@@ -100,7 +106,6 @@ def _definition_for(
100
106
  if not issubclass(candidate, ApplicationService) or candidate in (
101
107
  ApplicationService,
102
108
  IntegrationService,
103
- _InternalApplicationServiceContributor,
104
109
  ):
105
110
  return None
106
111
  metadata = candidate.__dict__.get(_APPLICATION_SERVICE_DEFINITION_ATTRIBUTE)
@@ -170,10 +175,6 @@ def _validate_contract(candidate: object, *, symbol: str) -> None:
170
175
  )
171
176
 
172
177
 
173
- def _is_internal_contributor(candidate: type[object]) -> bool:
174
- return issubclass(candidate, _InternalApplicationServiceContributor)
175
-
176
-
177
178
  def _validate_connection_name(connection_name: str) -> str:
178
179
  if not isinstance(connection_name, str) or not connection_name:
179
180
  raise ApplicationServiceDefinitionError(