Skip to content

Python · architecture

require-port-for-service

python:require-port-for-service

Consider a consumer-owned port for a service with a behaviorally used collaborator.

Code
SARJ071
Default
error
Fix
none
Languages
python

Why

A small port can decouple consumers when they genuinely need to substitute a concrete service boundary.

Fix

Define a focused `Protocol` or ABC and type substituting consumers against it, or suppress the advisory when no substitution boundary exists.

Before / after

Executed by this rule’s unit tests.

Before

Concrete service directly exposes an injected collaborator

app/services/thing_service.py · focus
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)

After

Service implements a declared port

app/services/thing_service.py · focus
class ThingService(ThingServicePort):
    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)

Limits

  • This advisory uses service-family names, constructor annotations, collaborator calls, and public-method counts as heuristics.
  • Tests, generated code, scripts, known framework shapes, persistence-only dependencies, and classes with declared bases are excluded.