Announcement Announcement Module
Collapse
No announcement yet.
Transactions and error handling in a Web app Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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
    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.

    Comment


    • #3
      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.

      Comment


      • #4
        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.

        Comment


        • #5
          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.

          Comment


          • #6
            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)
            ---------

            Comment


            • #7
              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.

              Comment


              • #8
                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

                Comment


                • #9
                  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.

                  Comment


                  • #10
                    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!

                    Comment


                    • #11
                      Originally posted by axxa View Post
                      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!
                      OK, see what your saying. One way of doing it would be to decide on a general set of exceptions and the related messages. You could then create an abstract bean and a bean that uses this as the parent for each service. You then inject in the messages for each type of exception. So in the user service aspect objectNotFound=error.user.not.existing, in role service aspect objectNotFound=error.role.not.existing. If there are exceptions specific to the service you can then subclass the base aspect.

                      It just means that for the services that throw general exceptions theres not much to do. For the ones that do something specific theres a lot less to do.

                      One thing you might have to think about is transactions. If your exceptions aren't runtime your going to have to configure what triggers a rollback etc....

                      Comment

                      Working...
                      X