VO на практике бывает очень локальным, поэтому не всегда есть смысл их выделять в отдельные классы и можно вообще оставить.
То, что логика локальная, — это не значит, что её необходимо оставлять как есть (иначе получатся анемичные объекты с логикой в агрегатах). Конкретно в моём случае эта логика постоянно переиспользовалась в разных местах, в том числе за пределами агрегата (для построения графиков, отчётиков и т. д.).
Сам VO распространяется не на все контексты — ограничение таки есть (это сам контекст как раз). Также в тексте есть про общие типы, которые могут использоваться везде. Это как раз норм, так как логика 100% всегда одинаковая (email он и в Африке email), но там тоже есть оговорки (подробнее в статье).
Второй комментарий придётся разобрать по частям.
Получается, во-первых, что VO должен что-то знать об объектах, из которых он может быть сконвертирован (например, IP-адрес знает о том, что может быть создан из строки и о формате этой самой строки),
В целом это норм, обычно на практике проблем не вызывает. При работе с VO мы примерно представляем, из чего собирается объект (email — строка, денежка —
VO должен заботиться о том, чтобы отдать клиенту ошибку создания в красивом и понятном виде. А в этом последнем трудно соперничать со специальными инструментами валидации, которые готовы тебе указать локализацию вплоть до json-path свойства и позиции в строке.
Вот как раз-таки и не должен. У VO клиент — это вызывающий его код, а не конечный потребитель. Вы бы знали, сколько раз я переделывал с JSON-ов не на JSON-ы и обратно, подключал вообще не HTTP-каналы и т. д. Если логика отображения заложена в VO, вы никогда больше не сможете поменять бизнес-логику, потому что «ой-ой, у нас отображение формочки сломалось».
Плюс множество встроенных валидаторов этих инструментов, которые придётся тогда игнорировать и велосипедить.
«А минусы?» Тут возникает проблема — а какой валидатор брать? Спринговый? Хиберовский? Ещё какой-то? Выпилили/обновили Хибер — а что теперь делать? Поэтому вся логика валидации аккуратно запихана в сам домен. Вообще мы используем инструмент для валидации в виде ArrowKt, про это тоже есть в посте. Он действительно упрощает сбор ошибок, и мы считаем его частью домена на системном уровне.
На другой стороне инфраструктуры (ORM) тоже достаточно заморочек с сериализацией/поиском по ValueObject. Entity Framework дотнетовский за последние годы многому в этом направлении научился, но всё равно сохраняются ограничения на применимость VO-подхода "мощностью" нижележащего ORM-фреймворка.
Собсна, в этом и заключается суть чистой архитектуры — отделить инфру от домена. Очень любим такой подход, потому что уже наелись. Про сохранение тоже в статье было — проблем, как правило, не вызывает, если умело пользоваться.