SuperAPI AI Directory

python-di-soa-patterns

Coding & development

View repository

Updated 10-15 20:07

About this skill

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:

# 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):

# 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.