refactor: fix architecture violations with provider patterns and dependency inversion

Major changes:
- Add AuditProvider protocol for cross-module audit logging
- Move customer order operations to orders module (dependency inversion)
- Add customer order metrics via MetricsProvider pattern
- Fix missing db parameter in get_admin_context() calls
- Move ProductMedia relationship to catalog module (proper ownership)
- Add marketplace breakdown stats to marketplace_widgets

New files:
- contracts/audit.py - AuditProviderProtocol
- core/services/audit_aggregator.py - Aggregates audit providers
- monitoring/services/audit_provider.py - Monitoring audit implementation
- orders/services/customer_order_service.py - Customer order operations
- orders/routes/api/vendor_customer_orders.py - Customer order endpoints
- catalog/services/product_media_service.py - Product media service
- Architecture documentation for patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-04 21:32:32 +01:00
parent bd43e21940
commit 39dff4ab7d
34 changed files with 2751 additions and 407 deletions

View File

@@ -44,6 +44,7 @@ if TYPE_CHECKING:
from fastapi import APIRouter
from pydantic import BaseModel
from app.modules.contracts.audit import AuditProviderProtocol
from app.modules.contracts.metrics import MetricsProviderProtocol
from app.modules.contracts.widgets import DashboardWidgetProviderProtocol
@@ -434,6 +435,26 @@ class ModuleDefinition:
# The provider will be discovered by core's WidgetAggregator service.
widget_provider: "Callable[[], DashboardWidgetProviderProtocol] | None" = None
# =========================================================================
# Audit Provider (Module-Driven Audit Logging)
# =========================================================================
# Callable that returns an AuditProviderProtocol implementation.
# Use a callable (factory function) to enable lazy loading and avoid
# circular imports. Modules can provide audit logging backends.
#
# Example:
# def _get_audit_provider():
# from app.modules.monitoring.services.audit_provider import audit_provider
# return audit_provider
#
# monitoring_module = ModuleDefinition(
# code="monitoring",
# audit_provider=_get_audit_provider,
# )
#
# The provider will be discovered by core's AuditAggregator service.
audit_provider: "Callable[[], AuditProviderProtocol] | None" = None
# =========================================================================
# Menu Item Methods (Legacy - uses menu_items dict of IDs)
# =========================================================================
@@ -842,6 +863,28 @@ class ModuleDefinition:
return None
return self.widget_provider()
# =========================================================================
# Audit Provider Methods
# =========================================================================
def has_audit_provider(self) -> bool:
"""Check if this module has an audit provider."""
return self.audit_provider is not None
def get_audit_provider_instance(self) -> "AuditProviderProtocol | None":
"""
Get the audit provider instance for this module.
Calls the audit_provider factory function to get the provider.
Returns None if no provider is configured.
Returns:
AuditProviderProtocol instance, or None
"""
if self.audit_provider is None:
return None
return self.audit_provider()
# =========================================================================
# Magic Methods
# =========================================================================