Value Object posiada kilka charakerystyk, o których warto tu wspomnieć i w mojej opini różni się diametralnie od DTO.
Ulotność/krótkie życie
W pierwszej kolejności przy porównywaniu dwóch Value Object'ów ze sobą nie interesuje nas ich referencja tylko to co hermetyzują w środku. To znaczy, że jeśli chcemy porównać obie klasy z dużym pradopodobieństwem napiszemy swoją metodę "equals". Skąd bierze się ich ulotność? Możemy wziąć na tapet przykład zamodelowania klasy Money. Taka klasa będzie przychowywać nominał oraz walutę, która jest widoczna na banknocie. Z racji tego - możemy co chwilę tworzyć obiekt new Money(20, 'PLN') oraz go kasować, ponieważ tak na prawdę interesuję nas tylko wartość. W NASZYM KONTEŚCIE nie musimy śledzić co dzieje się z tymi pieniędzmi. Gdybyśmy implementowali za to klasę Money dla Narodowego Banku Polskiego. To sytuacja się zmienia. Wtedy taką kasę nazwalibyśmy encją w rozumieniu DDD i z wysokim pradopodobieństwem taka implementacją posiadałaby ID i cały system śledzenia ze statusami.
Immutability/brak side effect'ów
Jeśli chcemy zsumować dwie kwoty ze sobą to możemy zrobić coś takiego:
Kopiuj
class Money {
private value: number;
constructor(value: number) {
// tutaj walidacja jesli jest potrzebna
}
public add(other: Money): Money {
return new Money(this.value + other.asNumber());
}
}
W tym przykładzie jak możesz zauważyć używam metody "other.asNumber()", która służy do "projekcji" wewnetrzego atrybutu klasy Money. To dosyć ważne, ponieważ Value Object z definicji wyklucza gettery i settery. Ale to nie jest ważne. Ważne jest jak wygląda metoda do sumowania obu kwot. Za każdym razem zachowujemy silną hermetyzację i nie modyfikujemy bebechów takiej klasy za pomocą settera. Po prostu przy każdej takiej operacji staramy się tworzyć nowy obiekt z nową wartością.
Zastępowalność
To wynika z punktu Ulotność/krótkie życie. Z racji tego, że taki obiekt "żyje" bardzo, krótko i interesuję nas tylko wewnetrzna wartość to w takim wypadku możemy z łatwością zstępować takie obiekty nowymi.
Domena
VO posiada przede wszystkim metody, które są silnie powiązane z domeną klienta.
Oto inna implementacja Value Object'u dla biegu w skrzyni biegów w samochodzie. Oczywiście mój model rzeczywistości jest uproszczony na potrzeby przykładu 
Kopiuj
class Gear {
private gear: number;
constructor(gear: number) {
if (gear < 0) {
throw new Error('Negative representation of gear');
}
this.gear = gear;
}
public next(): Gear {
return new Gear(this.gear + 1);
}
public previous(): Gear {
return new Gear(this.gear - 1);
}
public equals(otherGear: Gear): boolean {
return this.gear === otherGear.toIntValue();
}
public greaterThan(gear: Gear): boolean {
return this.gear > gear.toIntValue();
}
public lowerOrEqualTo(gear: Gear): boolean {
return this.gear <= gear.toIntValue();
}
public toIntValue(): number {
return this.gear;
}
}
DTO - to obiekt, który jest anemiczny. Nie posiada metod. Po prostu transportuje potrzebne informacje na potrzebny pewnej operacji w naszym systemie lub mikroserwisie. Za pomocą DTO ustalamy co może dotrzeć do naszego API w warstwie UI.
@Riddle ma 100% racji. Cytuję:
Nie rób tak że jak zobaczysz jakąś praktykę to starasz się od razu gdzieś jej użyć w projekcie, to często przynosi słaby efekt. Lepiej - poznaj ją, zrozum, I poczekaj aż podczas programowania zauważysz ze warto jej użyć.
W takim wypadku zapytałbym. Czy rejestracja użytkownika jest powiazana z logiką biznesową klienta? Czy jednak implementacja rejestracji oraz logowania to robimy zawsze niezależnie od domeny?
Prawdopodobnie kumaty senior takie operacje jak rejestracja lub logowanie wyniesie do osobnego modułu lub mikroserwisu, ponieważ to nie jest domena klienta.
Tak na prawdę mówisz o dwóch różnych aspektach i dwóch różnych warstwach w rozumieniu DDD. Walidacja DTO, która odbywa się na poziomie warstwy UI oraz walidacji w konteście warstwy domenowej. To w zupełności dwie różne koncepcje. Tutaj odsyłam Cie do Implementing Domain-Driven Design by Vernon Vaughn oraz Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans. Tak znajdziesz opisy warstw oraz ich przeznaczenie oraz znajdziesz bardziej komplementarny opis czym jest Value Object.
Ciekawe źródła do przejrzenia
https://github.com/BottegaIT/ddd-leaven-v2
https://github.com/ddd-by-examples
https://bottega.com.pl/pdf/materialy/ddd/ddd1.pdf