08. Cross-Cutting Concepts

Cross-cutting concepts are design decisions and patterns that apply across multiple building blocks rather than belonging to a single component.


Domain Model

The domain is modelled as a set of pure value objects and entities with no dependencies on I/O, frameworks, or infrastructure. This is enforced by the hexagonal architecture: the domain/ package must never import from adapters/ or application/.


Error Handling

Situation

Behaviour

Rover would move outside the plateau

Silent safe-stop — position unchanged, processing continues

Rover would move into an obstacle (optional)

Silent safe-stop — position unchanged, mission for that rover ends

Malformed input (bad heading, unknown command)

ValueError raised in InputParser; propagates to __main__ which prints an error message to stderr and exits with code 1

Errors in the domain are never swallowed silently — only the two safe-stop cases above are intentional no-ops.


Immutability and State

  • Plateau is immutable after construction — its dimensions never change during a mission.

  • Heading is an immutable enum — rotation methods return a new Heading value.

  • Rover is mutable — it accumulates state changes as commands are applied. This is intentional; it models the physical rover moving through space.


Testability

All domain classes are designed to be instantiated directly in tests with no mocking required:

plateau = Plateau(5, 5)
rover = Rover(1, 2, Heading.N)
rover.execute(MoveForward(plateau))
assert rover.x == 1
assert rover.y == 3

The InputParser and OutputFormatter are tested with plain strings — no file handles or subprocess calls needed.


Logging

The system is a simple CLI kata — no structured logging framework is used. Debug output, if needed, goes to stderr so it does not pollute the stdout result stream.


Input / Output Format

The text protocol is intentionally minimal:

# Plateau (upper-right corner; lower-left is always 0 0)
5 5

# Per rover: initial position line, then command string line
1 2 N
LMLMLMLMM

Parsing is centralised in InputParser. No other component reads raw strings.


Code Style

Enforced uniformly across the entire codebase via pre-commit hooks:

Tool

Purpose

black

Opinionated code formatting

isort

Import ordering

ruff

Fast linting (style, complexity, unused imports)