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");
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.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.