| name | modularity-patterns |
| description | Recommends modularity, composition, and decoupling patterns for design challenges. Use when designing plugin architectures, reducing coupling, improving testability, or separating cross-cutting concerns. |
Modularity Patterns
Apply these patterns when designing or refactoring code for modularity, extensibility, and decoupling.
Trigger Conditions
Apply this skill when:
- Designing plugin or extension architectures
- Reducing coupling between components
- Improving testability through dependency management
- Creating flexible, configurable systems
- Separating cross-cutting concerns
Select the Right Pattern
| Problem | Apply These Patterns |
|---|---|
| Hard-coded dependencies | DI, IoC, Service Locator, SAM |
| Need runtime extensions | Plugin, Microkernel, Extension Points |
| Swappable algorithms | Strategy, Abstract Factory |
| Additive behavior | Decorator, Chain of Responsibility, SAM |
| Feature coupling | Package by Feature |
| Scattered concerns | AOP, Interceptors, Mixins, SAM |
| Temporal coupling | Observer, Event Bus, Event Sourcing, SAM |
| Read/write optimization | CQRS |
| Deployment flexibility | Feature Toggles, Microservices |
Implementation Checklist
When applying any modularity pattern:
- Define clear interfaces - Contracts before implementations
- Minimize surface area - Expose only what's necessary
- Depend on abstractions - Not concrete implementations
- Favor composition - Over inheritance
- Isolate side effects - Push to boundaries
- Make dependencies explicit - Visible in signatures
- Design for substitution - Any implementation satisfying contract works
- Consider lifecycle - Creation, configuration, destruction
- Plan for versioning - APIs evolve, maintain compatibility
- Test boundaries - Verify contracts, mock implementations
Pattern Reference
Inversion Patterns
Dependency Injection (DI) - Provide dependencies externally rather than creating internally.
- Constructor injection (preferred, immutable)
- Setter injection (optional dependencies)
- Interface injection (framework-driven)
Inversion of Control (IoC) - Let framework control flow, calling your code. Use IoC containers (Spring, Guice, .NET DI) to manage object lifecycles and wiring.
Service Locator - Query central registry for dependencies at runtime. Achieves decoupling but hides dependencies. Prefer DI for explicitness.
Plugin & Extension Architectures
Plugin Pattern - Load and integrate external code at runtime via defined contracts.
- Discovery: directory scanning, manifests, registries
- Lifecycle: load, initialize, unload
- Isolation: classloaders, processes, sandboxes
- Versioning: API compatibility
Microkernel Architecture - Build minimal core with all domain functionality in plugins. Examples: VS Code, Eclipse IDE.
Extension Points & Registry - Define multiple specific extension points rather than single plugin interface. Extensions declare which points they extend.
Structural Composition
Strategy Pattern - Encapsulate interchangeable algorithms behind common interface.
Context → Strategy Interface → Concrete Strategies
Use for runtime behavioral swapping (sorting, validation, pricing).
Decorator Pattern - Wrap objects to add behavior without modification.
Component → Decorator → Decorator → Concrete Component
Use for composable behavior chains (logging, caching, validation).
Composite Pattern - Treat individuals and compositions uniformly via shared interface. Use for tree structures, UI hierarchies, file systems.
Chain of Responsibility - Create pipeline of handlers where each processes or forwards. Use for middleware stacks, request processing pipelines.
Bridge Pattern - Separate abstraction from implementation hierarchies. Use to prevent subclass explosion with multiple varying dimensions.
Module Boundaries
Module Pattern - Encapsulate private state, expose public interface. Modern implementations: ES Modules, CommonJS, Java 9 modules.
Facade Pattern - Provide simplified interface to complex subsystem. Use to establish clean module boundaries.
Package Organization
- By Layer: Group controllers, repositories, services separately
- By Feature: Group everything for "orders" together (preferred for modularity)
Event-Driven Decoupling
Observer Pattern - Implement publish-subscribe where subjects notify observers without knowing them.
Event Bus / Message Broker - Enable system-wide pub-sub with fully decoupled publishers and subscribers. Add behaviors by adding subscribers without publisher changes.
Event Sourcing - Store state changes as event sequence, not snapshots. Enable new projections via event replay; new behaviors react to stream.
CQRS - Separate read and write models.
Commands → Write Model (validation, rules, persistence)
Queries → Read Model (optimized for reading)
Cross-Cutting Concerns
Aspect-Oriented Programming (AOP) - Modularize scattered concerns (logging, security, transactions). Define pointcuts (where) and advice (what).
Interceptors & Middleware - Explicitly wrap method calls or request pipelines. Less magical than AOP, more traceable.
Mixins & Traits - Compose behaviors from multiple sources without deep inheritance. Examples: Scala traits, Rust traits, TypeScript intersections.
Configuration Patterns
Feature Toggles - Decouple deployment from release by shipping new code behind flags. Enables trunk-based development, A/B testing, gradual rollouts.
Strategy Configuration - Externalize algorithmic choices to configuration files.
Convention over Configuration - Reduce wiring through established defaults and naming conventions.
Component Models
Component-Based Architecture - Build self-contained components with defined interfaces managing own state. Examples: React, Vue, server-side component frameworks.
Entity-Component-System (ECS) - Separate identity (entities), data (components), behavior (systems). Use for game development, highly dynamic systems.
Service-Oriented / Microservices - Apply component thinking at system level with process isolation boundaries.
Creational Patterns
Registry Pattern - Maintain collection of implementations keyed by type/name, queried at runtime.
Abstract Factory - Create families of related objects without specifying concrete classes.
Prototype Pattern - Create objects by cloning prototypes, avoiding direct class instantiation.
SAM Pattern (State-Action-Model)
Functional decomposition for reactive systems:
- State: Pure representation of current state
- Action: Pure functions proposing state changes
- Model: State acceptor enforcing business rules
Loop: View renders State → Actions propose → Model accepts/rejects → State updates.
Provides natural boundaries between representation, proposals, and validation.