2 Replies Latest reply on May 17, 2015 5:38 AM by BKBK

    Implementing a service layer to call gateways and DAOs

    philerimos Level 1

      I am using CF11. In addition to the Datasource component, I created a package of four components ( 1 x Bean, 1 x DAO, 1 x Gateway, 1 x Service) that deals with grapes...

      The datasource object and the service object have been persisted in Application.cfc as follows:

       

      <cffunction name="onApplicationStart" output="false">

       

        <!--- Instantiate the Datasource object --->

       

        <cfset var objDatasource = createObject ('component','components.beans.Datasource').init(

        DSName='dns', username='abc',

        password='pass') />


        <!--- Instantiate and persist the Grape service in the application scope  --->

       

        <cfset Application.GrapeSVC = createObject('component','components.dataAccess.GrapeSVC').init(

        datasource = objDatasource) />

       

      </cffunction>

       

      The service object (GrapeSVC.cfc) acts as a façade pattern to the other inner components: a Bean (Grape.cfc), a DAO (GrapeDAO.cfc) and a Gateway (GrapesGW.cfc).

      Now, I have below a CFM template that instantiates the above service object (GrapeSVC) to simply call a method - getAllGrapes() from the gateway - that lists all the grapes in the database:

       

      ListGrapes.cfm

       

      <!--- Instantiate the Datasource object --->

       

        <cfset objDatasource = createObject ('component','components.beans.Datasource').init(

        DSName='dns', username='abc',

        password='pass') />

       

        <!--- Instantiate the Grape service object and pass in the datasource object --->

       

        <cfset objGrapeSVC = createObject('component','components.dataAccess.GrapeSVC').init(

        datasource = objDatasource) />

       

        <!--- Pull out all grapes from the database --->

       

        <cfset qResults = objGrapeSVC.getAllGrapes() /> 

       

          <!--- Dump the results --->

       

          <cfdump var="#qResults#" label="getAllGrapes results" />


      The code above works fine as long as I keep instantiating the datasource object within the above CFM template. If I take it out, the code breaks. This does not make sense to me since I have already persisted the datasource object in Application.cfc above and mostly because the objective is to avoid repeats. My question therefore is:

      What is wrong in the above approach and is there a way to NOT re-instantiate the datasource object in every CFM template?

        • 1. Re: Implementing a service layer to call gateways and DAOs
          philerimos Level 1

          After a more careful reading of Matt Gifford's OO Programming in ColdFusion, I could re-factor the code as follows:

           

          Application.cfc:

           

          <cffunction name="onApplicationStart" output="false">

           

            <!--- Instantiate the Datasource object --->

           

            <cfset var objDatasource = createObject ('component','components.beans.Datasource') />

           

            <!--- Instantiate and persist the Grape service in the application scope  --->

           

            <cfset Application.GrapeSVC = createObject('component','components.dataAccess.GrapeSVC').init(

            datasource = objDatasource) />

           

            </cffunction>

           

          dropping the init() that contained the datasource login credentials. Those have been set once as default properties in the Datasource bean instead, and nowhere repeated.

          Same thing in the Listgrapes.cfm template:

           

          <!--- Instantiate the Datasource object --->

           

            <cfset objDatasource = createObject('component','components.beans.Datasource') />

           

            <!--- Instantiate the Grape service object and pass in the datasource object --->

           

            <cfset objGrapeSVC = createObject('component','components.dataAccess.GrapeSVC').init(

            datasource = objDatasource) />

           

            <!--- Pull out all grapes from the database --->

           

            <cfset qResults = objGrapeSVC.getAllGrapes() /> 

           

              <!--- Dump the results --->

           

              <cfdump var="#qResults#" label="getAllGrapes results" />


          Still not sure whether this code is optimised, but it works. Not sure whether setting the default Datasource bean properties with its access credentials is secure as well.

          However I encountered an interesting problem accessing a private method in the underlying gateway component. The method getAllGrapes() is public both in the service component and the gateway component. Within the gateway component this public method getAllGrapes() calls a private method filterAllGrapes() that implements a search filter. I had to set the access to the method filterAllGrapes() in the gateway object from private to package in order to make the call from the service layer possible. Leaving the access method to private triggers an error. I had no such problem when calling the same method from the gateway directly (i.e not implementing the service layer). I am not really sure what caused this issue.

          • 2. Re: Implementing a service layer to call gateways and DAOs
            BKBK Adobe Community Professional & MVP

            That is so by design. In ColdFusion, private access means that the function will only be accessible to the component within which it is defined, or to any components that extend it.