W tym przypadku rozwiązanie to byłoby dokładnie UserResponse (można jak klasę bazową/interfejs z dwoma implementacjami OKResponse i ErrorResponse).
Tylko jak będzie miał więcej takich metod (jak addUser) to sie trochę napiszesz... Więc pewnie zrobisz to generycznie (OKResponse<T> { private T response; }... I w końcu wyjdzie, że zrobiłeś właśnie swojego EIthera, tylko gorszego.
Co do wyjątków - są dla ludzi, ale generalnie warto jednak używać tylko na sytuacje ostateczne - u Ciebie akurat własnie wyjątek, to żaden wyjątek (żadna panika) i raczej warto Eitherem pojechać. Dlaczego: Wyjatki się gorzej komponują, rzuconego wyjątku nie podstawisz pod zmienną (w intuicyjny sposób -wyjątek złapany w zmiennną przestaje być dalej propagowany ).
Gdyby to był Spring kontroller - i wyniku/wyjątku już bym w swoim kodzie nie łapał (bo byłby tylko przez spring obsługiwany i pchany na HTTP) to wyjatek nie byłby taki zły.
Co do VAVR nawet jeśli by tam Eithera (i Optiona) nie było to polecam ze względu na niemutowalne kolekcje. Jeżeli robisz typowy soft biznesowy to kolekcje z VAVR nie tylko są przyjemniejsze w użyciu (niż ta zabawa w stream(), collect()), są przede wszystkim bezpieczniejsze (mniej błędogęnne). Do tego w realistycznych przypadkach często wydajniesjsze(!!!), bo nie musisz stosować defensywnego kopiowania. To ostatnie dyskusyjne - mało kto stosuje defensywne kopiowanie (ale mi się czesto kiedyś zdarzało. wiec po przejsciu na VAVR moje procki odetchnęly). jakkolwiek w typowym biznesowym sofcie i tak wydajnoć kolekcji praktycznie nie ma znaczenia (jak często obrabiasz kolekcje większe niż 500 elementów?).
Jak zobaczysz jak Ci spada ilośc głupich błędów w logice to nie będziesz na java.util. chciał patrzeć (szczególnie w projektach zespołowych).