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.
- python_ddd_framework/__init__.py +139 -21
- python_ddd_framework/application/builder.py +23 -78
- python_ddd_framework/application/composition.py +4 -22
- python_ddd_framework/application/runtime.py +120 -288
- python_ddd_framework/application/state.py +7 -75
- python_ddd_framework/application_services/__init__.py +5 -21
- python_ddd_framework/{services/application_bindings.py → application_services/bindings.py} +52 -9
- python_ddd_framework/application_services/catalog.py +36 -288
- python_ddd_framework/application_services/contracts.py +14 -13
- python_ddd_framework/application_services/dispatcher.py +19 -269
- python_ddd_framework/application_services/invocation.py +197 -15
- python_ddd_framework/application_services/module.py +168 -0
- python_ddd_framework/application_services/pagination.py +21 -0
- python_ddd_framework/auditing/control.py +1 -1
- python_ddd_framework/auditing/module.py +15 -0
- python_ddd_framework/authorization/__init__.py +2 -0
- python_ddd_framework/authorization/contracts.py +7 -2
- python_ddd_framework/authorization/definitions.py +5 -0
- python_ddd_framework/authorization/module.py +40 -0
- python_ddd_framework/authorization/options.py +7 -0
- python_ddd_framework/background_execution/child.py +13 -3
- python_ddd_framework/background_execution/declarations.py +45 -0
- python_ddd_framework/background_execution/lifecycle.py +12 -0
- python_ddd_framework/background_execution/local.py +20 -1
- python_ddd_framework/background_execution/module.py +107 -1
- python_ddd_framework/background_execution/processes.py +11 -1
- python_ddd_framework/background_jobs/catalog.py +59 -42
- python_ddd_framework/background_jobs/contracts.py +16 -19
- python_ddd_framework/background_jobs/pgqueuer/enqueue.py +4 -0
- python_ddd_framework/background_jobs/pgqueuer/module.py +2 -1
- python_ddd_framework/background_jobs/pgqueuer/runtime.py +7 -7
- python_ddd_framework/background_workers/catalog.py +23 -36
- python_ddd_framework/background_workers/contracts.py +3 -0
- python_ddd_framework/background_workers/execution.py +1 -1
- python_ddd_framework/background_workers/runtime.py +47 -1
- python_ddd_framework/caching/__init__.py +7 -6
- python_ddd_framework/caching/contracts.py +65 -67
- python_ddd_framework/caching/errors.py +0 -7
- python_ddd_framework/caching/module.py +18 -0
- python_ddd_framework/caching/naming.py +29 -0
- python_ddd_framework/caching/options.py +38 -0
- python_ddd_framework/caching/unit_of_work.py +46 -0
- python_ddd_framework/cli/__init__.py +31 -6
- python_ddd_framework/cli/project.py +68 -6
- python_ddd_framework/cli/runtime.py +1 -1
- python_ddd_framework/developer_kit/generation.py +75 -22
- python_ddd_framework/developer_kit/project_metadata.py +2 -4
- python_ddd_framework/developer_kit/templates/basic/cookiecutter.json +4 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/README.md +45 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/__init__.py.jinja +0 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/__init__.py.jinja +0 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/contracts/conversion_service.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/module.py.jinja +38 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/__init__.py.jinja +0 -0
- python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/default_conversion_service.py.jinja +10 -0
- python_ddd_framework/developer_kit/templates/module/cookiecutter.json +1 -1
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md +42 -49
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/handler.py.jinja +45 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_approval/payload.py.jinja +8 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/handler.py.jinja +35 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/payload.py.jinja +5 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/order_statistics/schedule.py.jinja +15 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_maintenance_worker.py.jinja +28 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_workers/order_statistics_worker.py.jinja +26 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/order_cache.py.jinja +9 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/caching/statistics_cache.py.jinja +9 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/event_handlers/order_changed_handler.py.jinja +23 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/order_integration_service.py.jinja +71 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/hosted_services/order_observation_handler.py.jinja +16 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration_services/order_reporting_service.py.jinja +16 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/interceptors/order_timing_interceptor.py.jinja +17 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja +105 -10
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/{options.py.jinja → options/order_options.py.jinja} +1 -1
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_approval_service.py.jinja +65 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_management_service.py.jinja +35 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/order_query_service.py.jinja +41 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/approval_setting_observer.py.jinja +17 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/approve_order.py.jinja +6 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/create_order.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/order_reporting_service.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/module.py.jinja +39 -2
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_approval_service.py.jinja +13 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_management_service.py.jinja +10 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/services/order_query_service.py.jinja +12 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_statistics_snapshot.py.jinja +9 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/views/order_view.py.jinja +12 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/entities/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{orders.py.jinja → entities/order.py.jinja} +10 -18
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/events/order_changed.py.jinja +13 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/module.py.jinja +42 -3
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repositories/order_repository.py.jinja +17 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding/order_seed_contributor.py.jinja +22 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/services/order_approval_service.py.jinja +23 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/settings/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/{settings.py.jinja → settings/approval_settings.py.jinja} +3 -2
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/value_objects/order_title.py.jinja +10 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/constants/order_constants.py.jinja +3 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/enums/order_status.py.jinja +6 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/errors/order_errors.py.jinja +11 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_messages.py.jinja +26 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/messages/order_observation.py.jinja +13 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/module.py.jinja +40 -1
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/{permissions.py.jinja → permissions/order_permission_provider.py.jinja} +6 -2
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/permissions/order_permissions.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/value_objects/money.py.jinja +18 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/filters/export_filter.py.jinja +15 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/models/refresh_orders.py.jinja +5 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/module.py.jinja +85 -7
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/routers/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/{files.py.jinja → routers/order_files.py.jinja} +23 -20
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/websockets/order_socket.py.jinja +44 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/migrations/__init__.py.jinja +1 -1
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/__init__.py.jinja +1 -7
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/base.py.jinja +7 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/models/{orders.py.jinja → order_model.py.jinja} +7 -3
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/module.py.jinja +42 -5
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/order_repository.py.jinja +79 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/__init__.py.jinja +1 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/test_domain.py.jinja +4 -3
- python_ddd_framework/developer_kit/templates/project/cookiecutter.json +1 -1
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md +23 -3
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +29 -22
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{app.development.yaml.jinja → backend/app.development.yaml.jinja} +2 -0
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{pyproject.toml.jinja → backend/pyproject.toml.jinja} +1 -1
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/main.py.jinja +2 -0
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/module.py.jinja +40 -3
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md +42 -11
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md +144 -23
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/scripts/.gitkeep +0 -0
- python_ddd_framework/distributed_lock/contracts.py +4 -1
- python_ddd_framework/domain/__init__.py +2 -0
- python_ddd_framework/domain/services.py +11 -0
- python_ddd_framework/errors/business.py +10 -1
- python_ddd_framework/errors/lifecycle.py +3 -31
- python_ddd_framework/events/__init__.py +5 -2
- python_ddd_framework/events/aggregate.py +3 -3
- python_ddd_framework/events/catalog.py +12 -11
- python_ddd_framework/events/contracts.py +6 -1
- python_ddd_framework/events/contribution.py +1 -52
- python_ddd_framework/events/entities.py +23 -0
- python_ddd_framework/events/module.py +97 -0
- python_ddd_framework/events/runtime.py +80 -29
- python_ddd_framework/events/subscription.py +36 -0
- python_ddd_framework/events/unit_of_work.py +25 -0
- python_ddd_framework/extensions/__init__.py +22 -0
- python_ddd_framework/extensions/catalog.py +198 -0
- python_ddd_framework/extensions/contracts.py +85 -0
- python_ddd_framework/extensions/models.py +142 -0
- python_ddd_framework/extensions/module.py +21 -0
- python_ddd_framework/fastapi/__init__.py +2 -0
- python_ddd_framework/fastapi/action.py +1 -2
- python_ddd_framework/fastapi/adapter.py +16 -0
- python_ddd_framework/fastapi/application_services.py +43 -25
- python_ddd_framework/fastapi/background.py +2 -1
- python_ddd_framework/fastapi/contracts.py +26 -8
- python_ddd_framework/fastapi/health.py +1 -1
- python_ddd_framework/fastapi/module.py +14 -0
- python_ddd_framework/fastapi/parameters.py +3 -18
- python_ddd_framework/fastapi/realtime/connection.py +10 -7
- python_ddd_framework/fastapi/realtime/module.py +3 -0
- python_ddd_framework/fastapi/realtime/runtime.py +10 -24
- python_ddd_framework/fastapi/request_context.py +2 -1
- python_ddd_framework/fastapi/routing.py +2 -1
- python_ddd_framework/fastapi/server.py +8 -0
- python_ddd_framework/fastapi/settings.py +2 -1
- python_ddd_framework/hosted_services/bridge.py +95 -17
- python_ddd_framework/hosted_services/catalog.py +6 -16
- python_ddd_framework/hosted_services/contracts.py +9 -1
- python_ddd_framework/hosted_services/declarations.py +15 -0
- python_ddd_framework/hosted_services/module.py +69 -0
- python_ddd_framework/hosted_services/runtime.py +101 -18
- python_ddd_framework/identity/__init__.py +24 -0
- python_ddd_framework/identity/application.py +38 -90
- python_ddd_framework/identity/contracts.py +181 -16
- python_ddd_framework/identity/http_api.py +79 -2
- python_ddd_framework/identity/management.py +166 -0
- python_ddd_framework/identity/module.py +12 -4
- python_ddd_framework/identity/services.py +29 -16
- python_ddd_framework/identity/sqlalchemy/migrations/0003_extra_properties.py +26 -0
- python_ddd_framework/identity/sqlalchemy/migrations/0004_concurrency_version.py +29 -0
- python_ddd_framework/identity/sqlalchemy/models.py +21 -2
- python_ddd_framework/identity/sqlalchemy/module.py +11 -2
- python_ddd_framework/identity/sqlalchemy/repository.py +458 -0
- python_ddd_framework/identity/sqlalchemy/session_security.py +88 -0
- python_ddd_framework/identity/sqlalchemy/stores.py +69 -290
- python_ddd_framework/identity/tokens.py +2 -5
- python_ddd_framework/{application_services/validation.py → invocation/arguments.py} +8 -6
- python_ddd_framework/invocation/contribution.py +61 -0
- python_ddd_framework/invocation/dispatcher.py +291 -0
- python_ddd_framework/invocation/entries.py +2 -25
- python_ddd_framework/invocation/entrypoints.py +126 -0
- python_ddd_framework/{application_services → invocation}/execution.py +8 -5
- python_ddd_framework/invocation/function_runtime.py +3 -1
- python_ddd_framework/invocation/interception.py +113 -42
- python_ddd_framework/{application_services → invocation}/interceptors.py +2 -2
- python_ddd_framework/invocation/managed_proxy.py +116 -0
- python_ddd_framework/invocation/managed_services.py +250 -0
- python_ddd_framework/invocation/methods.py +345 -0
- python_ddd_framework/invocation/module.py +113 -0
- python_ddd_framework/{application_services → invocation}/policies.py +8 -1
- python_ddd_framework/invocation/validation.py +25 -0
- python_ddd_framework/lifecycle/__init__.py +2 -0
- python_ddd_framework/lifecycle/composition.py +5 -2
- python_ddd_framework/lifecycle/participants.py +37 -0
- python_ddd_framework/messaging/__init__.py +25 -0
- python_ddd_framework/messaging/channel.py +232 -0
- python_ddd_framework/messaging/contracts.py +78 -0
- python_ddd_framework/messaging/module.py +114 -0
- python_ddd_framework/messaging/options.py +13 -0
- python_ddd_framework/messaging/receipt.py +27 -0
- python_ddd_framework/messaging/runtime.py +139 -0
- python_ddd_framework/modularity/__init__.py +11 -1
- python_ddd_framework/modularity/contracts.py +47 -16
- python_ddd_framework/modularity/discovery.py +21 -2
- python_ddd_framework/modularity/graph.py +45 -1
- python_ddd_framework/modularity/registry.py +83 -3
- python_ddd_framework/notifications/__init__.py +2 -0
- python_ddd_framework/notifications/catalog.py +3 -2
- python_ddd_framework/notifications/declarations.py +18 -0
- python_ddd_framework/notifications/module.py +21 -0
- python_ddd_framework/observability/logging.py +3 -0
- python_ddd_framework/observability/tracing.py +3 -0
- python_ddd_framework/options/contribution.py +12 -0
- python_ddd_framework/options/registry.py +44 -11
- python_ddd_framework/realtime/__init__.py +2 -1
- python_ddd_framework/realtime/contracts.py +3 -7
- python_ddd_framework/realtime/messages.py +26 -1
- python_ddd_framework/redis/cache.py +243 -0
- python_ddd_framework/redis/cache_scripts.py +40 -0
- python_ddd_framework/redis/distributed_lock.py +10 -2
- python_ddd_framework/redis/module.py +13 -4
- python_ddd_framework/redis/notifications.py +5 -2
- python_ddd_framework/redis/runtime.py +1 -79
- python_ddd_framework/seeding/__init__.py +13 -0
- python_ddd_framework/seeding/contracts.py +45 -0
- python_ddd_framework/seeding/module.py +73 -0
- python_ddd_framework/seeding/runtime.py +63 -0
- python_ddd_framework/services/arbitration.py +23 -0
- python_ddd_framework/services/binding.py +2 -3
- python_ddd_framework/services/composition.py +108 -0
- python_ddd_framework/services/contribution.py +44 -54
- python_ddd_framework/services/convention.py +53 -181
- python_ddd_framework/services/fixed_lifetime.py +12 -15
- python_ddd_framework/services/framework_provider.py +3 -158
- python_ddd_framework/services/native_graph.py +35 -60
- python_ddd_framework/services/provider.py +59 -4
- python_ddd_framework/services/repository.py +6 -0
- python_ddd_framework/services/runtime.py +129 -215
- python_ddd_framework/settings/catalog.py +5 -4
- python_ddd_framework/settings/declarations.py +18 -0
- python_ddd_framework/settings/definitions.py +5 -0
- python_ddd_framework/settings/manager.py +1 -1
- python_ddd_framework/settings/module.py +50 -1
- python_ddd_framework/settings/notifications.py +4 -2
- python_ddd_framework/settings/refresh_module.py +9 -2
- python_ddd_framework/settings/sqlalchemy/module.py +2 -1
- python_ddd_framework/sqlalchemy/__init__.py +12 -0
- python_ddd_framework/sqlalchemy/alembic_runtime/env.py +1 -0
- python_ddd_framework/sqlalchemy/auditing.py +7 -1
- python_ddd_framework/sqlalchemy/extensions.py +186 -0
- python_ddd_framework/sqlalchemy/metadata.py +110 -6
- python_ddd_framework/sqlalchemy/migration.py +61 -8
- python_ddd_framework/sqlalchemy/migration_operations.py +86 -0
- python_ddd_framework/sqlalchemy/module.py +14 -11
- python_ddd_framework/sqlalchemy/module_migration.py +71 -3
- python_ddd_framework/sqlalchemy/repository.py +217 -13
- python_ddd_framework/sqlalchemy/session_provider.py +14 -1
- python_ddd_framework/testing/runtime.py +5 -4
- python_ddd_framework/unit_of_work/__init__.py +2 -1
- python_ddd_framework/unit_of_work/contracts.py +6 -7
- python_ddd_framework/unit_of_work/errors.py +11 -0
- python_ddd_framework/unit_of_work/lifecycle.py +19 -0
- python_ddd_framework/unit_of_work/manager.py +86 -32
- python_ddd_framework/unit_of_work/module.py +61 -0
- python_ddd_framework/unit_of_work/options.py +7 -1
- python_ddd_framework/validation/__init__.py +1 -0
- python_ddd_framework/validation/models.py +37 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/METADATA +147 -44
- python_ddd_framework-0.5.1.dist-info/RECORD +488 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/WHEEL +1 -1
- python_ddd_framework/application_services/seeding.py +0 -72
- python_ddd_framework/caching/catalog.py +0 -73
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/cache.py.jinja +0 -10
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/events.py.jinja +0 -24
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/integration.py.jinja +0 -41
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/orders.py.jinja +0 -83
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/tasks.py.jinja +0 -84
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/orders.py.jinja +0 -35
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/repository.py.jinja +0 -14
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain/seeding.py.jinja +0 -19
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/domain_shared/definitions.py.jinja +0 -24
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/http_api/realtime.py.jinja +0 -36
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/orders.py.jinja +0 -49
- python_ddd_framework-0.3.2.dist-info/RECORD +0 -355
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.dockerignore → backend/.dockerignore} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.gitignore → backend/.gitignore} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{.python-version → backend/.python-version} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{Dockerfile → backend/Dockerfile} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.dev.yaml.jinja → backend/compose.dev.yaml.jinja} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{compose.production.yaml.jinja → backend/compose.production.yaml.jinja} +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{src → backend/src}/host/__init__.py.jinja +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/conftest.py.jinja +0 -0
- /python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/{tests → backend/tests}/host/test_http.py.jinja +0 -0
- /python_ddd_framework/{application_services → invocation}/signature.py +0 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/entry_points.txt +0 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/licenses/LICENSE +0 -0
- {python_ddd_framework-0.3.2.dist-info → python_ddd_framework-0.5.1.dist-info}/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Record invocation timing through the framework's interceptor extension."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from time import monotonic
|
|
5
|
+
|
|
6
|
+
from python_ddd_framework import Interceptor, MethodInvocation
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OrderTimingInterceptor(Interceptor):
|
|
10
|
+
async def intercept(self, invocation: MethodInvocation) -> object:
|
|
11
|
+
started = monotonic()
|
|
12
|
+
try:
|
|
13
|
+
return await invocation.proceed()
|
|
14
|
+
finally:
|
|
15
|
+
logging.getLogger(__name__).info(
|
|
16
|
+
"Order invocation completed in %.4fs", monotonic() - started
|
|
17
|
+
)
|
|
@@ -1,20 +1,115 @@
|
|
|
1
1
|
"""模块贡献自己的服务与任务;provider 选择留给 Host。"""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from dishka import Scope
|
|
4
|
+
from modules.{{ cookiecutter.module_name }}.application.background_workers.order_maintenance_worker import (
|
|
5
|
+
OrderMaintenanceWorker,
|
|
6
|
+
)
|
|
7
|
+
from modules.{{ cookiecutter.module_name }}.application.background_workers.order_statistics_worker import (
|
|
8
|
+
OrderStatisticsWorker,
|
|
9
|
+
)
|
|
10
|
+
from modules.{{ cookiecutter.module_name }}.application.interceptors.order_timing_interceptor import (
|
|
11
|
+
OrderTimingInterceptor,
|
|
12
|
+
)
|
|
13
|
+
from modules.{{ cookiecutter.module_name }}.application.options.order_options import {{ cookiecutter.class_prefix }}Options
|
|
14
|
+
from modules.{{ cookiecutter.module_name }}.application.setting_handlers.approval_setting_observer import (
|
|
15
|
+
ApprovalSettingObserver,
|
|
16
|
+
)
|
|
17
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.module import (
|
|
18
|
+
{{ cookiecutter.class_prefix }}ApplicationContractsModule,
|
|
19
|
+
)
|
|
20
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_approval_service import (
|
|
21
|
+
{{ cookiecutter.class_prefix }}ApprovalApplicationService,
|
|
22
|
+
)
|
|
23
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_management_service import (
|
|
24
|
+
{{ cookiecutter.class_prefix }}ManagementApplicationService,
|
|
25
|
+
)
|
|
26
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_query_service import (
|
|
27
|
+
{{ cookiecutter.class_prefix }}QueryApplicationService,
|
|
28
|
+
)
|
|
29
|
+
from modules.{{ cookiecutter.module_name }}.domain.module import {{ cookiecutter.class_prefix }}DomainModule
|
|
30
|
+
from modules.{{ cookiecutter.module_name }}.domain.settings.approval_settings import APPROVAL_ENABLED
|
|
31
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import MODULE_NAME
|
|
4
32
|
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
33
|
+
from python_ddd_framework import (
|
|
34
|
+
BackgroundExecutionModule,
|
|
35
|
+
BackgroundWorkers,
|
|
36
|
+
BackgroundSchedules,
|
|
37
|
+
HostedServices,
|
|
38
|
+
LocalEventsModule,
|
|
39
|
+
SettingHandlers,
|
|
40
|
+
add_interceptor,
|
|
41
|
+
AppModule,
|
|
42
|
+
ConfigureContext,
|
|
43
|
+
InitializeContext,
|
|
44
|
+
InterceptionStage,
|
|
45
|
+
PostConfigureContext,
|
|
46
|
+
PostInitializeContext,
|
|
47
|
+
PreConfigureContext,
|
|
48
|
+
PreInitializeContext,
|
|
49
|
+
ShutdownContext,
|
|
50
|
+
)
|
|
51
|
+
from python_ddd_framework.caching.module import CachingModule
|
|
52
|
+
from python_ddd_framework.settings import SettingHandlerDefinition
|
|
11
53
|
|
|
12
54
|
|
|
13
55
|
class {{ cookiecutter.class_prefix }}ApplicationModule(AppModule):
|
|
14
|
-
dependencies = (
|
|
56
|
+
dependencies = (
|
|
57
|
+
{{ cookiecutter.class_prefix }}DomainModule,
|
|
58
|
+
{{ cookiecutter.class_prefix }}ApplicationContractsModule,
|
|
59
|
+
CachingModule,
|
|
60
|
+
BackgroundExecutionModule,
|
|
61
|
+
LocalEventsModule,
|
|
62
|
+
)
|
|
15
63
|
scan_packages = (__package__,)
|
|
16
|
-
|
|
17
|
-
|
|
64
|
+
background_workers = BackgroundWorkers((OrderStatisticsWorker, OrderMaintenanceWorker))
|
|
65
|
+
|
|
66
|
+
# 显式启用时导入 hosted_services.order_integration_service.OrderIntegrationService 并加入元组。
|
|
67
|
+
hosted_services = HostedServices(())
|
|
68
|
+
# 可选消息示例见项目 docs/development.md:选择 MessagingModule、声明 OrderObservation
|
|
69
|
+
# 通道并注册 hosted_services.order_observation_handler;不会自动替换线程 callback。
|
|
70
|
+
# 显式启用时导入 background_jobs.order_statistics.schedule.STATISTICS_SCHEDULE 并加入元组。
|
|
71
|
+
background_job_schedules = BackgroundSchedules(())
|
|
72
|
+
setting_handler_definitions = SettingHandlers((
|
|
73
|
+
SettingHandlerDefinition(APPROVAL_ENABLED, ApprovalSettingObserver),
|
|
74
|
+
))
|
|
75
|
+
|
|
76
|
+
def pre_configure(self, context: PreConfigureContext) -> None:
|
|
77
|
+
# 贡献影响后续注册的 PreOptions;此阶段不启动资源。
|
|
78
|
+
pass
|
|
18
79
|
|
|
19
80
|
def configure(self, context: ConfigureContext) -> None:
|
|
81
|
+
# 组装扩展:在这里声明本模块的启动 Options 和必要服务注册;业务执行代码放在应用服务中。
|
|
20
82
|
context.configure({{ cookiecutter.class_prefix }}Options, section=MODULE_NAME)
|
|
83
|
+
context.services.add_scoped(OrderTimingInterceptor, scope=Scope.REQUEST)
|
|
84
|
+
add_interceptor(context,
|
|
85
|
+
OrderTimingInterceptor,
|
|
86
|
+
match=lambda method: (
|
|
87
|
+
method.service
|
|
88
|
+
in (
|
|
89
|
+
{{ cookiecutter.class_prefix }}QueryApplicationService,
|
|
90
|
+
{{ cookiecutter.class_prefix }}ManagementApplicationService,
|
|
91
|
+
{{ cookiecutter.class_prefix }}ApprovalApplicationService,
|
|
92
|
+
)
|
|
93
|
+
),
|
|
94
|
+
before=InterceptionStage.METHOD,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def post_configure(self, context: PostConfigureContext) -> None:
|
|
98
|
+
# 读取已冻结 PreOptions,补充 Options 或覆盖已有服务注册。
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
async def pre_initialize(self, context: PreInitializeContext) -> None:
|
|
102
|
+
# 异步初始化前置条件;依赖模块在同一阶段先完成。
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
async def initialize(self, context: InitializeContext) -> None:
|
|
106
|
+
# 初始化本模块资源;不手工启动框架管理的 Worker 或 HostedService。
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
async def post_initialize(self, context: PostInitializeContext) -> None:
|
|
110
|
+
# 完成初始化收尾;应用此时尚未对外就绪。
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
async def shutdown(self, context: ShutdownContext) -> None:
|
|
114
|
+
# 逆依赖关闭本模块拥有的资源;返回前等待清理结束。
|
|
115
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from modules.{{ cookiecutter.module_name }}.application.background_jobs.order_approval.handler import (
|
|
5
|
+
ApprovalJob,
|
|
6
|
+
)
|
|
7
|
+
from modules.{{ cookiecutter.module_name }}.application.background_jobs.order_approval.payload import (
|
|
8
|
+
ApprovalPayload,
|
|
9
|
+
)
|
|
10
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.inputs.approve_order import ApproveOrder
|
|
11
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_approval_service import (
|
|
12
|
+
{{ cookiecutter.class_prefix }}ApprovalApplicationService,
|
|
13
|
+
)
|
|
14
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
|
|
15
|
+
from modules.{{ cookiecutter.module_name }}.domain.repositories.order_repository import OrderRepository
|
|
16
|
+
from modules.{{ cookiecutter.module_name }}.domain.services.order_approval_service import OrderApprovalService
|
|
17
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.permissions.order_permissions import ORDERS_WRITE
|
|
18
|
+
|
|
19
|
+
from python_ddd_framework import ApplicationService, CurrentUser
|
|
20
|
+
from python_ddd_framework.authorization import authorize
|
|
21
|
+
from python_ddd_framework.background_jobs import BackgroundJobEnqueuer
|
|
22
|
+
from python_ddd_framework.errors.base import ResourceConflictError, ResourceNotFoundError
|
|
23
|
+
from python_ddd_framework.fastapi import http
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _{{ cookiecutter.class_prefix }}ApprovalApplicationService(
|
|
29
|
+
ApplicationService, {{ cookiecutter.class_prefix }}ApprovalApplicationService
|
|
30
|
+
):
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
repository: OrderRepository,
|
|
34
|
+
user: CurrentUser,
|
|
35
|
+
jobs: BackgroundJobEnqueuer,
|
|
36
|
+
approval: OrderApprovalService,
|
|
37
|
+
) -> None:
|
|
38
|
+
self._repository = repository
|
|
39
|
+
self._user = user
|
|
40
|
+
self._jobs = jobs
|
|
41
|
+
self._approval = approval
|
|
42
|
+
|
|
43
|
+
@authorize(ORDERS_WRITE)
|
|
44
|
+
async def approve(self, id: UUID, command: ApproveOrder) -> OrderView:
|
|
45
|
+
# 业务扩展:这里编排“读取聚合 → 核对版本 → 领域服务检查策略并执行动作 → 保存”。
|
|
46
|
+
# 审批只是动作示例;替换为入库等用例时保留权限/并发边界,把状态规则放进领域方法。
|
|
47
|
+
order = await self._repository.find(id)
|
|
48
|
+
if order is None:
|
|
49
|
+
raise ResourceNotFoundError("Order not found")
|
|
50
|
+
if order.version != command.expected_version:
|
|
51
|
+
raise ResourceConflictError("Order version changed")
|
|
52
|
+
await self._approval.approve(order, user_id=self._user.user_id)
|
|
53
|
+
await self._repository.save(order)
|
|
54
|
+
logger.info("Order approved")
|
|
55
|
+
return OrderView.model_validate(order)
|
|
56
|
+
|
|
57
|
+
@authorize(ORDERS_WRITE)
|
|
58
|
+
@http.post(status_code=202)
|
|
59
|
+
async def queue_approval(self, id: UUID) -> str:
|
|
60
|
+
# 业务扩展:这里校验入队条件并构造自己的任务载荷;实际后台业务写在对应 Job.execute。
|
|
61
|
+
# Job 和载荷类型一起替换,通过注入的入队服务调用,不自行创建消费者或数据库连接。
|
|
62
|
+
if await self._repository.find(id) is None:
|
|
63
|
+
raise ResourceNotFoundError("Order not found")
|
|
64
|
+
# 入队参与本方法原生事务;失败时订单业务与队列写入一起回滚。
|
|
65
|
+
return await self._jobs.enqueue(ApprovalJob, ApprovalPayload(order_id=id))
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from uuid import uuid4
|
|
3
|
+
|
|
4
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.inputs.create_order import CreateOrder
|
|
5
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_management_service import (
|
|
6
|
+
{{ cookiecutter.class_prefix }}ManagementApplicationService,
|
|
7
|
+
)
|
|
8
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
|
|
9
|
+
from modules.{{ cookiecutter.module_name }}.domain.entities.order import Order
|
|
10
|
+
from modules.{{ cookiecutter.module_name }}.domain.repositories.order_repository import OrderRepository
|
|
11
|
+
from modules.{{ cookiecutter.module_name }}.domain.value_objects.order_title import OrderTitle
|
|
12
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.permissions.order_permissions import ORDERS_WRITE
|
|
13
|
+
|
|
14
|
+
from python_ddd_framework import ApplicationService
|
|
15
|
+
from python_ddd_framework.authorization import authorize
|
|
16
|
+
from python_ddd_framework.fastapi import http
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _{{ cookiecutter.class_prefix }}ManagementApplicationService(
|
|
22
|
+
ApplicationService, {{ cookiecutter.class_prefix }}ManagementApplicationService
|
|
23
|
+
):
|
|
24
|
+
def __init__(self, repository: OrderRepository) -> None:
|
|
25
|
+
self._repository = repository
|
|
26
|
+
|
|
27
|
+
@authorize(ORDERS_WRITE)
|
|
28
|
+
@http.post(status_code=201)
|
|
29
|
+
async def create(self, command: CreateOrder) -> OrderView:
|
|
30
|
+
# 业务扩展:这里编排创建用例,把输入 DTO 转成领域对象,调用领域方法,再交给仓储保存。
|
|
31
|
+
# 将 Order/OrderTitle 换成自己的聚合和值对象;领域规则写进聚合,事务由正式调用管线完成。
|
|
32
|
+
order = Order.create(uuid4(), OrderTitle(value=command.title))
|
|
33
|
+
await self._repository.save(order)
|
|
34
|
+
logger.info("Order created")
|
|
35
|
+
return OrderView.model_validate(order)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from modules.{{ cookiecutter.module_name }}.application.caching.order_cache import OrderCacheItem
|
|
5
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.services.order_query_service import (
|
|
6
|
+
{{ cookiecutter.class_prefix }}QueryApplicationService,
|
|
7
|
+
)
|
|
8
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
|
|
9
|
+
from modules.{{ cookiecutter.module_name }}.domain.repositories.order_repository import OrderRepository
|
|
10
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.permissions.order_permissions import ORDERS_READ
|
|
11
|
+
|
|
12
|
+
from python_ddd_framework import ApplicationService
|
|
13
|
+
from python_ddd_framework.authorization import authorize
|
|
14
|
+
from python_ddd_framework.caching import DistributedCache
|
|
15
|
+
from python_ddd_framework.errors.base import ResourceNotFoundError
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _{{ cookiecutter.class_prefix }}QueryApplicationService(ApplicationService, {{ cookiecutter.class_prefix }}QueryApplicationService):
|
|
21
|
+
def __init__(self, repository: OrderRepository, cache: DistributedCache[OrderCacheItem]) -> None:
|
|
22
|
+
self._repository = repository
|
|
23
|
+
self._cache = cache
|
|
24
|
+
|
|
25
|
+
@authorize(ORDERS_READ)
|
|
26
|
+
async def get(self, id: UUID) -> OrderView:
|
|
27
|
+
# 业务扩展:这里组织单条查询及 DTO 返回;保留缓存时,定义和变更后的失效逻辑要一起调整。
|
|
28
|
+
cached = await self._cache.get(str(id))
|
|
29
|
+
if cached is not None:
|
|
30
|
+
return cached
|
|
31
|
+
order = await self._repository.find(id)
|
|
32
|
+
if order is None:
|
|
33
|
+
raise ResourceNotFoundError("Order not found")
|
|
34
|
+
view = OrderCacheItem.model_validate(order)
|
|
35
|
+
await self._cache.set(str(id), view)
|
|
36
|
+
return view
|
|
37
|
+
|
|
38
|
+
@authorize(ORDERS_READ)
|
|
39
|
+
async def get_list(self) -> tuple[OrderView, ...]:
|
|
40
|
+
# 业务扩展:这里接收实际业务的查询条件;需要筛选/分页时同步扩展 DTO 和仓储查询契约。
|
|
41
|
+
return tuple(OrderView.model_validate(order) for order in await self._repository.get_list())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Application-owned setting snapshot refreshed by the framework."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from python_ddd_framework.settings import SettingSnapshot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ApprovalSettingObserver:
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
self.snapshot: SettingSnapshot[bool] | None = None
|
|
12
|
+
|
|
13
|
+
async def apply(self, snapshot: SettingSnapshot[bool]) -> None:
|
|
14
|
+
self.snapshot = snapshot
|
|
15
|
+
logging.getLogger(__name__).info(
|
|
16
|
+
"Approval setting applied: %s pid=%s", snapshot.value, os.getpid()
|
|
17
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.constants.order_constants import TITLE_MAX_LENGTH
|
|
2
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CreateOrder(BaseModel):
|
|
6
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
7
|
+
title: str = Field(min_length=1, max_length=TITLE_MAX_LENGTH)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
"""契约层不反向依赖应用实现或 provider。"""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.module import {{ cookiecutter.class_prefix }}DomainSharedModule
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from python_ddd_framework import (
|
|
6
|
+
AppModule,
|
|
7
|
+
ConfigureContext,
|
|
8
|
+
InitializeContext,
|
|
9
|
+
PostConfigureContext,
|
|
10
|
+
PostInitializeContext,
|
|
11
|
+
PreConfigureContext,
|
|
12
|
+
PreInitializeContext,
|
|
13
|
+
ShutdownContext,
|
|
14
|
+
)
|
|
6
15
|
|
|
7
16
|
|
|
8
17
|
class {{ cookiecutter.class_prefix }}ApplicationContractsModule(AppModule):
|
|
9
18
|
dependencies = ({{ cookiecutter.class_prefix }}DomainSharedModule,)
|
|
10
19
|
scan_packages = (__package__,)
|
|
20
|
+
|
|
21
|
+
def pre_configure(self, context: PreConfigureContext) -> None:
|
|
22
|
+
# 贡献影响后续注册的 PreOptions;此阶段不启动资源。
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def configure(self, context: ConfigureContext) -> None:
|
|
26
|
+
# 注册服务和普通 Options;业务执行留在所属能力。
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
def post_configure(self, context: PostConfigureContext) -> None:
|
|
30
|
+
# 读取已冻结 PreOptions,补充 Options 或覆盖已有服务注册。
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
async def pre_initialize(self, context: PreInitializeContext) -> None:
|
|
34
|
+
# 异步初始化前置条件;依赖模块在同一阶段先完成。
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
async def initialize(self, context: InitializeContext) -> None:
|
|
38
|
+
# 初始化本模块资源;不手工启动框架管理的 Worker 或 HostedService。
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
async def post_initialize(self, context: PostInitializeContext) -> None:
|
|
42
|
+
# 完成初始化收尾;应用此时尚未对外就绪。
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
async def shutdown(self, context: ShutdownContext) -> None:
|
|
46
|
+
# 逆依赖关闭本模块拥有的资源;返回前等待清理结束。
|
|
47
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.inputs.approve_order import ApproveOrder
|
|
5
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
|
|
6
|
+
|
|
7
|
+
from python_ddd_framework import ApplicationServiceContract
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class {{ cookiecutter.class_prefix }}ApprovalApplicationService(ApplicationServiceContract, Protocol):
|
|
11
|
+
async def approve(self, id: UUID, command: ApproveOrder) -> OrderView: ...
|
|
12
|
+
|
|
13
|
+
async def queue_approval(self, id: UUID) -> str: ...
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.inputs.create_order import CreateOrder
|
|
4
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
|
|
5
|
+
|
|
6
|
+
from python_ddd_framework import ApplicationServiceContract
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class {{ cookiecutter.class_prefix }}ManagementApplicationService(ApplicationServiceContract, Protocol):
|
|
10
|
+
async def create(self, command: CreateOrder) -> OrderView: ...
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from modules.{{ cookiecutter.module_name }}.application_contracts.views.order_view import OrderView
|
|
5
|
+
|
|
6
|
+
from python_ddd_framework import ApplicationServiceContract
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class {{ cookiecutter.class_prefix }}QueryApplicationService(ApplicationServiceContract, Protocol):
|
|
10
|
+
async def get(self, id: UUID) -> OrderView: ...
|
|
11
|
+
|
|
12
|
+
async def get_list(self) -> tuple[OrderView, ...]: ...
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from uuid import UUID
|
|
2
|
+
|
|
3
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.enums.order_status import OrderStatus
|
|
4
|
+
from pydantic import BaseModel, ConfigDict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class OrderView(BaseModel):
|
|
8
|
+
model_config = ConfigDict(frozen=True, from_attributes=True)
|
|
9
|
+
id: UUID
|
|
10
|
+
title: str
|
|
11
|
+
status: OrderStatus
|
|
12
|
+
version: int
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -1,26 +1,14 @@
|
|
|
1
|
-
"""聚合拥有审批规则、版本及事件;持久化实现只负责映射。"""
|
|
2
|
-
|
|
3
1
|
from __future__ import annotations
|
|
4
2
|
|
|
5
|
-
from dataclasses import dataclass
|
|
6
3
|
from uuid import UUID
|
|
7
4
|
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
|
|
12
|
-
from ..domain_shared.definitions import ALREADY_APPROVED, TITLE_MAX_LENGTH, OrderStatus
|
|
13
|
-
|
|
5
|
+
from modules.{{ cookiecutter.module_name }}.domain.events.order_changed import OrderChanged
|
|
6
|
+
from modules.{{ cookiecutter.module_name }}.domain.value_objects.order_title import OrderTitle
|
|
7
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.enums.order_status import OrderStatus
|
|
8
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.errors.order_errors import ALREADY_APPROVED
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@dataclass(frozen=True)
|
|
20
|
-
class OrderChanged:
|
|
21
|
-
order_id: UUID
|
|
22
|
-
status: OrderStatus
|
|
23
|
-
user_id: UUID | None = None
|
|
10
|
+
from python_ddd_framework import AggregateRoot
|
|
11
|
+
from python_ddd_framework.errors import BusinessError
|
|
24
12
|
|
|
25
13
|
|
|
26
14
|
class Order(AggregateRoot[UUID]):
|
|
@@ -45,11 +33,15 @@ class Order(AggregateRoot[UUID]):
|
|
|
45
33
|
|
|
46
34
|
@classmethod
|
|
47
35
|
def create(cls, id: UUID, title: OrderTitle) -> Order:
|
|
36
|
+
# 业务扩展:在这里校验新聚合的创建条件、初始化实体和值对象;替换订单示例时一起调整参数。
|
|
37
|
+
# 领域方法只维护业务状态和事件,持久化由应用服务调用仓储完成。
|
|
48
38
|
order = cls(id, title)
|
|
49
39
|
order.raise_local_event(OrderChanged(id, order.status))
|
|
50
40
|
return order
|
|
51
41
|
|
|
52
42
|
def approve(self, *, user_id: UUID | None = None) -> None:
|
|
43
|
+
# 业务扩展:在这里实现自己的状态转换条件,例如库存是否足够、单据是否允许提交。
|
|
44
|
+
# 校验通过后再修改状态并记录对应事件;HTTP 参数绑定和调用者授权留在外层。
|
|
53
45
|
if self.status is OrderStatus.APPROVED:
|
|
54
46
|
raise BusinessError(ALREADY_APPROVED)
|
|
55
47
|
self._status = OrderStatus.APPROVED
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.enums.order_status import OrderStatus
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class OrderChanged:
|
|
11
|
+
order_id: UUID
|
|
12
|
+
status: OrderStatus
|
|
13
|
+
user_id: UUID | None = None
|
|
@@ -1,10 +1,49 @@
|
|
|
1
1
|
"""扫描仅包含实际业务声明,模块 tests 不参与服务发现。"""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from modules.{{ cookiecutter.module_name }}.domain_shared.module import {{ cookiecutter.class_prefix }}DomainSharedModule
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from python_ddd_framework import (
|
|
6
|
+
AppModule,
|
|
7
|
+
SettingsModule,
|
|
8
|
+
DataSeedingModule,
|
|
9
|
+
ConfigureContext,
|
|
10
|
+
InitializeContext,
|
|
11
|
+
PostConfigureContext,
|
|
12
|
+
PostInitializeContext,
|
|
13
|
+
PreConfigureContext,
|
|
14
|
+
PreInitializeContext,
|
|
15
|
+
ShutdownContext,
|
|
16
|
+
)
|
|
6
17
|
|
|
7
18
|
|
|
8
19
|
class {{ cookiecutter.class_prefix }}DomainModule(AppModule):
|
|
9
|
-
dependencies = ({{ cookiecutter.class_prefix }}DomainSharedModule,)
|
|
20
|
+
dependencies = ({{ cookiecutter.class_prefix }}DomainSharedModule, SettingsModule, DataSeedingModule)
|
|
10
21
|
scan_packages = (__package__,)
|
|
22
|
+
|
|
23
|
+
def pre_configure(self, context: PreConfigureContext) -> None:
|
|
24
|
+
# 贡献影响后续注册的 PreOptions;此阶段不启动资源。
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
def configure(self, context: ConfigureContext) -> None:
|
|
28
|
+
# 注册服务和普通 Options;业务执行留在所属能力。
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def post_configure(self, context: PostConfigureContext) -> None:
|
|
32
|
+
# 读取已冻结 PreOptions,补充 Options 或覆盖已有服务注册。
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
async def pre_initialize(self, context: PreInitializeContext) -> None:
|
|
36
|
+
# 异步初始化前置条件;依赖模块在同一阶段先完成。
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
async def initialize(self, context: InitializeContext) -> None:
|
|
40
|
+
# 初始化本模块资源;不手工启动框架管理的 Worker 或 HostedService。
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
async def post_initialize(self, context: PostInitializeContext) -> None:
|
|
44
|
+
# 完成初始化收尾;应用此时尚未对外就绪。
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
async def shutdown(self, context: ShutdownContext) -> None:
|
|
48
|
+
# 逆依赖关闭本模块拥有的资源;返回前等待清理结束。
|
|
49
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""领域需要的业务仓储边界;不暴露 Session 或容器。"""
|
|
2
|
+
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from modules.{{ cookiecutter.module_name }}.domain.entities.order import Order
|
|
7
|
+
|
|
8
|
+
from python_ddd_framework import RepositoryContract
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OrderRepository(RepositoryContract, Protocol):
|
|
12
|
+
async def find(self, id: UUID, include_details: bool = True) -> Order | None: ...
|
|
13
|
+
async def get(self, id: UUID, include_details: bool = True) -> Order: ...
|
|
14
|
+
async def get_count(self) -> int: ...
|
|
15
|
+
async def get_paged_list(self, skip_count: int, max_result_count: int, sorting: str | None = None, include_details: bool = False) -> list[Order]: ...
|
|
16
|
+
async def get_list(self, include_details: bool = False) -> list[Order]: ...
|
|
17
|
+
async def save(self, order: Order) -> None: ...
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""所属能力的实现或契约。"""
|