---
name: reusability-patterns
description: "Reusability decision framework, naming conventions, and parameterization patterns for rules and components. Cross-referenced by rule-scaffold, mystique-component, feature-plan, use-case-discover."
version: 1.0
relevant-skills: rule-scaffold, mystique-component, feature-plan, use-case-discover, mystique-scaffold, custom-code, feature-explain
load: on-demand
---

# Reusability Patterns for Rules & Components

> **Default assumption: design for reuse unless proven otherwise.**
> Before building any new rule or component, ask: "Could this work on a different entity type, workflow, or page?" If yes — parameterize, name generically, and document reuse examples. If no — explain why it's entity-specific and name it accordingly.

## 1. Reusability Decision Framework

### When to ask

**MANDATORY** at three gates:
1. **Spec time** (use-case-discover Phase 4) — "Does this logic/data apply across entity types?"
2. **Plan time** (feature-plan S7 Rules, S10 UI) — "For each NEW artifact: reusable or scoped?"
3. **Scaffold time** (rule-scaffold, mystique-component) — "Confirm reusability decision before generating files."

### Decision signals

| Signal | Reusable | Entity-Specific |
|--------|:--------:|:---------------:|
| Logic operates on generic concepts (status, attributes, events) | Yes | |
| Logic references entity-specific fields (e.g., `order.items`, `fulfilment.fromAddress`) | | Yes |
| Multiple workflows or pages could use the same behavior | Yes | |
| Business rule is unique to one entity subtype's lifecycle | | Yes |
| Rule does 2+ things that could be split | Split first, then evaluate each piece | |

### Naming convention

| Scope | Rule naming | Component naming |
|-------|-------------|-----------------|
| **Generic (reusable)** | No entity prefix: `SetEntityStatus`, `ValidateRequiredAttributes`, `SendEventWithAttributes` | No entity prefix: `<ns>.content.status-timeline`, `<ns>.content.attribute-card` |
| **Entity-specific** | Entity prefix: `CalculateAppeasementAmount`, `CreateFulfilmentFromSourcingLocation` | Entity prefix: `<ns>.content.order-appeasement-form`, `<ns>.content.fulfilment-pick-list` |

**Rule:** If the name makes sense with a different entity type substituted, it should be generic.

## 2. Rule Reusability Patterns (from OOTB)

### Pattern A: Pure Routing (generic, @ParamString only)

Rules that route events based on configurable parameters. Zero entity-specific logic.

**Example: `SendEvent`**
```java
@ParamString(name = "eventName", description = "Event to fire")
// Works on ORDER, FULFILMENT, CONSIGNMENT — any entity.
// Reusable because the only variable is the event name.
```

**Reuse count:** 40+ workflows use this exact rule with different `eventName` props.

**Other examples:** `ForwardEventToEntity`, `SetState`, `SendEventOnVerifyingAttributeValue`

**Parameterization key:** All variable behavior is in `@ParamString` props. The rule never reads entity-specific fields.

### Pattern B: Configurable Entity Operation (generic, both channels)

Rules that operate on any entity type by parameterizing which fields/attributes to use.

**Example: `SetAttributes` (hypothetical generic)**
```java
@ParamString(name = "attributeMap", description = "JSON map of attribute names to values")
@EventAttribute(name = "dynamicValues")
// Sets arbitrary attributes on any entity type.
// @ParamString defines WHICH attributes; @EventAttribute provides runtime VALUES.
```

**Parameterization key:**
- `@ParamString` for: which attributes to set, which fields to read, status names, setting keys
- `@EventAttribute` for: runtime values from user action forms or upstream events
- The rule reads generic entity APIs (`entity.getAttributes()`, `DynamicUtils.create()`) — not entity-specific getters

### Pattern C: Entity-Specific Logic (scoped, both channels)

Rules with business logic tied to one entity type's data model.

**Example: `CreateFulfilmentFromSourcingLocation`**
```java
@ParamString(name = "fulfilmentType", description = "Subtype to create")
@EventAttribute(name = "sourcingLocationRef")
// ORDER-specific: reads order.items, creates FULFILMENT child.
// Cannot work on RETURN_ORDER or CONSIGNMENT without code changes.
```

**Why not generic:** The rule queries `orderById.items`, constructs `CreateFulfilmentInput` — deeply tied to ORDER->FULFILMENT relationship.

**When C is correct:** The business logic genuinely requires entity-specific GraphQL fields, relationships, or mutations that don't generalize.

## 3. Component Reusability Patterns

### Pattern A: Data-Driven Display (generic)

Components that render data from props without hardcoding field names.

**Example: Status Timeline**
```tsx
// GENERIC: <ns>.content.status-timeline
// Props: attributeName (which attribute holds history), dateFormat, colorMap
// Works on: Order detail, Fulfilment detail, Return Order detail
ComponentRegistry.register(['acme.content.status-timeline'], StatusTimeline, { category: 'content' });

// The component reads: props.data[props.attributeName] — not hardcoded to "orderStatusHistory"
```

**Parameterization key:** `attributeName`, `dateFormat`, `labelField`, `valueField` — all configurable via manifest `props`.

### Pattern B: Entity-Specific Form (scoped)

Components that handle forms with entity-specific field sets.

**Example: Appeasement Form**
```tsx
// SCOPED: <ns>.content.order-appeasement-form
// Renders appeasementAmount, appeasementReason, comment fields
// Dispatches specific event attributes that match @EventAttribute on custom rules
ComponentRegistry.register(['acme.content.order-appeasement-form'], AppeasementForm, { category: 'content' });
```

**Why scoped:** The form fields are tied to the appeasement business process — `appeasementAmount` and `appeasementReason` are not generic concepts.

### Pattern C: Custom FieldRegistry Type (always generic)

FieldRegistry components are inherently reusable — they replace field rendering in ANY user action dialog.

**Example: Currency Input**
```tsx
// ALWAYS GENERIC: registered by type name, used wherever type appears
FieldRegistry.register('currencyInput', CurrencyInput);

// Any workflow user action with attributes[].type = "currencyInput" renders this field
// Works across ORDER, FULFILMENT, CREDIT_MEMO — any entity's user actions
```

### Pattern D: TemplateRegistry Helper (always generic)

Template helpers are pure functions — inherently reusable across any manifest.

**Example:**
```tsx
TemplateRegistry.register('acme.helpers.format-weight', (value, unit) => `${value} ${unit}`);
// Used in any manifest template: {{acme.helpers.format-weight node.weight 'kg'}}
```

## 4. Parameterization Strategy for Reusable Rules

### Making @ParamString enable reuse

Instead of hardcoding entity-specific values, externalize them as rule props:

| Hardcoded (entity-specific) | Parameterized (reusable) |
|---|---|
| `String status = "AWAITING_PICKUP";` | `String status = context.getProp("targetStatus");` |
| `order.getAttributes().get("pickupTime")` | `entity.getAttributes().get(context.getProp("attributeName"))` |
| `if (entity.getType().equals("ORDER"))` | `// Don't check — let workflow placement control which entities run this rule` |
| `String eventName = "FulfilmentCreated";` | `String eventName = context.getProp("eventName");` |

### Generic prop patterns

| Prop Name | Purpose | Enables |
|-----------|---------|---------|
| `entityType` | Which entity type to query/create | Cross-entity reuse |
| `attributeName` | Which attribute to read/write | Cross-field reuse |
| `targetStatus` | Status to set | Cross-workflow reuse |
| `eventName` | Event to fire | Cross-workflow reuse |
| `settingName` | Setting to read config from | Cross-retailer reuse |
| `queryName` | GraphQL query to execute | Cross-entity reuse |

### When NOT to parameterize

- The rule's purpose IS entity-specific (e.g., "calculate appeasement amount from order items")
- Parameterizing would require passing 10+ props — better to have a clear, named rule
- The business logic involves entity relationships that are structurally different per type

## 5. Reusability Checklist (pre-scaffold)

Before generating any rule or component file, confirm:

- [ ] **Checked OOTB catalog:** No existing platform rule/component covers this need
- [ ] **Checked existing custom artifacts:** Searched `accounts/<PROFILE>/SOURCE/backend/` for rules and `accounts/<PROFILE>/SOURCE/frontend/` for components that might already do this or could be extended
- [ ] **Decision made:** Generic or entity-specific? (documented in plan)
- [ ] **Naming follows convention:** Generic = no entity prefix, Scoped = entity prefix
- [ ] **Props designed for reuse:** Variable parts in `@ParamString` (rules) or manifest `props` (components)
- [ ] **Reuse documented:** `@RuleInfo(description=...)` mentions reuse scope; JSDoc/README describes which pages/entities it works on
- [ ] **If scoped:** Confirmed that generalizing would add unreasonable complexity

### How to check existing custom artifacts

**Rules (backend):**
```bash
# Search for existing custom rule classes
find accounts/<PROFILE>/SOURCE/backend/ -name "*.java" -path "*/rules/*" | head -20
# Search by keyword in rule names and annotations
grep -r "@RuleInfo" accounts/<PROFILE>/SOURCE/backend/ --include="*.java" -l
grep -r "attributeName\|targetStatus\|eventName" accounts/<PROFILE>/SOURCE/backend/ --include="*.java" -l
```

**Components (frontend):**
```bash
# Search for existing SDK component registrations
grep -r "ComponentRegistry.register\|FieldRegistry.register\|TemplateRegistry.register" accounts/<PROFILE>/SOURCE/frontend/ --include="*.tsx" --include="*.ts"
# Search by functional keyword
grep -r "timeline\|stepper\|status\|attribute" accounts/<PROFILE>/SOURCE/frontend/ --include="*.tsx" -l
```

**If a matching artifact exists:** Evaluate whether to extend it (add a prop, add a case) vs. create new. Extending is preferred — less maintenance, single deployment.

## 6. Cross-Skill Integration

| Skill | Reusability touchpoint |
|-------|----------------------|
| `/fluent-use-case-discover` | Phase 4 Q4.6: "Does this logic/data apply across entity types?" |
| `/fluent-feature-plan` | S7 Rules: reusability decision per NEW rule. S10 UI: reusability decision per NEW component |
| `/fluent-rule-scaffold` | Reusability-First Design section (existing). References this doc. |
| `/fluent-mystique-component` | Reusability-First Design section (existing). References this doc. |
| `/fluent-mystique-scaffold` | Project-level reuse strategy question at creation time. |
| `coding-standards.md` | Java Rules and Frontend sections include reusability conventions. |
