AbstractStructural equality over the declared props, plus a concrete-class check so that two different Value Objects with coincidentally identical shapes are not confused for one another.
Deliberately a shallow comparison: props are expected to be primitives or other Value Objects. See ../../docs/value-object.md.
Protected StaticrejectHelper for subclasses: fail construction when the attributes do not make sense. A Value Object that cannot be constructed in an invalid state is the cheapest invariant enforcement there is — the rule is upheld by the type simply existing.
A Value Object is defined entirely by its attributes. It has no identity and no lifecycle.
The test: if you replaced this instance with another one carrying the same attributes, would anything in the business change? If no, it is a Value Object. A 10-euro note is interchangeable with any other 10-euro note; a specific copy of a book, bearing barcode
LIB-000031, is not interchangeable with any other copy — the library must know which one is on your shelf at home.Value Objects are immutable. "Changing" one means constructing a new one. This is what makes them safe to share freely across aggregates: nobody can mutate a value you are holding.