Scaling Patterns Across Your Codebase

Module 15: Fractal Architecture | Expansion Guide

Back to Module 15

The Problem

You established a great pattern for API endpoints in one service. Then you built a second service and used a different pattern. A third service uses yet another approach. Now you have 12 microservices, each with different file structures, naming conventions, and error handling strategies. Onboarding takes weeks. AI suggestions are inconsistent because it can't tell which pattern to follow.

Patterns that don't scale across your codebase create chaos and confusion.

The challenge: taking a pattern that works at one level and replicating it consistently across all levels - from single functions to entire service fleets.

The Core Insight

Fractal scaling means applying the same patterns at function, file, module, service, and system levels. AI learns once, applies everywhere.

Think of a matryoshka doll: each level is a smaller version of the larger one. In code: a function should look like a miniature service. A service should look like a miniature system. Same patterns, different scales.

When AI sees this consistency, it doesn't need to learn your architecture separately for each component. It recognizes the pattern once and transfers the understanding everywhere.

The Walkthrough

Level 1: Function-Level Patterns

Establish a consistent pattern for all functions:

# Pattern: Input validation → Business logic → Output formatting
def create_user(user_data: dict) -> Result[User]:
    # 1. Validate
    if not user_data.get('email'):
        return Err(ValidationError("Email required"))

    # 2. Business logic
    user = User(**user_data)
    user.hash_password()

    # 3. Return
    return Ok(user)

def create_post(post_data: dict) -> Result[Post]:
    # Same 3-step pattern
    if not post_data.get('title'):
        return Err(ValidationError("Title required"))

    post = Post(**post_data)
    post.generate_slug()

    return Ok(post)

AI sees: "Every function follows validate → process → return. All return Result[T]."

Level 2: File-Level Patterns

Every file in a domain has the same structure:

# users/models.py
from dataclasses import dataclass

@dataclass
class User:
    id: str
    email: str
    # Always: dataclasses for models

# users/services.py
class UserService:
    def __init__(self, repo: UserRepository):
        self.repo = repo
    # Always: service class with injected repository

# users/routes.py
@app.route('/users', methods=['POST'])
def create_user(ctx: Context):
    result = ctx.services.users.create(ctx.request.json)
    return ctx.respond(result)
# Always: route functions that delegate to services

This pattern repeats for every domain: posts/, comments/, orders/, etc.

Level 3: Module-Level Patterns

Every domain module has identical structure:

users/
  ├── __init__.py      # Exports public API
  ├── models.py        # Data definitions
  ├── services.py      # Business logic
  ├── repository.py    # Data access
  ├── routes.py        # HTTP endpoints
  ├── tests.py         # Tests
  └── README.md        # Domain documentation

posts/
  ├── __init__.py      # Same structure
  ├── models.py
  ├── services.py
  ├── repository.py
  ├── routes.py
  ├── tests.py
  └── README.md

AI prompt: "Add a new 'notifications' domain"

AI response: Creates all 7 files following the established pattern.

Level 4: Service-Level Patterns

Every microservice has the same architecture:

user-service/
  ├── api/           # HTTP layer
  ├── core/          # Business logic
  ├── data/          # Database layer
  ├── config/        # Configuration
  ├── tests/         # Test suite
  └── Dockerfile     # Deployment

order-service/
  ├── api/           # Same layers
  ├── core/
  ├── data/
  ├── config/
  ├── tests/
  └── Dockerfile

payment-service/
  ├── api/           # Identical pattern
  ├── core/
  ├── data/
  ├── config/
  ├── tests/
  └── Dockerfile

Someone joining the team knows: "All services follow this structure. If I learned user-service, I know how to navigate payment-service."

The Cognitive Load Advantage

When patterns scale, developers (and AI) only learn architecture once. New services, modules, or functions are immediately familiar. This compounds over time.

Real Example: E-commerce Platform

Fractal Pattern at Every Scale

Pattern: Separate concerns into layers: API → Domain → Data

Scale Pattern Application Example
Function Parse input → Execute logic → Format output def create_order(data) → validate() → process() → respond()
Class Constructor → Methods → Private helpers class OrderService: __init__() → public methods → _private helpers
Module routes.py → services.py → repository.py HTTP → business logic → database
Service api/ → core/ → data/ REST layer → domain layer → persistence
System API Gateway → Business Services → Data Stores nginx → microservices → PostgreSQL

At every scale, the pattern is the same: interface → logic → storage. AI sees this once and understands it everywhere.

Enforcement Through Tooling

Scaffolding Scripts

Don't manually replicate patterns. Automate it:

#!/usr/bin/env python3
# scripts/new_domain.py

import os
import sys

def create_domain(name):
    """Create a new domain following fractal pattern."""
    domain_dir = f"src/{name}"

    # Create directory structure
    os.makedirs(f"{domain_dir}")

    # Generate files from templates
    files = {
        'models.py': MODEL_TEMPLATE.format(name=name),
        'services.py': SERVICE_TEMPLATE.format(name=name),
        'repository.py': REPO_TEMPLATE.format(name=name),
        'routes.py': ROUTES_TEMPLATE.format(name=name),
        'tests.py': TEST_TEMPLATE.format(name=name),
        '__init__.py': INIT_TEMPLATE.format(name=name),
        'README.md': README_TEMPLATE.format(name=name)
    }

    for filename, content in files.items():
        with open(f"{domain_dir}/{filename}", 'w') as f:
            f.write(content)

    print(f"Created {name} domain following fractal pattern")

# Usage: python scripts/new_domain.py notifications

Now every new domain is guaranteed to follow the pattern. No drift.

Linting for Pattern Compliance

# .eslintrc.yml or similar
rules:
  # Enforce naming patterns
  - "Service classes must end with 'Service'"
  - "Repository classes must end with 'Repository'"
  - "Route functions must be in routes.py"
  - "Models must use dataclasses or Pydantic"

  # Enforce structure
  - "Services must inject dependencies via constructor"
  - "All functions must have type hints"
  - "All public methods must have docstrings"

Architecture Decision Records (ADRs)

Document your fractal patterns:

# docs/architecture/ADR-001-fractal-patterns.md

## Decision: Fractal Domain Structure

All domains (users, orders, payments, etc.) follow this structure:

```
domain/
  ├── models.py      # Data models (dataclasses)
  ├── services.py    # Business logic (injected dependencies)
  ├── repository.py  # Data access (interface + impl)
  ├── routes.py      # HTTP endpoints (thin layer)
  ├── tests.py       # Test suite
  └── __init__.py    # Public API
```

**Rationale:** Consistency enables fast onboarding, clear ownership, and predictable AI suggestions.

**Enforcement:** Use `scripts/new_domain.py` to scaffold. Linter checks for compliance.

Migrating to Fractal Patterns

Step 1: Document Current State

# Audit existing patterns
find . -name "*.py" -exec grep "class.*Service" {} \; | wc -l
# Count: 47 service classes

# How many follow DI pattern?
grep -r "def __init__.*Repository" --include="*service.py" | wc -l
# Count: 12 (only 25% fractal!)

# Document gap
echo "39 services need refactoring to fractal pattern"

Step 2: Pick One Pattern and Migrate

Don't try to fix everything. Pick one pattern (e.g., service layer) and migrate it fully:

# Week 1: Refactor all services to use DI
# Week 2: Refactor all repositories to use interfaces
# Week 3: Standardize all route handlers
# Week 4: Unify error handling across all layers

Step 3: Prevent Regression

# Add CI check
# .github/workflows/fractal-check.yml

name: Fractal Pattern Check
on: [pull_request]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Check pattern compliance
        run: |
          python scripts/check_fractal_patterns.py
          # Fails if new code violates patterns

Failure Patterns

1. Partial Migration

Symptom: Half the codebase uses new pattern, half uses old. Confusion reigns.

Fix: Don't introduce new patterns until you can migrate all existing code or clearly mark legacy vs new.

2. Pattern Drift

Symptom: Pattern starts consistent but each team adds "improvements" until it fragments.

Fix: Central architecture ownership. Changes to patterns require approval.

3. Over-Engineering for Fractal Purity

Symptom: Forcing fractal patterns where they don't fit, creating unnecessary abstraction.

# Don't do this
class PrintHelloService:
    def __init__(self, printer: Printer):
        self.printer = printer

    def execute(self):
        self.printer.print("Hello")

# Simple function is fine
def print_hello():
    print("Hello")

Fix: Apply fractal patterns to reusable components. One-off scripts don't need it.

4. No Documentation

Symptom: Patterns exist but aren't documented. New developers don't know they should follow them.

Fix: ADRs, scaffolding scripts, and onboarding docs that explicitly teach the patterns.

When NOT to Scale Patterns

AI-Assisted Pattern Scaling

Prompt: Generate Scaffolding

Prompt to AI:
"Create a scaffolding script that generates a new domain following this pattern:

Existing domains:
- users/ (with models, services, repository, routes, tests)
- posts/ (same structure)

Generate a Python script that creates a new domain with the same file structure and template content."

Prompt: Migrate to Pattern

Prompt to AI:
"Refactor this service to match the fractal pattern used in UserService.

Current code:
[paste non-fractal code]

Reference pattern (UserService):
[paste fractal example]

Requirements:
- Constructor dependency injection
- Same method naming conventions
- Same error handling approach"

Prompt: Validate Pattern Compliance

Prompt to AI:
"Review this code and check if it follows our fractal patterns:

1. Services end with 'Service' suffix
2. Dependencies injected via constructor
3. Returns Result[T] type
4. Has docstrings on public methods

Code:
[paste code]

List any violations and suggest fixes."

Quick Reference

Fractal Scaling Checklist:

  1. ✓ Function patterns are consistent across codebase
  2. ✓ File structures repeat for each domain
  3. ✓ Module layouts are identical for similar components
  4. ✓ Service architectures mirror each other
  5. ✓ Naming conventions scale across all levels
  6. ✓ Error handling is uniform everywhere
  7. ✓ Testing approaches are standardized

Pattern Scaling Layers:

Layer What to Standardize
Function Signature style, error handling, return types
File Imports order, section organization, naming
Module File structure, directory layout, exports
Service Architecture layers, config management, deployment
System Inter-service communication, observability, infrastructure

Enforcement Tools:

# 1. Scaffolding
python scripts/new_domain.py {name}
python scripts/new_service.py {name}

# 2. Linting
eslint --rule "fractal-patterns"
pylint --load-plugins fractal_checker

# 3. CI/CD Checks
.github/workflows/pattern-check.yml

# 4. Documentation
docs/architecture/ADR-fractal-patterns.md

# 5. Code Review
.github/PULL_REQUEST_TEMPLATE.md
- [ ] Follows fractal patterns
- [ ] Used scaffolding scripts
- [ ] Updated pattern docs if changed

Migration Strategy:

Phase 1: Document current patterns (1 week)
Phase 2: Choose target fractal pattern (1 week)
Phase 3: Create scaffolding tools (1 week)
Phase 4: Migrate core services (4 weeks)
Phase 5: Enforce for new code (ongoing)
Phase 6: Gradual migration of legacy (ongoing)