require-port-for-service
Consider a consumer-owned port when visible service structure suggests a substitution boundary.
Why
A small port can decouple consumers when they genuinely need to substitute a concrete service boundary.
Fix
When a real consumer needs substitution, define a focused consumer-owned Protocol and type that consumer against it; otherwise suppress the advisory instead of adding an unused abstraction.
Examples
class ThingService: def __init__(self, client: ThingClient) -> None: self.client = client
def read(self, key: str) -> str: return self.client.get(key)
def write(self, key: str, value: str) -> None: self.client.put(key, value)
def sync(service: ThingService) -> None: service.write("inbox", service.read("outbox"))from typing import Protocol
class ThingServicePort(Protocol): def read(self, key: str) -> str: ... def write(self, key: str, value: str) -> None: ...
class ThingService: def __init__(self, client: ThingClient) -> None: self.client = client
def read(self, key: str) -> str: return self.client.get(key)
def write(self, key: str, value: str) -> None: self.client.put(key, value)
def sync(service: ThingServicePort) -> None: service.write("inbox", service.read("outbox"))from typing import Protocol
class Backend(Protocol): def read(self) -> str: ... def write(self, value: str) -> None: ...
class Coordinator: def __init__(self) -> None: self.backend: Backend = native_backend()
def read(self) -> str: return self.backend.read()
def write(self, value: str) -> None: self.backend.write(value)
class Consumer: def receive(self) -> str: return Coordinator().read()
def send(self, value: str) -> None: Coordinator().write(value)[project]name = "boundary-example"version = "0.1.0"from typing import Protocol, final
class Backend(Protocol): def read(self) -> str: ... def write(self, value: str) -> None: ...
class Operations(Protocol): def read(self) -> str: ... def write(self, value: str) -> None: ...
@finalclass Coordinator: def __init__(self) -> None: self.backend: Backend = native_backend()
def read(self) -> str: return self.backend.read()
def write(self, value: str) -> None: self.backend.write(value)
def coordinator() -> Operations: return Coordinator()
class Consumer: def receive(self) -> str: return coordinator().read()
def send(self, value: str) -> None: coordinator().write(value)[project]name = "boundary-example"version = "0.1.0"