refactor(arch): eliminate all cross-module model imports in service layer
Some checks failed
CI / ruff (push) Successful in 9s
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / pytest (push) Has been cancelled

Enforce MOD-025/MOD-026 rules: zero top-level cross-module model imports
remain in any service file. All 66 files migrated using deferred import
patterns (method-body, _get_model() helpers, instance-cached self._Model)
and new cross-module service methods in tenancy. Documentation updated
with Pattern 6 (deferred imports), migration plan marked complete, and
violations status reflects 84→0 service-layer violations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 06:13:15 +01:00
parent e3a52f6536
commit 86e85a98b8
66 changed files with 2242 additions and 1295 deletions

View File

@@ -53,17 +53,16 @@ class SubscriptionService:
Raises:
ResourceNotFoundException: If store not found or has no platform
"""
from app.modules.tenancy.models import Store, StorePlatform
from app.modules.tenancy.services.platform_service import platform_service
from app.modules.tenancy.services.store_service import store_service
store = db.query(Store).filter(Store.id == store_id).first()
store = store_service.get_store_by_id_optional(db, store_id)
if not store or not store.merchant_id:
raise ResourceNotFoundException("Store", str(store_id))
sp = db.query(StorePlatform.platform_id).filter(
StorePlatform.store_id == store_id
).first()
if not sp:
platform_id = platform_service.get_primary_platform_id_for_store(db, store_id)
if not platform_id:
raise ResourceNotFoundException("StorePlatform", f"store_id={store_id}")
return store.merchant_id, sp[0]
return store.merchant_id, platform_id
def get_store_code(self, db: Session, store_id: int) -> str:
"""Get the store_code for a given store_id.
@@ -71,9 +70,9 @@ class SubscriptionService:
Raises:
ResourceNotFoundException: If store not found
"""
from app.modules.tenancy.models import Store
from app.modules.tenancy.services.store_service import store_service
store = db.query(Store).filter(Store.id == store_id).first()
store = store_service.get_store_by_id_optional(db, store_id)
if not store:
raise ResourceNotFoundException("Store", str(store_id))
return store.store_code
@@ -175,9 +174,10 @@ class SubscriptionService:
The merchant subscription, or None if the store, merchant,
or platform cannot be resolved.
"""
from app.modules.tenancy.models import Store
from app.modules.tenancy.services.platform_service import platform_service
from app.modules.tenancy.services.store_service import store_service
store = db.query(Store).filter(Store.id == store_id).first()
store = store_service.get_store_by_id_optional(db, store_id)
if not store:
return None
@@ -185,17 +185,7 @@ class SubscriptionService:
if merchant_id is None:
return None
# Get platform_id from store
platform_id = getattr(store, "platform_id", None)
if platform_id is None:
from app.modules.tenancy.models import StorePlatform
sp = (
db.query(StorePlatform.platform_id)
.filter(StorePlatform.store_id == store_id)
.first()
)
platform_id = sp[0] if sp else None
platform_id = platform_service.get_primary_platform_id_for_store(db, store_id)
if platform_id is None:
return None
@@ -394,5 +384,60 @@ class SubscriptionService:
return subscription
# =========================================================================
# Cross-module public API methods
# =========================================================================
def get_active_subscription_platform_ids(
self, db: Session, merchant_id: int
) -> list[int]:
"""
Get platform IDs where merchant has active subscriptions.
Args:
db: Database session
merchant_id: Merchant ID
Returns:
List of platform IDs with active subscriptions
"""
active_statuses = [
SubscriptionStatus.ACTIVE,
SubscriptionStatus.TRIAL,
]
results = (
db.query(MerchantSubscription.platform_id)
.filter(
MerchantSubscription.merchant_id == merchant_id,
MerchantSubscription.status.in_(active_statuses),
)
.all()
)
return [r[0] for r in results]
def get_all_active_subscriptions(
self, db: Session
) -> list[MerchantSubscription]:
"""
Get all active/trial subscriptions with tier and feature limits.
Returns:
List of MerchantSubscription objects with eager-loaded tier data
"""
active_statuses = [
SubscriptionStatus.ACTIVE,
SubscriptionStatus.TRIAL,
]
return (
db.query(MerchantSubscription)
.options(
joinedload(MerchantSubscription.tier)
.joinedload(SubscriptionTier.feature_limits),
)
.filter(MerchantSubscription.status.in_(active_statuses))
.all()
)
# Singleton instance
subscription_service = SubscriptionService()