refactor(subtitles): introduce SubtitleKnowledge Protocol port
Domain services (SubtitleIdentifier, PatternDetector) used to import the concrete SubtitleKnowledgeBase class directly from infrastructure for their type hint. With this commit they depend on a structural Protocol in alfred/domain/subtitles/ports/knowledge.py declaring just the 7 read-only query methods the domain actually consumes. The concrete YAML-backed SubtitleKnowledgeBase in infrastructure remains the sole adapter — no rename, no shim. With this change alfred/domain/subtitles/ has zero imports from alfred/infrastructure/. Also extend the changelog entry covering the full domain-io-extraction branch.
This commit is contained in:
@@ -143,6 +143,17 @@ callers).
|
||||
an explicit `default_rules: SubtitleMatchingRules` parameter. The
|
||||
`ManageSubtitles` use case loads defaults from the KB once and
|
||||
passes them in.
|
||||
- **`SubtitleKnowledge` Protocol port** at
|
||||
`alfred/domain/subtitles/ports/knowledge.py` declares the read-only
|
||||
query surface domain services consume (7 methods:
|
||||
`known_extensions`, `format_for_extension`, `language_for_token`,
|
||||
`is_known_lang_token`, `type_for_token`, `is_known_type_token`,
|
||||
`patterns`). `SubtitleIdentifier` and `PatternDetector` depend on
|
||||
this Protocol instead of the concrete `SubtitleKnowledgeBase` from
|
||||
infrastructure — `domain/subtitles/` now has zero imports from
|
||||
`infrastructure/`. The remaining domain → infra leak
|
||||
(`domain/release/` loading separator YAML at import-time) is
|
||||
documented in tech-debt and scheduled for its own branch.
|
||||
- **`to_dot_folder_name(title)` helper** in
|
||||
`alfred/domain/shared/value_objects.py` — extracts the
|
||||
`re.sub(r"[^\w\s\.\-]", "", title).replace(" ", ".")` pattern that was
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Domain ports for the subtitles domain — Protocol-based abstractions
|
||||
that decouple domain services from concrete infrastructure adapters."""
|
||||
|
||||
from .knowledge import SubtitleKnowledge
|
||||
|
||||
__all__ = ["SubtitleKnowledge"]
|
||||
@@ -0,0 +1,38 @@
|
||||
"""SubtitleKnowledge port — the query surface domain services need from the
|
||||
subtitle knowledge base, expressed as a Protocol so the domain never imports
|
||||
the infrastructure adapter that backs it.
|
||||
|
||||
The concrete implementation lives in
|
||||
``alfred/infrastructure/knowledge/subtitles/base.py`` (the YAML-backed
|
||||
``SubtitleKnowledgeBase``). Tests can supply any object that satisfies this
|
||||
structural contract.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from ..value_objects import SubtitleFormat, SubtitleLanguage, SubtitlePattern, SubtitleType
|
||||
|
||||
|
||||
class SubtitleKnowledge(Protocol):
|
||||
"""Read-only query surface for subtitle knowledge consumed by the domain.
|
||||
|
||||
Only the methods that domain services actually call belong here — anything
|
||||
else (defaults loading, reload, pattern groups, raw dicts) stays on the
|
||||
concrete class and is reserved for the application layer.
|
||||
"""
|
||||
|
||||
def known_extensions(self) -> set[str]: ...
|
||||
|
||||
def format_for_extension(self, ext: str) -> SubtitleFormat | None: ...
|
||||
|
||||
def language_for_token(self, token: str) -> SubtitleLanguage | None: ...
|
||||
|
||||
def is_known_lang_token(self, token: str) -> bool: ...
|
||||
|
||||
def type_for_token(self, token: str) -> SubtitleType | None: ...
|
||||
|
||||
def is_known_type_token(self, token: str) -> bool: ...
|
||||
|
||||
def patterns(self) -> dict[str, SubtitlePattern]: ...
|
||||
@@ -4,9 +4,8 @@ import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from alfred.infrastructure.knowledge.subtitles.base import SubtitleKnowledgeBase
|
||||
|
||||
from ...shared.ports import FilesystemScanner, MediaProber
|
||||
from ..ports import SubtitleKnowledge
|
||||
from ...shared.value_objects import ImdbId
|
||||
from ..entities import MediaSubtitleMetadata, SubtitleCandidate
|
||||
from ..value_objects import ScanStrategy, SubtitlePattern, SubtitleType
|
||||
@@ -59,7 +58,7 @@ class SubtitleIdentifier:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
kb: SubtitleKnowledgeBase,
|
||||
kb: SubtitleKnowledge,
|
||||
prober: MediaProber,
|
||||
scanner: FilesystemScanner,
|
||||
):
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from alfred.infrastructure.knowledge.subtitles.base import SubtitleKnowledgeBase
|
||||
|
||||
from ...shared.ports import FilesystemScanner, MediaProber
|
||||
from ..ports import SubtitleKnowledge
|
||||
from ..value_objects import ScanStrategy, SubtitlePattern
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -22,7 +21,7 @@ class PatternDetector:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
kb: SubtitleKnowledgeBase,
|
||||
kb: SubtitleKnowledge,
|
||||
prober: MediaProber,
|
||||
scanner: FilesystemScanner,
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user