# Python Di Soa Patterns

> DI/SOA decision tree with full code examples for Python architecture decisions

- Skill: `bobmatnyc/python-di-soa-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add bobmatnyc/python-di-soa-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bobmatnyc/python-di-soa-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: bobmatnyc (https://skillmd.com/u/bobmatnyc)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bobmatnyc/python-di-soa-patterns

---


# When to Use DI/SOA vs Simple Scripts

## Use DI/SOA Pattern (Service-Oriented Architecture) For:
- **Web Applications**: Flask/FastAPI apps with multiple routes and services
- **Background Workers**: Celery tasks, async workers processing queues
- **Microservices**: Services with API endpoints and business logic
- **Data Pipelines**: ETL with multiple stages, transformations, and validations
- **CLI Tools with Complexity**: Multi-command CLIs with shared state and configuration
- **Systems with External Dependencies**: Apps requiring mock testing (databases, APIs, caches)
- **Domain-Driven Design**: Applications with complex business rules and aggregates

**Benefits**: Testability (mock dependencies), maintainability (clear separation), extensibility (swap implementations)

## Skip DI/SOA (Keep It Simple) For:
- **One-Off Scripts**: Data migration scripts, batch processing, ad-hoc analysis
- **Simple CLI Tools**: Single-purpose utilities without shared state
- **Jupyter Notebooks**: Exploratory analysis and prototyping
- **Configuration Files**: Environment setup, deployment scripts
- **Glue Code**: Simple wrappers connecting two systems
- **Proof of Concepts**: Quick prototypes to validate ideas

**Benefits**: Less boilerplate, faster development, easier to understand

## Decision Tree
```
Is this a long-lived service or multi-step process?
  YES -> Use DI/SOA (testability, maintainability matter)
  NO |

Does it need mock testing or swappable dependencies?
  YES -> Use DI/SOA (dependency injection enables testing)
  NO |

Is it a one-off script or simple automation?
  YES -> Skip DI/SOA (keep it simple, minimize boilerplate)
  NO |

Will it grow in complexity over time?
  YES -> Use DI/SOA (invest in architecture upfront)
  NO -> Skip DI/SOA (don't over-engineer)
```

## Example: When NOT to Use DI/SOA

**Lightweight Script Pattern**:
```python
# Simple CSV processing script - NO DI needed
import pandas as pd
from pathlib import Path

def process_sales_data(input_path: Path, output_path: Path) -> None:
    """Process sales CSV and generate summary report.
    
    This is a one-off script, so we skip DI/SOA patterns.
    No need for IFileReader interface or dependency injection.
    """
    # Read CSV directly - no repository pattern needed
    df = pd.read_csv(input_path)
    
    # Transform data
    df['total'] = df['quantity'] * df['price']
    summary = df.groupby('category').agg({
        'total': 'sum',
        'quantity': 'sum'
    }).reset_index()
    
    # Write output directly - no abstraction needed
    summary.to_csv(output_path, index=False)
    print(f"Summary saved to {output_path}")

if __name__ == "__main__":
    process_sales_data(
        Path("data/sales.csv"),
        Path("data/summary.csv")
    )
```

**Same Task with Unnecessary DI/SOA (Over-Engineering)**:
```python
# DON'T DO THIS for simple scripts!
from abc import ABC, abstractmethod
from dataclasses import dataclass
import pandas as pd
from pathlib import Path

class IDataReader(ABC):
    @abstractmethod
    def read(self, path: Path) -> pd.DataFrame: ...

class IDataWriter(ABC):
    @abstractmethod
    def write(self, df: pd.DataFrame, path: Path) -> None: ...

class CSVReader(IDataReader):
    def read(self, path: Path) -> pd.DataFrame:
        return pd.read_csv(path)

class CSVWriter(IDataWriter):
    def write(self, df: pd.DataFrame, path: Path) -> None:
        df.to_csv(path, index=False)

@dataclass
class SalesProcessor:
    reader: IDataReader
    writer: IDataWriter
    
    def process(self, input_path: Path, output_path: Path) -> None:
        df = self.reader.read(input_path)
        df['total'] = df['quantity'] * df['price']
        summary = df.groupby('category').agg({
            'total': 'sum',
            'quantity': 'sum'
        }).reset_index()
        self.writer.write(summary, output_path)

# Too much boilerplate for a simple script!
if __name__ == "__main__":
    processor = SalesProcessor(
        reader=CSVReader(),
        writer=CSVWriter()
    )
    processor.process(
        Path("data/sales.csv"),
        Path("data/summary.csv")
    )
```

**Key Principle**: Use DI/SOA when you need testability, maintainability, or extensibility. For simple scripts, direct calls and minimal abstraction are perfectly fine.

