Purpose

A domain module that defines exactly one class stays easy to locate: the file name is the class name, and there is never a second symbol hiding below the first. This rule bounds the number of top-level class definitions per module to one. Definitions nested inside another class or function are not counted; definitions wrapped in flow control (try/except, if TYPE_CHECKING) still count as top level.

Configuration

rules:
  - name: one-class-per-domain-module
    type: symbol-count
    description: Domain modules define a single class
    within: src/contexts/{ctx}/domain/**
    symbol: class
    max: 1
    ignore_files: [__init__.py]

Violation Example

# src/contexts/orders/domain/entities/order.py

class Order:
    def __init__(self, order_id: str) -> None:
        self.order_id = order_id


class OrderLine:   # second top-level class -> surplus
    def __init__(self, sku: str, quantity: int) -> None:
        self.sku = sku
        self.quantity = quantity

Passing Example

# src/contexts/orders/domain/entities/order.py

class Order:
    def __init__(self, order_id: str) -> None:
        self.order_id = order_id

Output

$ psl check
src/contexts/orders/domain/entities/order.py:8
    [one-class-per-domain-module] Domain modules define a single class
    2 top-level class definitions found, at most 1 allowed

Found 1 violation(s).