Core Patterns¶
Summary¶
The patterns on this page describe the key behavioural mechanisms that make the layered BDD automation framework maintainable, predictable, and scalable.
They support long-running workflows, clean separation of concerns, consistent parsing, and extensibility across environments.
1. End-to-End Execution Flow¶
Intent: Show how the BDD layers collaborate from human-readable steps to structured workflow results.
Visual Diagram¶

Here CLIResponse is a generic, technology‑neutral envelope. It can safely hold JSON, plain text, XML‑style output, or multi‑line logs.
Parsing is optional — when executor output is already structured (for example, JSON), response models can hydrate directly without invoking any parser.
When needed, a lightweight parser—or direct Pydantic model parsing—converts the raw output into a typed WorkflowResponse (which includes its own status field).
Specific workflow types may define richer response models, but they always inherit from the same base contract to ensure compatibility.
This ensures the API surface stays stable while still allowing domain‑specific responses to grow in detail and expressiveness without breaking tests, services, or orchestrators.
Typed request/response models ensure that:
- Test intent remains independent of execution mode
- Results can be logged, persisted, and replayed
- Executors and parsers can be swapped without impacting tests
2. Polling Lifecycle Pattern¶
Intent: Decouple workflow-lifecycle monitoring from execution and parsing.
For: Long-running or asynchronous workflows.
Why it matters:
- Prevents polling logic from leaking into test steps
- Provides consistent timeout and retry behaviour
- Handles transient states cleanly (for example,
PENDING,RUNNING,RETRYABLE_ERROR)
sequenceDiagram
participant Test
participant Orch
participant Svc
participant Exec
participant Target
Test->>Orch: start_workflow(req)
Orch->>Svc: submit()
Svc->>Exec: run(start_cmd)
Exec->>Target: execute
loop until terminal_status
Orch->>Svc: status()
Svc->>Exec: run(status_cmd)
Exec-->>Svc: raw_status
Svc->>Svc: normalise raw_status into structured state
end
Orch->>Svc: result()
Svc->>Exec: run(result_cmd)
Exec-->>Svc: raw_result
Svc->>Svc: parse raw_result into result_obj
Orch-->>Test: structured_result Conceptual Python:
def wait_until_complete(service, request):
while not service.is_complete(request):
service.poll(request)
3. Command → Executor → Parser Chain¶
Intent: Provide deterministic transformation from intent to structured response.
Why it matters:
- Enforces consistency across all CLI and API interactions
- Enables audit trails and replayability
- Keeps parsing logic minimal so test code remains clean
graph LR
WR[WorkflowRequest] --> B[Build Command]
B --> Cmd[Command]
Cmd --> Exec[Executor]
Exec --> RAW[Raw Output]
RAW --> CR[CLIResponse - raw or normalized]
CR --> WS[WorkflowResponse] The request–execution–response pipeline is intentionally simple. Most workflows return machine‑readable output that can be parsed directly into a WorkflowResponse.
Only when output is unstructured is a lightweight normaliser used to generate a consistent CLIResponse envelope.
Common edge cases:
- Empty raw output → warning result
- Malformed output → structured parse error
4. Extensibility Hooks¶
Intent: Enable new capabilities without breaking existing tests.
Why it matters: Supports long-term evolution of the framework.
| Extension | Add Without Touching | Replace Strategy Via |
|---|---|---|
| New Executor | Service / Orchestrator | Dependency injection |
| New Normaliser | Service logic | Factory registration |
| New Workflow Type | Step and Orchestrator | New domain models |
Additional benefit: inversion of control prevents extension changes from cascading across layers.
5. Generic Retry Pattern (Optional)¶
Intent: Handle transient failures at the orchestration level only.
Why it matters:
- Keeps services, orchestrators, and executors stateless
- Provides controlled retry windows for long-running workflows
- Avoids duplicating retry logic inside step definitions
Common transient scenarios:
- Temporary network jitter
- Tool initialisation delays
- Resource locks in distributed systems
6. Neutral Error Representation¶
Intent: Provide machine-readable, environment-neutral errors.
Why it matters:
- Avoids scraping vendor-specific text
- Ensures reliability across environments
- Makes CI failure triage simpler
Example:
7. Parallel Execution Pattern (Optional)¶
Intent: Safely run multiple workflows or BDD scenarios concurrently.
Why it matters:
- Distributed workflows naturally parallelise
- CI pipelines benefit from reduced execution time
- Executors are stateless, allowing safe concurrency
Conceptual strategy:
- Use unique workflow identifiers per scenario
- Ensure log paths and temporary files are isolated
- Configure pytest-xdist or worker pools for parallel scenarios
- Let orchestrators handle concurrency while steps stay simple
Parallel execution is safe because all request and response models are immutable and self‑contained.
Underlying Design Patterns¶
The framework naturally incorporates several classic design patterns:
- Command Pattern — commands express intent in a transport-neutral manner.
- Strategy Pattern — executors are pluggable and selected at runtime.
- Factory Pattern — Optional normalisers or direct model constructors are selected only when required; structured output bypasses parsing.
- Dependency Injection — executors and configuration are injected into services and orchestrators.
- Adapter Pattern — executors map high-level intent to environment-specific execution.
- Template Method Pattern — orchestrators define a start → poll → finalise lifecycle.
These patterns emerge naturally rather than being enforced explicitly.
Related Docs¶
architecture-overview.mdlayers.md- Root
README.md
Notes¶
Patterns are conceptual only; they do not bind to specific environments, tools, or proprietary systems.