Page 1 of 2 12 LastLast
Results 1 to 10 of 11

Thread: Transactions and error handling in a Web app

  1. #1
    Join Date
    Jun 2006
    Posts
    22

    Default Transactions and error handling in a Web app

    Hi,
    I have some head ache about some design decisions and would like to hear your opinion.


    Present situation:
    I have a web app with the following layers.

    Presentation (Spring MVC controllers)
    ---------
    Facade (wrapping service calls into "use case" method calls. )
    ---------
    Service ("Global" try/catch(Throwable), translating any Exceptions into ResultObjects)
    ---------
    Integration
    ---------

    * Communitation between Service->Facade->Presentation are made via ResultObjects.

    Wants:
    * Spring Declarative Transaction support are supposed to start in the Facade layer.
    * The Presentation layer should not be aware of any Exceptions thrown in any layers under it, it should always recieve a ResultObject regardless if it contains an ok message, including the result, or only an error message

    Problems:
    * If I catch all exceptions in the Service layer, what about our declarative transactions, don't they rely on an exception is thrown from the wrapped "transacted" method ?

    Thoughts:
    * What about an CreateResultObjectFromExcaption Aspect (around or after throwing advice?) which triggers on an exception thrown from the Facade layer (rethrow the exception caught in Service rather than creating a ResultObject), creating a ResultObject based on the exception? Which aspect will trigger first, the Declarative Transaction aspect or the around/after throwing advice, i.e will the transaction do the rollback before my CreateResultObjectFromExcaption or after?
    * Should I have the transactions around the service layer methods and create my ResultObject in the Facade (throw exception from the Service layer). What about rollback if service call A is fine but service call B fails?
    * Do I have to let all exceptions propagate all the way to the Presentation layer which make this layer responsible for business issues like i18n, "exception translation" and such?

    Need more info about my situation?

    regards
    -Martin Börlin

  2. #2
    Join Date
    Jun 2006
    Posts
    22

    Default

    Ok, no one has any opinions on the matter... I'll discuss with my self then. :-)

    Another thought I have, and the one I will try to implement:

    I'll cast any exceptions thrown in the Integration layer to my own unchecked exceptions, including a message code for i18n. I do not catch them in my Facade layer, so the Transaction can do its rollback (if needed). I'll create an aspect, wrapping any call to the facade from the presentation layer.
    Code:
    public aspect TranslateFromExceptionToResultObject {
    
      pointcut facadeCall() :
        call(ServiceResult+ com.my.package.facade.*.*(..))
        && within(com.my.package.client..*);
      
      Object around() : facadeCall() {
        Object res = null;
        try{
          res = proceed();
        } catch(Throwable e) {
          if(e instanceof MyRunTimeException) {
              res = new ServiceResult(ServiceResultStatus.ERROR, e.getMessageCode());
          } else {
              res = new ServiceResult(ServiceResultStatus.ERROR, e.getMessage());
          }
        }
        return res;
      }
    }
    This ensures my presentationlayer will always get an object it can handle, I have a clean facade and service layer, without any TCF's and I can use Springs Declarative Transaction support without hassle.

  3. #3
    Join Date
    Oct 2004
    Location
    Herndon, VA, US
    Posts
    648

    Default

    I'm sure this approach suits your needs, although it might not be entirely a bad idea to allow the presentation layer catching application exceptions. If something goes wrong in the service layer, it would actually be easier for the presentation layer to decide how to handle it based on an exception (or some error code at least), instead of an error message.
    --Jing Xue

  4. #4
    Join Date
    Jun 2006
    Posts
    22

    Default

    In the general case I agree, but the way we implement this webapp I believe the non-catching in Presentation is a good approach. We do all validation in the service layer (except for some stuff like newPassword==confirmPassword etc. which is done in the presentation layer), i.e beans validation and business rules validation (using spring-modules bean validation framework). Any Errors object produced by the validation are transformed into a ServiceResult, like any correct results from the service layer are transformed into a ServiceResult. Hence the presentation layer is always "prepared" for a faulty
    result, and can act upon it.

  5. #5
    Join Date
    Sep 2006
    Location
    UK
    Posts
    8,425

    Default

    These ResultObjects do they contain a DTO version of the DomainObjects? Although I'm not too sure about the architecture, it might be a nice idea to keep the services away from these objects. The facades could assemble the ResultObjects, that way your services only know about domain objects. If you change you mind about the whole ResultObject thing you only have to change one layer. As far as exception handling goes you should just be able to handle all this in your aspect.

  6. #6
    Join Date
    Jun 2006
    Posts
    22

    Default

    The result objects (I refactored them from ServiceResult to FacadeResult since they are assembled in the Facade) encapsulates actual domain objects. The architecture is based on the "POJO facade" and "Exposed domain model" patterns described in "POJO's in Action" by Chris Richardson.
    The services doesn't know about the facade result objects, but create their own result objects.

    ---------
    Presentation (translate the MessageCode, if ResultStatus == ERROR in the FacadeResult just show the message. If ResultStatus == OK, continue with next step in flow, e.g populate a web page with data from the domain object User)
    ---------
    [TranslateFromExceptionToResultObject aspect] (create a FacadeResult from any MyRunTimeException based on MessageCode, e.g "error.user.not.existing")
    ---------
    Facade (return encasulated domain objects assembled from the service calls (FacadeResult), no catch of exceptions )
    ---------
    Service (return encasulated domain objects, "Global" try/catch(Throwable), translating any Exceptions into (subclasses of) MyRunTimeException)
    ---------
    Integration (return "clean" domain objects or throw any exception)
    ---------

  7. #7
    Join Date
    Sep 2006
    Location
    UK
    Posts
    8,425

    Default

    I'm sure it will work fine, have to confess I've not read POJO's in Action so I'm in the dark here. I'm still not exactly sure though why you need the service layer. I would have thought that the facade could just do it instead. If you have different facade implementations then I can maybe see why your doing it that way.

  8. #8
    Join Date
    Jun 2006
    Posts
    22

    Default

    Quote Originally Posted by karldmoore View Post
    I'm sure it will work fine, have to confess I've not read POJO's in Action so I'm in the dark here. I'm still not exactly sure though why you need the service layer. I would have thought that the facade could just do it instead. If you have different facade implementations then I can maybe see why your doing it that way.
    Spot on! :-) The webapp will be launched in different locations with somewhat different needs hence we need to inject location specific facades into our controllers

  9. #9
    Join Date
    Sep 2006
    Location
    UK
    Posts
    8,425

    Default

    Quote Originally Posted by axxa View Post
    Spot on! :-) The webapp will be launched in different locations with somewhat different needs hence we need to inject location specific facades into our controllers
    OK, I'm getting there. Couldn't you also use an aspect around the service calls to do the "global try/catch" work.

  10. #10
    Join Date
    Jun 2006
    Posts
    22

    Default

    The thought has crossed my mind, but I'm not too sure it will be the easiest way since each (most) call from a service to integration, throwing an exception, will need their own messageCode, e.g findUserById() -> "error.user.not.existing", findOrderById() -> "error.order.not.existing" etc.
    Or do you have an idea how to solve this, without having to create an aspect for each service call? ...Just about learning to use AOP so every new "aspect" on the matter is welcome!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •