refactor: review remaining justified lazy imports for dependency injection #57

Open
opened 2026-03-02 19:30:54 +00:00 by cal · 0 comments
Owner

Context

A full codebase audit identified ~50 lazy imports. ~42 unnecessary ones have been moved to top-level imports. The remaining 8 locations have legitimate reasons (circular imports, init-order, optional deps) but most could be eliminated with proper dependency injection.

Remaining Lazy Imports to Review

Circular Import Avoidance — DI Candidates

  1. models/player.py:92from models.team import Team inside Player.from_api_data()

    • Circular: player ↔ team models reference each other
    • Could be resolved by extracting shared types or restructuring model relationships
  2. models/team.py:154, 175, 198from services.team_service import team_service (×3)

    • Inside major_league_affiliate(), minor_league_affiliate(), injured_list_affiliate()
    • Circular: model imports its own service
    • Strong DI candidate: pass team_service as a parameter to these methods instead of importing the singleton
  3. utils/decorators.py:128-129from services.league_service import league_service and from views.embeds import EmbedTemplate

    • Inside the requires_draft_period decorator's wrapper function
    • Circular: decorators ↔ services
    • Could accept league_service as a decorator parameter or use a registry pattern
  4. utils/permissions.py:51from services.team_service import team_service

    • Inside get_user_team()
    • Circular: utils ↔ services
    • DI candidate: accept team_service as a parameter
  5. services/decision_service.py:103from services.player_service import player_service

    • Inside find_winning_losing_pitchers()
    • Circular avoidance between sibling services
    • DI candidate: inject player_service via constructor

Intentional Architectural Patterns — Keep As-Is

  1. commands/draft/admin.py:41from tasks.draft_monitor import setup_draft_monitor

    • Inside _ensure_monitor_running()
    • Init-order dependency: task module needs bot internals ready before import
    • Recommendation: Keep — this is a valid deferred initialization pattern
  2. bot.py:114-130 — 17 command package imports inside _load_command_packages()

    • Intentional on_ready resilience: each cog loads in try/except for fault isolation
    • Recommendation: Keep — this is a deliberate architectural choice
  3. bot.py:194-211 — 4 task/service imports inside _setup_background_tasks()

    • Same on_ready resilience pattern as above
    • Recommendation: Keep — same rationale

Not a Lazy Import

  • utils/cache.py:10-15import redis.asyncio in try/except at module level
    • Optional dependency guard, not a lazy import
  • services/player_service.py:16 and views/players.py:16-18TYPE_CHECKING guards
    • Standard typing pattern, correct as-is

Suggested Approach

Items 1-5 are all candidates for dependency injection refactoring. The most impactful would be:

  • models/team.py (3 lazy imports) — pass team_service to affiliate lookup methods
  • services/decision_service.py — inject player_service via constructor
  • utils/permissions.py — accept team_service as parameter to get_user_team()

Items 6-8 should remain as-is — they serve important architectural purposes.

## Context A full codebase audit identified ~50 lazy imports. ~42 unnecessary ones have been moved to top-level imports. The remaining 8 locations have legitimate reasons (circular imports, init-order, optional deps) but most could be eliminated with proper dependency injection. ## Remaining Lazy Imports to Review ### Circular Import Avoidance — DI Candidates 1. **`models/player.py:92`** — `from models.team import Team` inside `Player.from_api_data()` - Circular: `player ↔ team` models reference each other - Could be resolved by extracting shared types or restructuring model relationships 2. **`models/team.py:154, 175, 198`** — `from services.team_service import team_service` (×3) - Inside `major_league_affiliate()`, `minor_league_affiliate()`, `injured_list_affiliate()` - Circular: model imports its own service - Strong DI candidate: pass `team_service` as a parameter to these methods instead of importing the singleton 3. **`utils/decorators.py:128-129`** — `from services.league_service import league_service` and `from views.embeds import EmbedTemplate` - Inside the `requires_draft_period` decorator's wrapper function - Circular: decorators ↔ services - Could accept `league_service` as a decorator parameter or use a registry pattern 4. **`utils/permissions.py:51`** — `from services.team_service import team_service` - Inside `get_user_team()` - Circular: utils ↔ services - DI candidate: accept `team_service` as a parameter 5. **`services/decision_service.py:103`** — `from services.player_service import player_service` - Inside `find_winning_losing_pitchers()` - Circular avoidance between sibling services - DI candidate: inject `player_service` via constructor ### Intentional Architectural Patterns — Keep As-Is 6. **`commands/draft/admin.py:41`** — `from tasks.draft_monitor import setup_draft_monitor` - Inside `_ensure_monitor_running()` - Init-order dependency: task module needs bot internals ready before import - **Recommendation: Keep** — this is a valid deferred initialization pattern 7. **`bot.py:114-130`** — 17 command package imports inside `_load_command_packages()` - Intentional on_ready resilience: each cog loads in try/except for fault isolation - **Recommendation: Keep** — this is a deliberate architectural choice 8. **`bot.py:194-211`** — 4 task/service imports inside `_setup_background_tasks()` - Same on_ready resilience pattern as above - **Recommendation: Keep** — same rationale ### Not a Lazy Import - **`utils/cache.py:10-15`** — `import redis.asyncio` in try/except at module level - Optional dependency guard, not a lazy import - **`services/player_service.py:16`** and **`views/players.py:16-18`** — `TYPE_CHECKING` guards - Standard typing pattern, correct as-is ## Suggested Approach Items 1-5 are all candidates for dependency injection refactoring. The most impactful would be: - **`models/team.py`** (3 lazy imports) — pass `team_service` to affiliate lookup methods - **`services/decision_service.py`** — inject `player_service` via constructor - **`utils/permissions.py`** — accept `team_service` as parameter to `get_user_team()` Items 6-8 should remain as-is — they serve important architectural purposes.
cal added the
ai-working
label 2026-03-06 14:31:00 +00:00
cal added the
status/in-progress
label 2026-03-06 14:34:43 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: cal/major-domo-v2#57
No description provided.