Question

In order to handle exceptions in Scala, I prefer avoiding basic try/catch and benefit from functional thinking with Validation from Scalaz (similar to Either type in certain cases).

My application exposes some services. Imagine this method (making no real sense, but good for the concept) in my service layer. It associates a Buyer (purchaser) with his new Car and returns the Car containing this association if all rules are passed with success:

def create(carDTO: CarDTO, buyerDTO: BuyerDTO): Validation[Either[TechnicalFailure, List[CarCreationFailure]], CarDTO]

Explanation: Creating a Car may lead to one of both exceptions types:

  • Technical failures (when databases crashes for instance) wrapping Throwable exceptions.
  • Business failures (custom application's rules preventing from inconsistence, for instance, a Car with an illegal status). CarCreationFailure is one and of course could be extended by more precised failure.

My question is especially focus on client side and deals specifically with more than one potential Business Failure.

Should the return type Validation[Either[TechnicalFailure, List[CarCreationFailure]], CarDTO] be replaced by the less cumbersome: ValidationNel[Throwable, CarDTO]

Note here ValidationNel (accumulating errors/exceptions into a NonEmptyList).

A drawback would be that a new reader couldn't, at first glance, guess that this method returns either TechnicalFailure or CarCreationFailure (subclass of BusinessFailure so); just a too frightening Throwable.

He'd be forced to apply a pattern matching on every Throwable types contained within my application to in order to be sure to not forget anyone... => messy.

What is the cleanest way among those solutions, or maybe...other one?

Was it helpful?

Solution

In cases like these, I personally use a custom class called Tri which works like both Try and Either:

sealed trait Tri [+Value, +Problem] { ... }
final case class Good[+Value](v: Value) extends Tri[Value, Nothing] { ... }
final case class Bad[+Problem](p: Problem) extends Tri[Nothing, Problem] { ... }
final case class Ugly(th: Throwable) extends Tri[Nothing, Nothing] { ... }

with appropriate methods to handle the most common operations one wants. In this case, I'd simply return to the client

Tri[CarDTO, List[CarCreationFailure]]

and have the exceptions wrapped into a custom exception class as needed. This both is up-front about what may or may not happen when the code itself is working normally, and leaves unstated--as usual--exceptional conditions that need to be handled sooner or later, including things like database errors. Then you just e.g.:

create(car, buyer) match {
  case Good(dto) => // Yay
  case Bad(xs) =>   // Handle list of car creation failures
  case Ugly(th) =>  // Handle exceptions
}

I am not aware of any similar functionality in Scalaz7, though you can build the pieces (as you have, though why not use \/?) more easily than in Scalazless Scala, so you might be less motivated to create this kind of tripartite error handling class.

OTHER TIPS

We use something similar (simpler and less elegant) to what Rex Kerr uses.

All business exceptions/errors are wrapped in a custom BusinessException, all other errors are throwing exceptions of various kind.

We use the basic Try and a servlet will have code looking like (much simplified)

    ( req getParameter "action" match {

        case null          ⇒ Failure( new BusinessException( "Missing required parameter 'action'" ) )
        case "doThis"  ⇒ doThis()
        case "doThat"  ⇒ doThat( req )
        case x         ⇒ Failure( new BusinessException( "Uknown Action '" + x + "'" ) )
    } ) match {

        case Success( someResponse ) ⇒ //send 200 with some response
        case Failure(t) if t.isInstanceOf[BusinessException] => //send 400 with exception message 
        case Failure( t )        ⇒ //send 500 with exception message
    }

Methods like doThishave signatures of the type

def doThis(): Try[ Option[ String ] ] = Try {
    //do stuff
}

The advantage of this simple mechanism is that it is easy to wrap existing Java code throwing exceptions, which is our use case.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top