2017-01-28 4 views
2

Ich entwickle einen Web-Service mit Scala und Play Framework 2.5. Meine Anwendung hat eine typische Schichtarchitektur. Ich habe die folgenden Klassen: model.User, repository.UserRepository, und controllers.UserController und ich versuche, eine gute Möglichkeit, Geschäftslogikfehler zu behandeln, ohne Exception zu verwenden.Buisness Logic Fehlerbehandlung in Scala

Betrachten Sie den folgenden Fall: eine Anfrage für die Registrierung eines neuen Benutzers erhalten wurde. Der Anfragetext enthält zwei Parameter: email und password. Diese Parameter werden an UserService#registerUser übergeben, die prüft, ob die E-Mail gültig ist und ob ein Benutzer mit einer solchen E-Mail bereits existiert. Meine Lösung ist UserService#registerUser zurückzukehren als Ergebnis ein Either[BuisnessFailure, Int] Objekt. BuisnessFailure ist ein Merkmal und wird vererbt von WrongEmailFormatFailure und UserAlreadyExistFailure. Wenn die E-Mail nicht gültig ist, gibt UserService#registerUserLeft(WrongEmailFormatFailure) zurück. Wenn ein Benutzer mit einer solchen E-Mail bereits existiert, gibt UserService#registerUserLeft(UserAlreadyExistFailure) zurück. Und im Erfolgsfall UserService#registerUser gibt Right(userRepository.create(User(email, password)) zurück. Danach kann ich in controllers.UserController diesen Fall mit Mustervergleich umgehen und eine entsprechende Antwort senden.

Also, ist dieser Ansatz gut genug, um ähnliche Fälle zu behandeln? Bitte finden meinen Code unten:

Benutzer:

package model 
case class User(email: String, password: String) 

UserRepository:

package repository 

import model.User 

class UserRepository { 
    def create(user: User): Int = ??? 
    def find(email: String): Option[User] = ??? 
} 

Userservice:

package service 

import model.User 
import repository.UserRepository 
import util.{BuisnessFailure, UserAlreadyExistFailure, WrongEmailFormatFailure} 

class UserService { 
    private val userRepository: UserRepository = ??? 
    def registerUser(email: String, password: String): Either[BuisnessFailure, Int] = { 
    if (userAlreadyExists(email)) 
     Left(UserAlreadyExistFailure) 
    else 
     if (!isEmailValid(email)) 
     Left(WrongEmailFormatFailure) 
     else 
     Right(userRepository.create(User(email, password))) 

    } 
    private def isEmailValid(email: String): Boolean = ??? 
    private def userAlreadyExists(email: String): Boolean = ??? 
} 

Usercontroller:

package controller 

import service.UserService 
import util.{UserAlreadyExistFailure, WrongEmailFormatFailure} 

class UserController extends play.api.Controller { 
    private val userService = new UserService 
    def signUp() = Action(parse.json) { implicit request => 
    //obtaining email and password parameters from request body 
    val email = ??? 
    val password = ??? 
    userService.registerUser(email, password) match { 
     case Left(WrongEmailFormatFailure) => // send 400 code and appropriate error message 
     case Left(UserAlreadyExistFailure) => // send 400 code and appropriate error message 
     case Right(_) => // send response with 200 code 
    } 
    } 
} 

BuisnessFailure:

package util 

sealed trait BuisnessFailure 
case object UserAlreadyExistFailure extends BuisnessFailure 
case object WrongEmailFormatFailure extends BuisnessFailure 
+0

Gute Frage, aber es wäre angemessener auf http://softwareengineering.stackexchange.com – Jubobs

+1

@Jubobs Danke für den Link. Ich habe noch nie von Softwareengineering.stackexchange.com gehört. – alex

+1

Ich würde Validierung einen anderen Service, so dass es mehr testbar und wiederverwendbar ist –

Antwort

4

Das ist genau das, was wir für den Umgang mit Fehlern bezüglich eines unserer größten Projekte gemacht haben, und wir hatten kein Problem. Either sollte genau so verwendet werden. Left für Fehler und Right für Ergebnisse.

  • Es ist typsicher
  • Keine Notwendigkeit, über die Parallelität zur Sorge
  • Der Code ist skalierbar und verwaltbar

Der einzige Punkt ist, dass, da die meisten von Scala-Anwendungen sind nicht-Block (async) Leute würden Future[Either[Error, Int]] eher als Either[Error, Int] verwenden. Aber immer noch, es ist in Ordnung, wie immer, wenn Sie sich für nicht blockierende entscheiden, können Sie einfach Either in eine Future wrap und wie ich schon sagte, keine Sorgen über Nebenläufigkeitsprobleme.