Move Field

Symptom

A field on class A whose value is determined by data on class B (`customer.discountRate` set from `customer.plan.kind`) — the field belongs with the data it's derived from.

Goal

Each field belongs to the class that owns its lifecycle; cross-class reaching disappears.

Before the refactoring

class Customer {
plan;
discountRate;
}
// every customer in a given plan gets the same rate:
customers.forEach(c => c.discountRate = c.plan.kind === 'gold' ? 0.15 : 0.05);

After the refactoring

class Plan {
kind;
discountRate;
}
class Customer { plan; }
customer.plan.discountRate;
Example source: Illustrative example written for this site, not a quotation from any source.
Pressure

Every consumer of A must maintain the cross-class invariant; refactoring B's data shape breaks A in non-obvious ways; the field's true owner is invisible.

Tradeoff

Every reader of the original class now reaches across the new class boundary — coupling drops at the field's new home but reappears at each consumer.

Relief

Class boundaries align with data ownership; mutations are local; refactoring becomes safer.

Trap

Moving fields whose presence on the original class is part of the class's identity — even when the value derives elsewhere, moving the field can break consumer expectations.