Why architecture matters here
Records fail when misused. Treating them like beans (mutable) misses the point. Ignoring shallow immutability (a record holding a mutable list) leaks state. Using records where you need inheritance frustrates.
The architecture matters because records + sealed interfaces + pattern matching are a coherent style — data-oriented programming. Adopting the style pays.
The architecture: every piece explained
The top strip is the mechanics. Record declaration: `record Point(int x, int y)`. Auto members: accessors, equals, hashCode, toString synthesized. Immutability is shallow; components are final, but if a component references a mutable type it can still change. Compact ctor allows validation without listing all fields.
The middle row is language integration. Pattern matching deconstructs records: `if (obj instanceof Point(int x, int y))`. Sealed interfaces enable exhaustive switches. Serialization works with default mechanisms. Interop with beans, frameworks (Spring, Jackson) and JSON libraries.
The lower rows are practice. Refactoring classes into records is straightforward when the class is truly a data holder. Best practices: use records at API boundaries (DTOs) and for value objects; not as replacements for full domain classes. Ops: annotations (Jackson, JPA) work; tooling supports.
End-to-end flow
End-to-end: a REST API returns customer records as `record CustomerDto(String id, String name, LocalDate joined)`. Auto-generated equals + hashCode make caching easy. Pattern matching in the service: `switch (event) { case OrderPlaced(...) -> ...; case OrderCancelled(...) -> ...; }`. Sealed interface `Event` makes the switch exhaustive; the compiler catches unhandled cases when a new event type is added.