ValueObject, SingleValueObject, and value equality
There are two base classes, and picking the right one is a small but real convention. SingleValueObject<TSelf, TValue> is for a value object wrapping ONE primitive: a flight number (string), a bounded percentage (decimal). It gives you Value, equality, and ToString for free, so a subclass only needs a private constructor and a Create.
For a value object with MORE than one field, extend ValueObject<T> directly and override GetEqualityComponents, yielding each field. Equality is then a SequenceEqual over those components: two value objects are equal when all their components match. FlightNoteVo (flight key + body) is the two-field case.
One more real detail worth copying. Some single-value VOs expose a second factory: public static T From(value) => Create(value).Value;. It's a shorthand for domain-internal code and unit tests that throws if the value is invalid. The real XML doc is blunt: "Do NOT use at application boundaries, prefer Create and inspect the Result." Two factories, two trust levels: Create at the edge, From only where you already know the value is good.
Try it yourself
Write a SingleValueObject
Feel how little a single-value VO needs. Model it on FlightNumberVo above.
Tick every step to confirm you did it.
Pick the base class
You're modelling a Money value object with an amount (decimal) AND a currency (string). Which base?
Create versus From
Know which factory goes where.
A VO has both a Create and a From factory. Which do you call when parsing a value from an incoming API request, and why?