Implements robust framework integration patterns including plugin architectures, middleware chains, cross-framework adapters, and configuration layering to safely extend and connect external frameworks without tight coupling or upgrade friction.
Scanned 9/4/2026
Install to Claude Code
npx -y skills add paulpas/agent-skill-router --skill framework-integration-patterns --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Framework Integration Patterns?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/paulpas-framework-integration-patterns)More formats (shields.io, HTML) on the badges page.
---
name: framework-integration-patterns
description: Implements robust framework integration patterns including plugin architectures, middleware chains, cross-framework adapters, and configuration layering to safely extend and connect external frameworks without tight coupling or upgrade friction.
license: MIT
compatibility: opencode
metadata:
version: "1.0.0"
domain: coding
response_profile:
verbosity: low
directive_strength: high
abstraction_level: operational
triggers: framework integration, plugin architecture, middleware chains, extension points, how do i extend a framework, cross-framework communication, configuration layering, adapter pattern
role: implementation
scope: implementation
output-format: code
content-types: [code, guidance, config, examples, do-dont]
archetypes: [tactical, generation]
anti_triggers: [standalone script, cli tool, quick hack, no-framework]
related-skills: framework-driven-design, software-architecture, version-migration, dependency-injection-patterns
---
# Framework Integration & Extension Patterns
Safely integrates external frameworks into application codebases using plugin architectures, middleware chains, cross-framework adapters, and configuration layering. Prevents tight coupling, upgrade friction, and framework lock-in while maximizing extensibility and maintainability.
## TL;DR Checklist
- [ ] Define stable extension interfaces before integrating any external framework
- [ ] Isolate framework-specific types in adapter modules with zero domain leakage
- [ ] Implement middleware chains for cross-cutting concerns (logging, auth, validation)
- [ ] Layer configuration: framework defaults → environment overrides → custom config
- [ ] Audit integration points before every major framework upgrade
---
## When to Use
- Integrating a third-party or internal framework that requires extension beyond its defaults
- Building plugin-based architectures where external modules must hook into core lifecycle
- Connecting multiple frameworks (e.g., FastAPI + Celery, React + Redux) across boundaries
- Managing configuration that spans framework-specific settings and custom application rules
- Preparing codebase for framework upgrades by decoupling integration logic
## When NOT to Use
- Building standalone CLI tools or scripts that don't require framework extensibility
- When the framework already provides a built-in extension mechanism that meets your needs exactly
- For simple CRUD applications with zero customization requirements — use `framework-driven-design` conventions instead
---
## Core Workflow
1. **Audit Framework Boundaries** — Map all public APIs, lifecycle hooks, and extension points the framework exposes. Identify which internal APIs are private or version-dependent. **Checkpoint:** Document every integration point in a boundary diagram showing what lives inside vs outside the framework adapter layer.
2. **Define Extension Interfaces** — Create stable, framework-agnostic interfaces for every capability your application needs from the framework. Use abstract base classes (Python `abc.ABC`), TypeScript interfaces, or language-native contracts. Place these in the domain or core module. **Checkpoint:** No external framework imports allowed in interface definition files.
3. **Implement Adapter Wrappers** — Build thin adapter modules that translate between your stable interfaces and the framework's concrete types. Adapters handle version-specific quirks, serialization, and error mapping. Keep them under 200 lines per module. **Checkpoint:** Every public method must have type annotations and docstrings explaining the translation logic.
4. **Wire Middleware & Plugin Chains** — Register adapters as plugins or middleware in the framework's initialization phase. Use priority ordering for chains (e.g., authentication → validation → business logic → response serialization). **Checkpoint:** All middleware must implement a standard `(context, next) -> result` signature for composable chaining.
5. **Layer Configuration Strategically** — Combine framework defaults with environment-specific overrides and custom configuration. Use typed config objects (Pydantic models, TypeScript config schemas) to validate at startup. **Checkpoint:** Unrecognized or duplicate config keys must fail fast with clear error messages indicating the source file and expected schema.
6. **Validate Upgrade Path** — Write integration tests that cover every adapter wrapper and plugin hook. These tests isolate framework upgrade risk by asserting against your stable interfaces, not framework internals. **Checkpoint:** If a framework upgrade breaks an adapter test but not an interface contract, the adapter is over-coupled — refactor to decouple.
---
## Implementation Patterns
### Pattern 1: Plugin Architecture for Framework Extension
Define stable plugin interfaces that frameworks can discover and invoke during lifecycle phases. Plugins register themselves via decorators or registry patterns.
```python
# plugins/registry.py
from abc import ABC, abstractmethod
from typing import Callable, Any
import dataclasses
@dataclasses.dataclass
class PluginContext:
"""Shared state passed through plugin chain."""
request_id: str
metadata: dict[str, Any] = dataclasses.field(default_factory=dict)
class FrameworkPlugin(ABC):
"""Base interface for all framework plugins."""
@property
@abstractmethod
def priority(self) -> int:
"""Execution priority (lower runs first)."""
...
@abstractmethod
async def execute(self, ctx: PluginContext, next_fn: Callable[[PluginContext], Any]) -> Any:
"""Execute plugin logic and optionally call next in chain."""
...
class PluginRegistry:
"""Discovers and orders plugins by priority."""
def __init__(self):
self._plugins: list[FrameworkPlugin] = []
def register(self, plugin: FrameworkPlugin) -> None:
self._plugins.append(plugin)
self._plugins.sort(key=lambda p: p.priority)
async def chain(self, ctx: PluginContext) -> Any:
"""Execute plugins as a composable middleware chain."""
async def run(index: int) -> Any:
if index >= len(self._plugins):
return await self._execute_business_logic(ctx) # Final step
plugin = self._plugins[index]
return await plugin.execute(
ctx,
lambda next_ctx: run(index + 1)
)
return await run(0)
async def _execute_business_logic(self, ctx: PluginContext) -> Any:
"""Core application logic — injected at runtime."""
raise NotImplementedError("Business logic must be bound by the adapter")
```
### Pattern 2: Cross-Framework Adapter (BAD vs. GOOD)
```typescript
// ❌ BAD — Direct framework coupling leaks into domain layer
import { Client } from 'mongodb';
import { Router, Request, Response } from 'express';
export class UserService {
private db = new Client('mongodb://localhost'); // Framework type in domain
async getUser(req: Request, res: Response) {
const user = await this.db.collection('users').findOne({ id: req.params.id });
res.json(user); // Express types leak into business logic
}
}
// ✅ GOOD — Adapter isolates framework types behind interface
import { DbClientAdapter } from './adapters/db-client-adapter';
import { HttpResponseAdapter } from './adapters/http-response-adapter';
export interface UserRepository {
findById(id: string): Promise<User | null>;
}
export class UserService {
constructor(
private repo: UserRepository, // Framework-agnostic
private httpResponse: HttpResponseAdapter
) {}
async getUser(requestId: string, userId: string) {
const user = await this.repo.findById(userId);
if (!user) throw new Error(`User ${userId} not found`);
return this.httpResponse.ok(user); // Standardized response contract
}
}
```
### Pattern 3: Configuration Layering Strategy
Layer configuration with strict validation and clear override semantics to prevent silent misconfigurations.
```python
# config/layers.py
from pydantic import BaseSettings, Field
from typing import Optional
import os
class FrameworkConfig(BaseSettings):
"""Base settings provided by the external framework."""
debug: bool = False
log_level: str = "INFO"
max_retries: int = 3
class Config:
env_prefix = "FRAMEWORK_"
extra = "forbid" # Fail fast on unknown keys
class ApplicationConfig(FrameworkConfig):
"""Application-specific configuration that extends framework defaults."""
database_url: str = Field(..., description="PostgreSQL connection string")
cache_ttl_seconds: int = Field(default=300, ge=1, le=86400)
feature_flags: dict[str, bool] = Field(default_factory=dict)
class Config:
env_prefix = "APP_"
def load_config() -> ApplicationConfig:
"""Load and validate configuration from layered sources."""
config_path = os.getenv("APP_CONFIG_FILE")
if config_path and os.path.exists(config_path):
import yaml
with open(config_path) as f:
overrides = yaml.safe_load(f) or {}
merged = {**FrameworkConfig().model_dump(), **overrides}
return ApplicationConfig(**merged)
return ApplicationConfig()
```
### Pattern 4: Lifecycle Hook Isolation
Isolate framework-specific lifecycle hooks in dedicated modules to prevent state leakage and enable testability.
```python
# hooks/lifecycle.py
from typing import Callable, Any
import asyncio
class LifecycleManager:
"""Manages framework hook registration without exposing framework internals."""
def __init__(self):
self._startup: list[Callable] = []
self._shutdown: list[Callable] = []
self._request: list[Callable[[Any], Any]] = []
def on_startup(self, fn: Callable) -> Callable:
"""Register startup hook. Called exactly once per process."""
self._startup.append(fn)
return fn
def on_shutdown(self, fn: Callable) -> Callable:
"""Register shutdown hook. Called exactly once per process."""
self._shutdown.append(fn)
return fn
async def run_startup(self) -> None:
for hook in self._startup:
result = hook() if asyncio.iscoroutinefunction(hook) else hook()
if asyncio.isawaitable(result):
await result
async def run_shutdown(self) -> None:
for hook in reversed(self._shutdown):
result = hook() if asyncio.iscoroutinefunction(hook) else hook()
if asyncio.isawaitable(result):
await result
# Usage — framework-specific wiring lives ONLY in adapter layer
lifecycle = LifecycleManager()
@lifecycle.on_startup
async def initialize_cache():
"""Framework-agnostic initialization logic."""
from cache import RedisCache
cache = await RedisCache.connect(url="redis://localhost:6379")
print("Cache initialized")
@lifecycle.on_shutdown
async def close_connections():
"""Cleanup without framework dependency."""
print("Connections closed")
```
---
## Constraints
### MUST DO
- Isolate all framework-specific types (imports, classes, functions) in adapter modules — never let them leak into domain or core business logic
- Define stable interfaces before writing any integration code — change interfaces deliberately, not framework APIs
- Implement middleware/plugin chains with explicit priority ordering and composable `(ctx, next) -> result` signatures
- Layer configuration with typed validation (Pydantic, Zod, etc.) and fail-fast semantics for unrecognized keys
- Write integration tests that assert against your stable interfaces, not framework internals, to isolate upgrade risk
### MUST NOT DO
- Import framework types directly in domain service classes or business logic modules
- Monkey-patch framework classes at runtime for extensions — use registered hooks or plugins instead
- Share mutable global state across framework boundaries without explicit synchronization (async locks, thread-local storage)
- Hardcode framework version numbers in adapter logic — use feature detection or abstract over breaking changes
- Bypass framework error-handling conventions to catch exceptions manually — let adapters translate framework errors to domain-specific types
---
## Output Template
When applying this skill, your implementation output must contain:
1. **Boundary Diagram** — ASCII or markdown showing which modules touch the framework directly vs. which use adapters/interfaces
2. **Interface Definitions** - Stable contracts with type signatures and docstrings, placed in core/domain layer
3. **Adapter Implementations** - Thin wrappers mapping framework types to your interfaces, with version notes
4. **Configuration Schema** - Typed config object with validation rules and environment variable mapping
5. **Integration Tests** - Unit tests for adapters + contract tests for plugin/middleware chains
---
## Related Skills
| Skill | Purpose |
|---|---|
| `framework-driven-design` | Design applications that embrace framework constraints (IoC, DI, lifecycle hooks) instead of bypassing them |
| `software-architecture` | Hexagonal architecture patterns to isolate domain logic from all external frameworks |
| `version-migration` | Systematic approach to upgrading frameworks when breaking changes occur |
| `dependency-injection-patterns` | Wire dependencies at bootstrap to enable testability and framework-swapping |
---
## Live References
> Authoritative documentation links for framework integration and extension patterns.
- [Python Packaging User Guide — Plugin Systems](https://packaging.python.org/en/latest/tutorials/create-distributable-packages/)
- [Express.js Middleware Documentation](https://expressjs.com/en/guide/using-middleware.html)
- [FastAPI Dependency Injection System](https://fastapi.tutorial/dependency-injection/)
- [React Hooks Architecture Guide](https://react.dev/reference/react/hooks)
- [TypeScript Interface Design Patterns](https://www.typescriptlang.org/docs/handbook/2/objects.html)
- [Pydantic Configuration Management](https://docs.pydantic.dev/latest/concepts/pydantic_settings/)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!