Search code examples
asp.net-mvcservice-layerthree-tier

What to return from service to ASP MVC web app?


Right now I have an ASP MVC web application along a Models project, a Service project, an Utilities project, and a few Datastores projects that function as repository for one or more domain models. I'm pretty happy with the separation of each layer but I'm stuck on what to return from service layer to the web app.

For example, when a user try to register, a RegisterViewModel is received by the controller. Individual pieces (email, password, etc.) are send to service layer which construct the Member domain object with guid, status, createdate, etc., send it to repository for storage and finally return the Member object for the web app to redirect to /Member/{guid}.

But how should the service layer inform the web app if an email already exists? In a more complex situation I may have to check the existence/validity of multiple domain objects and business rules so would have to return multiple errors in one go. In addition, I don't want exception to bubble to the web layer so service layer traps all exceptions but need to notify the web layer some how.

Even if I find a way to return all that, the web layer would be burdened to process all that and provide user various feedback. The controller code would be bulky and error prune. Is there a best practice on service result to the presentation? Should I eliminate separate service layer and have the code inside controller? Any thoughts are welcomed.


Solution

  • I wrote the operation model library for this purpose, which allows you to write code like this:

    public OperationResult Register(RegisterInput input) {
    
       var errors = new ErrorBuilder();
    
       if (errors.NotValid(input) // Invoke DataAnnotations validation
          || errors.Not(this.repo.FindUserByEmail(input.Email) == null, "Email '{0}' already exists.", () => input.Email))
          return errors;
    
       // Do stuff
    
       return HttpStatusCode.OK;
    }
    

    ...and in the controller the error messages are copied to ModelState:

    [HttpPost]
    public ActionResult Register(RegisterInput input) {
    
       var result = this.service.Register(input);
    
       if (result.IsError)
          return View().WithErrors(result);
    
       // Do stuff
    }
    

    Check out the source code of the MvcAccount project which is written using this pattern.