# Plugin / Extensibility / IoC

> Every principle in this category is listed as a record.

Page: Ontology · Principles
Canonical: https://banes-lab.com/ontology#arch-category-plugin-extensibility-ioc

Every principle in this category is listed as a record. Each record carries its kind, its severity, the scopes it applies at and the layer it lives in, then the edge relations that join it to other records, the records that point back at it, the contracts that answer to it and the tensions it takes part in. The descriptors say how it is violated, detected, measured, repaired and enforced. Where the record carries one, an exemplar shows the shape before and after the principle is applied.

Relations diagram

The relations inside this category.

```mermaid
flowchart LR
n_plugin_architecture["Plugin Architecture"]
n_extension_points["Extension Points"]
n_inversion_of_control["Inversion of Control (IoC)"]
n_dependency_injection["Dependency Injection"]
n_service_registry["Service Registry"]
n_registry_pattern["Registry Pattern"]
n_service_locator_pattern["Service Locator Pattern"]
n_feature_toggle["Feature Toggle"]
n_plugin_architecture --> n_extension_points
n_extension_points --> n_plugin_architecture
n_inversion_of_control --> n_dependency_injection
```

### Plugin Architecture

- Kind: [style](https://banes-lab.com/records/kind/style.md)
- Severity: contextual
- Scope: component, runtime, system
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Requires
[Extension Points](https://banes-lab.com/records/arch/extension-points.md), [Stable Interfaces](https://banes-lab.com/records/arch/stable-interfaces.md), [Discovery](https://banes-lab.com/records/lex/discovery.md)

Reinforces
[Open/Closed Principle (OCP)](https://banes-lab.com/records/arch/open-closed.md), [Modularity](https://banes-lab.com/records/arch/modularity.md)

Enables
[Runtime Extensibility](https://banes-lab.com/records/arch/runtime-extensibility.md)

In tension with
[Static Analysis](https://banes-lab.com/records/arch/static-analysis.md), [Security](https://banes-lab.com/records/lex/security.md)

Conflicts with
[Hardcoded Extensions](https://banes-lab.com/records/lex/hardcoded-extensions.md)

Referenced by
[Modularity](https://banes-lab.com/records/arch/modularity.md), [Composability](https://banes-lab.com/records/arch/composability.md), [Replaceability](https://banes-lab.com/records/arch/replaceability.md), [Self-Describing Architecture](https://banes-lab.com/records/arch/self-describing-architecture.md), [Extension Points](https://banes-lab.com/records/arch/extension-points.md), [Runtime Discovery](https://banes-lab.com/records/arch/runtime-discovery.md), [Runtime Extensibility](https://banes-lab.com/records/arch/runtime-extensibility.md), [Open/Closed Principle (OCP)](https://banes-lab.com/records/arch/open-closed.md)

Tensions
[Plugin Architecture Static Analysis](https://banes-lab.com/records/tension/plugin-architecture-static-analysis.md), [Plugin Architecture Security](https://banes-lab.com/records/tension/plugin-architecture-security.md)

Violated by
core importing plugin implementations

Detected by
direct plugin imports, central switch for plugins

Measured by
plugin isolation score

Refactored by
Introduce SPI, Add Registry, Extract Extension Point

Enforced by
plugin contract tests, dependency rules

Before

```typescript
class FooApp {
run() { new FooExport().run(); new BarExport().run(); }
}
```

After

```typescript
interface FooPlugin { name: string; setup(app: FooApp): void; }
class FooApp {
constructor(private readonly plugins: readonly FooPlugin[]) {}
run() { this.plugins.forEach(plugin => plugin.setup(this)); }
}
```

### Extension Points

- Kind: [mechanism](https://banes-lab.com/records/kind/mechanism.md)
- Severity: recommended
- Scope: framework, plugin, module
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Requires
[Stable Interfaces](https://banes-lab.com/records/arch/stable-interfaces.md), [Contracts](https://banes-lab.com/records/lex/contracts.md)

Reinforces
[Open/Closed Principle (OCP)](https://banes-lab.com/records/arch/open-closed.md), [Plugin Architecture](https://banes-lab.com/records/arch/plugin-architecture.md)

Enables
[Third-Party Extension](https://banes-lab.com/records/lex/third-party-extension.md)

In tension with
[API Surface Growth](https://banes-lab.com/records/lex/api-surface-growth.md)

Conflicts with
[Closed Core](https://banes-lab.com/records/lex/closed-core.md)

Referenced by
[Plugin Architecture](https://banes-lab.com/records/arch/plugin-architecture.md), [Runtime Extensibility](https://banes-lab.com/records/arch/runtime-extensibility.md), [Open/Closed Principle (OCP)](https://banes-lab.com/records/arch/open-closed.md)

Tensions
[Extension Points API Surface Growth](https://banes-lab.com/records/tension/api-surface-growth-extension-points.md)

Violated by
modifying internals to add behavior

Detected by
repeated core edits for variants

Measured by
extension coverage

Refactored by
Add Hook, Add SPI, Extract Interface

Enforced by
extension tests, API review

Before

```typescript
function saveFoo(foo: Foo) {
validateFoo(foo);
fooStore.save(foo);
sendFooEmail(foo);
}
```

After

```typescript
type FooHooks = { beforeSave: Array<(foo: Foo) => void>; afterSave: Array<(foo: Foo) => void> };
function saveFoo(foo: Foo, hooks: FooHooks) {
hooks.beforeSave.forEach(hook => hook(foo));
fooStore.save(foo);
hooks.afterSave.forEach(hook => hook(foo));
}
```

### Inversion of Control (IoC)

- Kind: [principle](https://banes-lab.com/records/kind/principle.md)
- Severity: recommended
- Scope: framework, runtime, component
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Requires
[Abstraction](https://banes-lab.com/records/arch/abstraction.md), [Composition Root](https://banes-lab.com/records/lex/composition-root.md)

Reinforces
[Dependency Inversion Principle (DIP)](https://banes-lab.com/records/arch/dependency-inversion.md), [Dependency Injection](https://banes-lab.com/records/arch/dependency-injection.md)

Enables
[Framework Control Flow](https://banes-lab.com/records/lex/framework-control-flow.md), [Plugins](https://banes-lab.com/records/lex/plugins.md)

In tension with
[Traceability](https://banes-lab.com/records/arch/traceability.md)

Conflicts with
[Direct Control Ownership](https://banes-lab.com/records/lex/direct-control-ownership.md)

Tensions
[Inversion of Control (IoC) Traceability](https://banes-lab.com/records/tension/inversion-of-control-ioc-traceability.md)

Violated by
application manually controlling framework-owned lifecycle

Detected by
scattered object lifecycle construction

Measured by
composition centralization

Refactored by
Introduce Container, Extract Composition Root

Enforced by
lifecycle rules

Before

```typescript
class FooJob {
run() {
const store = new SqlFooStore();
return store.save(makeFoo());
}
}
```

After

```typescript
class FooJob {
constructor(private readonly make: () => Foo, private readonly store: FooStore) {}
run() { return this.store.save(this.make()); }
}
container.run(FooJob);
```

### Dependency Injection

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Severity: recommended
- Scope: class, module, component
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Requires
[Abstraction](https://banes-lab.com/records/arch/abstraction.md), [Composition Root](https://banes-lab.com/records/lex/composition-root.md)

Reinforces
[Dependency Inversion Principle (DIP)](https://banes-lab.com/records/arch/dependency-inversion.md), [Testability](https://banes-lab.com/records/arch/testability.md)

Enables
[Mocking](https://banes-lab.com/records/lex/mocking.md), [Replaceability](https://banes-lab.com/records/arch/replaceability.md)

In tension with
[Constructor Complexity](https://banes-lab.com/records/lex/constructor-complexity.md)

Conflicts with
[Hardcoded Instantiation](https://banes-lab.com/records/lex/hardcoded-instantiation.md), [Ambient Context](https://banes-lab.com/records/arch/ambient-context.md)

Referenced by
[Interface-Based Design](https://banes-lab.com/records/arch/interface-based-design.md), [Singleton Pattern](https://banes-lab.com/records/arch/singleton-pattern.md), [Inversion of Control (IoC)](https://banes-lab.com/records/arch/inversion-of-control.md), [Dependency Inversion Principle (DIP)](https://banes-lab.com/records/arch/dependency-inversion.md)

Tensions
[Dependency Injection Constructor Complexity](https://banes-lab.com/records/tension/constructor-complexity-dependency-injection.md)

Violated by
newing dependencies inside business logic

Detected by
direct construction of external dependencies

Measured by
injected dependency ratio

Refactored by
Inject Constructor Parameter, Add Factory

Enforced by
lint rules, dependency review

Before

```typescript
class FooService {
private readonly clock = new SystemClock();
private readonly store = new SqlFooStore();
}
```

After

```typescript
class FooService {
constructor(private readonly clock: Clock, private readonly store: FooStore) {}
}
```

### Service Registry

- Kind: [mechanism](https://banes-lab.com/records/kind/mechanism.md)
- Severity: contextual
- Scope: runtime, service, plugin
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Requires
[Registration Protocol](https://banes-lab.com/records/lex/registration-protocol.md)

Reinforces
[Discovery](https://banes-lab.com/records/lex/discovery.md), [Runtime Binding](https://banes-lab.com/records/arch/runtime-binding.md)

Enables
[Dynamic Resolution](https://banes-lab.com/records/lex/dynamic-resolution.md)

In tension with
[Registry Availability](https://banes-lab.com/records/lex/registry-availability.md)

Conflicts with
[Hardcoded Lookup](https://banes-lab.com/records/lex/hardcoded-lookup.md)

Referenced by
[Service Discovery](https://banes-lab.com/records/arch/service-discovery.md)

Tensions
[Service Registry Registry Availability](https://banes-lab.com/records/tension/registry-availability-service-registry.md)

Violated by
manual endpoint/plugin lookup

Detected by
static lookup tables

Measured by
registry coverage

Refactored by
Register Service, Add Discovery Client

Enforced by
startup checks, [health checks](https://banes-lab.com/records/arch/health-checks.md)

Before

```typescript
const fooService = new FooService(new SqlFooStore());
const barService = new BarService(new SqlBarStore());
```

After

```typescript
const services = new ServiceRegistry();
services.register("FooStore", () => new SqlFooStore());
services.register("FooService", r => new FooService(r.resolve("FooStore")));
```

### Registry Pattern

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Severity: contextual
- Scope: runtime, module, plugin
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Requires
[Keyed Registration](https://banes-lab.com/records/lex/keyed-registration.md)

Reinforces
[Discovery](https://banes-lab.com/records/lex/discovery.md), [Factory Pattern](https://banes-lab.com/records/arch/factory-pattern.md)

Enables
[Dynamic Lookup](https://banes-lab.com/records/lex/dynamic-lookup.md)

In tension with
[Global State](https://banes-lab.com/records/lex/global-state.md)

Conflicts with
[Direct Reference](https://banes-lab.com/records/lex/direct-reference.md)

Tensions
[Registry Pattern Global State](https://banes-lab.com/records/tension/global-state-registry-pattern.md)

Violated by
ungoverned global registry

Detected by
mutable global maps without lifecycle

Measured by
registry consistency

Refactored by
Encapsulate Registry, Add Typed Keys

Enforced by
registry validation

Before

```typescript
function makeFoo(kind: string) {
if (kind === "foo") return new Foo1();
if (kind === "bar") return new Bar();
throw new Error("unknown kind");
}
```

After

```typescript
type FooFactory = () => Foo;
const registry = new Map<string, FooFactory>();
export const registerFoo = (kind: string, factory: FooFactory) => registry.set(kind, factory);
export const makeFoo = (kind: string) => registry.get(kind)?.() ?? fail(`unknown ${kind}`);
```

### Service Locator Pattern

- Kind: [pattern](https://banes-lab.com/records/kind/pattern.md)
- Severity: discouraged
- Scope: runtime, dependency access
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Requires
[Registry](https://banes-lab.com/records/lex/registry.md)

Reinforces
[Runtime Lookup](https://banes-lab.com/records/lex/runtime-lookup.md)

Enables
[Late Resolution](https://banes-lab.com/records/lex/late-resolution.md)

In tension with
[Testability](https://banes-lab.com/records/arch/testability.md), [Dependency Inversion Principle (DIP)](https://banes-lab.com/records/arch/dependency-inversion.md), [Explicit Dependencies](https://banes-lab.com/records/lex/explicit-dependencies.md)

Conflicts with
none

Tensions
[Service Locator Pattern Testability](https://banes-lab.com/records/tension/service-locator-pattern-testability.md), [Service Locator Pattern Dependency Inversion Principle (DIP)](https://banes-lab.com/records/tension/dependency-inversion-principle-dip-service-locator-pattern.md), [Service Locator Pattern Explicit Dependencies](https://banes-lab.com/records/tension/explicit-dependencies-service-locator-pattern.md)

Violated by
hidden dependencies through global locator

Detected by
service locator calls inside domain logic

Measured by
hidden dependency count

Refactored by
Replace with Dependency Injection

Enforced by
banned API rules

Before

```typescript
class FooController {
save(foo: Foo) {
const store = serviceLocator.resolve<FooStore>("FooStore");
return store.save(foo);
}
}
```

After

```typescript
class FooController {
constructor(private readonly store: FooStore) {}
save(foo: Foo) { return this.store.save(foo); }
}
```

### Feature Toggle

- Kind: [mechanism](https://banes-lab.com/records/kind/mechanism.md)
- Severity: contextual
- Scope: application, release, runtime
- Layer: [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)

Details

Requires
[Externalized Flag State](https://banes-lab.com/records/lex/externalized-flag-state.md)

Reinforces
[Continuous Delivery](https://banes-lab.com/records/lex/continuous-delivery.md), [Runtime Extensibility](https://banes-lab.com/records/arch/runtime-extensibility.md)

Enables
[Decoupled Deploy and Release](https://banes-lab.com/records/lex/decoupled-deploy-and-release.md), [Gradual Rollout](https://banes-lab.com/records/lex/gradual-rollout.md)

In tension with
[Flag Debt](https://banes-lab.com/records/lex/flag-debt.md)

Conflicts with
[Hardcoded Branch Constant](https://banes-lab.com/records/lex/hardcoded-branch-constant.md)

Tensions
[Feature Toggle Flag Debt](https://banes-lab.com/records/tension/feature-toggle-flag-debt.md)

Violated by
release paths gated by a hardcoded boolean constant

Detected by
compile-time flags requiring redeploy to flip

Measured by
redeploys per behavior change

Refactored by
Introduce Runtime Feature Flags

Enforced by
release review

Before

```typescript
if (NEW_FOO_FLOW_ENABLED) runNewFooFlow();
else runOldFooFlow();
```

After

```typescript
if (featureFlags.enabled("new-foo-flow", { user, percentage: 10 })) runNewFooFlow();
else runOldFooFlow();
```

## Links to

- [style](https://banes-lab.com/records/kind/style.md)
- [Extensibility Core](https://banes-lab.com/records/layer/extensibility-core.md)
- [Extension Points](https://banes-lab.com/records/arch/extension-points.md)
- [Stable Interfaces](https://banes-lab.com/records/arch/stable-interfaces.md)
- [Discovery](https://banes-lab.com/records/lex/discovery.md)
- [Open/Closed Principle (OCP)](https://banes-lab.com/records/arch/open-closed.md)
- [Modularity](https://banes-lab.com/records/arch/modularity.md)
- [Runtime Extensibility](https://banes-lab.com/records/arch/runtime-extensibility.md)
- [Static Analysis](https://banes-lab.com/records/arch/static-analysis.md)
- [Security](https://banes-lab.com/records/lex/security.md)
- [Hardcoded Extensions](https://banes-lab.com/records/lex/hardcoded-extensions.md)
- [Composability](https://banes-lab.com/records/arch/composability.md)
- [Replaceability](https://banes-lab.com/records/arch/replaceability.md)
- [Self-Describing Architecture](https://banes-lab.com/records/arch/self-describing-architecture.md)
- [Runtime Discovery](https://banes-lab.com/records/arch/runtime-discovery.md)
- [Plugin Architecture / Static Analysis](https://banes-lab.com/records/tension/plugin-architecture-static-analysis.md)
- [Plugin Architecture / Security](https://banes-lab.com/records/tension/plugin-architecture-security.md)
- [mechanism](https://banes-lab.com/records/kind/mechanism.md)
- [Contracts](https://banes-lab.com/records/lex/contracts.md)
- [Plugin Architecture](https://banes-lab.com/records/arch/plugin-architecture.md)
- [Third-Party Extension](https://banes-lab.com/records/lex/third-party-extension.md)
- [API Surface Growth](https://banes-lab.com/records/lex/api-surface-growth.md)
- [Closed Core](https://banes-lab.com/records/lex/closed-core.md)
- [Extension Points / API Surface Growth](https://banes-lab.com/records/tension/api-surface-growth-extension-points.md)
- [principle](https://banes-lab.com/records/kind/principle.md)
- [Abstraction](https://banes-lab.com/records/arch/abstraction.md)
- [Composition Root](https://banes-lab.com/records/lex/composition-root.md)
- [Dependency Inversion Principle (DIP)](https://banes-lab.com/records/arch/dependency-inversion.md)
- [Dependency Injection](https://banes-lab.com/records/arch/dependency-injection.md)
- [Framework Control Flow](https://banes-lab.com/records/lex/framework-control-flow.md)
- [Plugins](https://banes-lab.com/records/lex/plugins.md)
- [Traceability](https://banes-lab.com/records/arch/traceability.md)
- [Direct Control Ownership](https://banes-lab.com/records/lex/direct-control-ownership.md)
- [Inversion of Control (IoC) / Traceability](https://banes-lab.com/records/tension/inversion-of-control-ioc-traceability.md)
- [pattern](https://banes-lab.com/records/kind/pattern.md)
- [Testability](https://banes-lab.com/records/arch/testability.md)
- [Mocking](https://banes-lab.com/records/lex/mocking.md)
- [Constructor Complexity](https://banes-lab.com/records/lex/constructor-complexity.md)
- [Hardcoded Instantiation](https://banes-lab.com/records/lex/hardcoded-instantiation.md)
- [Ambient Context](https://banes-lab.com/records/arch/ambient-context.md)
- [Interface-Based Design](https://banes-lab.com/records/arch/interface-based-design.md)
- [Singleton Pattern](https://banes-lab.com/records/arch/singleton-pattern.md)
- [Inversion of Control (IoC)](https://banes-lab.com/records/arch/inversion-of-control.md)
- [Dependency Injection / Constructor Complexity](https://banes-lab.com/records/tension/constructor-complexity-dependency-injection.md)
- [Registration Protocol](https://banes-lab.com/records/lex/registration-protocol.md)
- [Runtime Binding](https://banes-lab.com/records/arch/runtime-binding.md)
- [Dynamic Resolution](https://banes-lab.com/records/lex/dynamic-resolution.md)
- [Registry Availability](https://banes-lab.com/records/lex/registry-availability.md)
- [Hardcoded Lookup](https://banes-lab.com/records/lex/hardcoded-lookup.md)
- [Service Discovery](https://banes-lab.com/records/arch/service-discovery.md)
- [Service Registry / Registry Availability](https://banes-lab.com/records/tension/registry-availability-service-registry.md)
- [Health Checks](https://banes-lab.com/records/arch/health-checks.md)
- [Keyed Registration](https://banes-lab.com/records/lex/keyed-registration.md)
- [Factory Pattern](https://banes-lab.com/records/arch/factory-pattern.md)
- [Dynamic Lookup](https://banes-lab.com/records/lex/dynamic-lookup.md)
- [Global State](https://banes-lab.com/records/lex/global-state.md)
- [Direct Reference](https://banes-lab.com/records/lex/direct-reference.md)
- [Registry Pattern / Global State](https://banes-lab.com/records/tension/global-state-registry-pattern.md)
- [Registry](https://banes-lab.com/records/lex/registry.md)
- [Runtime Lookup](https://banes-lab.com/records/lex/runtime-lookup.md)
- [Late Resolution](https://banes-lab.com/records/lex/late-resolution.md)
- [Explicit Dependencies](https://banes-lab.com/records/lex/explicit-dependencies.md)
- [Service Locator Pattern / Testability](https://banes-lab.com/records/tension/service-locator-pattern-testability.md)
- [Service Locator Pattern / Dependency Inversion Principle (DIP)](https://banes-lab.com/records/tension/dependency-inversion-principle-dip-service-locator-pattern.md)
- [Service Locator Pattern / Explicit Dependencies](https://banes-lab.com/records/tension/explicit-dependencies-service-locator-pattern.md)
- [Externalized Flag State](https://banes-lab.com/records/lex/externalized-flag-state.md)
- [Continuous Delivery](https://banes-lab.com/records/lex/continuous-delivery.md)
- [Decoupled Deploy and Release](https://banes-lab.com/records/lex/decoupled-deploy-and-release.md)
- [Gradual Rollout](https://banes-lab.com/records/lex/gradual-rollout.md)
- [Flag Debt](https://banes-lab.com/records/lex/flag-debt.md)
- [Hardcoded Branch Constant](https://banes-lab.com/records/lex/hardcoded-branch-constant.md)
- [Feature Toggle / Flag Debt](https://banes-lab.com/records/tension/feature-toggle-flag-debt.md)

## Linked from

- [The layer topology](https://banes-lab.com/ontology/schema/the-layer-topology.md)
- [The membership](https://banes-lab.com/ontology/schema/the-membership.md)
