Skip to main content

Wrapper Pattern for Dual-Interface Modules

Extracted from CloudOps-Runbooks VPC module. Describes a reusable architecture pattern for any module that needs both CLI and programmatic interfaces.

Problem

Many operational modules need two interfaces:

  1. CLI — for technical users running commands in terminals
  2. Programmatic (Jupyter/scripts) — for non-technical users or automation pipelines

Without a pattern, this leads to:

  • Business logic duplicated between CLI handlers and notebook cells
  • Inconsistent outputs (CLI shows one format, notebook shows another)
  • Difficult testing (must test both code paths independently)

Decision: Wrapper Architecture

Centralise business logic in a wrapper class. Both CLI and programmatic interfaces delegate to the same wrapper.

Structure

src/module/
├── __init__.py # Module exports
├── wrapper.py # Business logic (single source of truth)
├── cost_engine.py # Domain-specific calculations
├── heatmap_engine.py # Visualisation logic
└── rich_formatters.py # Output formatting utilities

How It Works

# Wrapper holds all business logic
wrapper = ModuleWrapper(profile="aws-profile", console=console)

# CLI calls the wrapper
# (Click/Typer handler is thin — just arg parsing + wrapper call)
results = wrapper.analyze()

# Notebook calls the same wrapper
# (Cell is thin — just wrapper call + display)
results = wrapper.analyze()

Before (Anti-Pattern)

# Notebook cell: 200+ lines of duplicated business logic
class NetworkingCostHeatMapEngine:
def __init__(self, config):
# ... complex initialisation
def _generate_time_series(self):
# ... 50 lines of calculation
# ... more duplicated methods

After (Wrapper Pattern)

# Notebook cell: 3 lines — just the customer journey
from module import ModuleWrapper
wrapper = ModuleWrapper(profile=PROFILE)
results = wrapper.analyze()
# Rich display happens automatically

Design Principles

DRY

Business logic exists in exactly one place (the wrapper). CLI and notebooks are thin adapters.

Single Responsibility

  • Wrapper: orchestrates business operations
  • Engine classes: domain-specific calculations
  • Formatters: output presentation
  • CLI handler: argument parsing only
  • Notebook: user journey only

Testability

Wrapper classes can be unit-tested independently of CLI framework or notebook runtime.

When to Use This Pattern

ScenarioUse Wrapper?
Module has CLI + notebook usersYes
Module has CLI onlyNo — keep it simple
Module has notebook onlyNo — keep it simple
Module may gain a second interface laterNo — apply YAGNI; add wrapper when needed

Applicability to ADLC

This pattern applies to any consumer project module that serves multiple interfaces:

  • CloudOps runbook modules (CLI + Jupyter)
  • FinOps analysis tools (CLI + dashboard)
  • Monitoring utilities (CLI + API)

The key insight: the wrapper is the business logic boundary. Everything above it (CLI, notebook, API) is an adapter. Everything below it (engines, AWS clients) is an implementation detail.


Origin: CloudOps-Runbooks VPC networking module. Generalised for ADLC framework guidance.