Good Code
The good version copies the collection at construction time, so later caller mutation cannot change the snapshot. Its fields are final through the record shape.
Lesson 02
Protect domain snapshots from outside mutation by copying collections and exposing immutable views.
public record OrderSnapshot(OrderId id, List<LineItem> items) {
public OrderSnapshot {
Objects.requireNonNull(id, "id");
// Copy the incoming list so outside code cannot mutate this snapshot.
items = List.copyOf(items);
}
public Money total() {
return items.stream()
.map(LineItem::subtotal)
.reduce(Money.zero(), Money::plus);
}
}public class OrderSnapshot {
private final List<LineItem> items;
public OrderSnapshot(List<LineItem> items) {
// This keeps the caller's mutable list reference.
this.items = items;
}
public List<LineItem> items() {
return items;
}
}The good version copies the collection at construction time, so later caller mutation cannot change the snapshot. Its fields are final through the record shape.
The bad version stores and returns the same mutable list. Any code with a reference can add, remove, or reorder items after the object was created.