plugins/architecture/skills/data-oriented-architecture/SKILL.md
Apply when encountering switch/if-else dispatch on entity type, designing entity systems, or refactoring toward extensibility. Provides registry-based dispatch, capability composition, and infrastructure-first patterns. Complements solid-architecture.
npx skillsauth add rbergman/dark-matter-marketplace data-oriented-architectureInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Activate this skill when:
Separate data from behavior, dispatch via registry.
Entity = Pure Data (what it IS) + type discriminator
Definition = Bundled Behavior (what it DOES)
Registry = Type → Definition mapping (HOW to dispatch)
Switch statements scattered throughout codebase:
// Scattered in rendering.ts
switch (entity.type) {
case 'typeA': renderA(entity); break;
case 'typeB': renderB(entity); break;
}
// Scattered in update.ts
switch (entity.type) {
case 'typeA': updateA(entity); break;
case 'typeB': updateB(entity); break;
}
// Adding new type = edit N files
Single registry bundling all type-specific behavior:
// definitions.ts - ONE location for all type-specific code
const DEFS: Record<EntityType, Definition> = {
typeA: { render: renderA, update: updateA, ... },
typeB: { render: renderB, update: updateB, ... },
};
// Consumers dispatch generically
DEFS[entity.type].render(entity, ctx);
DEFS[entity.type].update(entity, dt);
// Adding new type = ONE registry entry, ZERO consumer changes
Definition interface with all operationsDEFS: Record<Type, Definition> registrygetDef(type): Definition helpergetDef(entity.type).operation()Record, Rust match)Not all entities need all behaviors. Deep inheritance or marker interfaces create coupling.
Optional capability configs with type guards:
interface Definition<T> {
// Required for all
create(): T;
render(): void;
// Optional capabilities - entities opt-in
collision?: CollisionConfig;
physics?: PhysicsConfig;
persistence?: PersistenceConfig;
}
// Type guard for safe access
function hasCollision(def): def is Definition & { collision: CollisionConfig } {
return def.collision !== undefined;
}
// Consumer checks capability
if (hasCollision(def)) {
collisionSystem.register(entity, def.collision);
}
BaseDefinition (create, render, layer)
↓ extends
DomainDefinition (domain-specific: AI, weapons)
↓ implemented by
ConcreteDefinitions (typeA, typeB, typeC)
// Base - works for any domain
interface EntityDefinition<TState, TType> {
type: TType;
create(pos): TState;
update?(entity, ctx): void;
render(entity, ctx): void;
collision?: CollisionConfig;
physics?: PhysicsConfig;
}
// Domain-specific extension
interface EnemyDefinition extends EntityDefinition<EnemyState, EnemyType> {
aiStrategy: AIStrategy;
weapons: WeaponConfig[];
}
// Another domain
interface PickupDefinition extends EntityDefinition<PickupState, PickupType> {
onCollect(collector): void;
floatAnimation: AnimationConfig;
}
Functions with many parameters, hard to extend.
Bundle related parameters into context objects:
// Bad - hard to extend
function update(entity, dt, playerPos, playerVel, gravity, time) { ... }
// Good - extensible
interface UpdateContext {
dt: number;
playerPos: Vec2;
playerVel: Vec2;
// Easy to add fields without breaking signatures
}
function update(entity, ctx: UpdateContext) { ... }
interface BaseContext { dt: number; }
interface AIContext extends BaseContext { playerPos: Vec2; threats: Entity[]; }
interface RenderContext { graphics: Graphics; screenPos: Vec2; scale: number; }
If writing a switch statement on entity type, infrastructure is missing.
Adding new type requires editing N files. Fix: Consolidate into registry.
SpecialEnemy extends FlyingEnemy extends Enemy extends Entity
Fix: Capability composition.
interface Entity {
weapon?: Weapon; // null checks everywhere
}
Fix: Separate capability with type guard.
Creating registry for 1 type. Fix: Wait for second type to validate pattern.
Definition with 50 fields for every possible behavior. Fix: Required base + optional capabilities.
Use language features to ensure all types are handled:
// TypeScript - Record requires all keys
const DEFS: Record<EntityType, Definition> = {
// Compiler error if type missing
};
// Helper for switch exhaustiveness
function assertNever(x: never): never {
throw new Error(`Unexpected: ${x}`);
}
When designing entity systems:
development
Initialize a new repository with standard scaffolding - git, gitignore, AGENTS.md, justfile, mise, beads, and timbers. Use when starting a new project or setting up an existing repo for Claude Code workflows.
data-ai
Activate at session start when using Agent Teams for complex multi-agent work. Establishes team lead role with delegation protocols, teammate spawning, model selection, and beads integration. You coordinate the team; teammates implement.
data-ai
Use when creating a worktree, setting up a worktree, starting feature work that needs isolation, or before executing implementation plans. Covers git worktree creation under .worktrees/, gitignore setup, beads integration, and merge guardrails.
data-ai
Activate when you are a delegated subagent (not the orchestrator). Establishes subagent protocol with terse returns, details to history/, file ownership boundaries, and escalation rules. You implement; orchestrator reviews and commits.