Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Deftly Done: Advanced Application Architecture ...

Deftly Done: Advanced Application Architecture w/Deft JS

Ext JS and Sencha Touch make it so easy to rapidly build and deploy applications; you won’t be able to stop at just one. What happens as these applications multiply in your organization? How can you structure them to maximize code reuse and reduce complexity and unnecessary duplication of code? Deft JS builds on Ext JS and Sencha Touch, adding even more power to your architectural toolbox. Come join us as we dive deep and show how Deft JS can help you reuse views and business logic across multiple applications; promote modularity and testability in large application code-bases; and coordinate asynchronous operations and tasks.

Presented by John Yanarella at SenchaCon 2013.

John Yanarella

July 18, 2013
Tweet

More Decks by John Yanarella

Other Decks in Technology

Transcript

  1. John Yanarella Software architect specializing in mobile and front-end web

    development. HTML / CSS / JavaScript, Node.js, CoffeeScript, iOS, Objective C, C/C++ Principal Software Architect at Universal Mind http://universalmind.com/ Freelance Software Consultant at CodeCatalyst http://www.codecatalyst.com/ https://github.com/CodeCatalyst/
  2. Development Team John Yanarella Universal Mind Isaac Johnston SMX Brian

    Kotek Booz Allen Hamilton Ryan Campbell Universal Mind David Tucker Universal Mind
  3. Distributions • Individual JS files for use with Ext.Loader() •

    Concatenated deft.js & deft- debug.js builds • Sencha Cmd Package
  4. Test Suite • Mocha + Chai + Sinon.JS • Karma

    Test Runner • Travis CI • Over 1,000 unit tests • 10 supported versions of Sencha Touch and Ext JS
  5. IoC Container • Class annotation-driven dependency injection • Maps dependencies

    via user- defined identifiers • Resolves dependencies by class instance, factory function or value.
  6. inject: (into config parameters) Ext.define('MyApp.service.ContactService',  {        extend:

     'MyApp.service.AbstractService',        inject:  [‘contactStore’],        config:  {                contactStore:  null        }        ... });
  7. Ext.define('MyApp.service.ContactService',  {        extend:  'MyApp.service.AbstractService',      

     inject:  {                store:  'contactStore'        },        ... }); inject: (into specific parameters)
  8. Deft.Injector.configure({        contactStore:  'MyApp.store.ContactStore',        contactService:

     'MyApp.service.ContactService' }); Deft.Injector.Configure() Class (Singleton)
  9. Deft.Injector.configure({        contactStore:  {        

             className:  'MyApp.store.ContactStore',                singleton:  false        } }); Deft.Injector.Configure() Class (Prototype)
  10. Deft.Injector.configure({        contactStore:  {        

             className:  'MyApp.store.ContactStore',                parameters:  [{                        proxy:  {                                type:  'ajax',                                url:  '/contacts.json',                                reader:  {                                type:  'json',                                root:  'contacts'                        }                }]        } }); Deft.Injector.Configure() Class with Constructor Parameters
  11. Deft.Injector.configure({        preferences:    {      

             className:  'MyApp.preferences.Preferences',                eager:  true        } }); Deft.Injector.Configure() Easy vs Lazy (Default)
  12. Deft.Injector.Configure() Factory Function Deft.Injector.configure({        useMocks:  false, });

    ... Deft.Injector.configure({        contactStore:  {                fn:  function(instance)  {                        if  (this.resolve(‘useMocks’))  {                                return  Ext.create('MyApp.mock.store.ContactStore');                        }  else  {                                return  Ext.create('MyApp.store.ContactStore');                        }                },                singleton:  true        } });
  13. Deft.Injector.Configure() Value //  Initialize  a  third-­‐party  JavaScript  library. var  player

     =  new  AudioPlayer(apiKey); ... Deft.Injector.configure({        brandedApplicationName:  {                value:  "Contact  Manager"        },        versionNumber:  {                value:  1.0        },        playerService:  {                value:  player  //  Populate  the  injector  with  a  reference        },        ... });
  14. Deft.Injector.resolve() and reset() //  Resolve  a  dependency  based  on  the

     current  configuration. var  contactService  =  Deft.Injector.resolve(‘contactService’); //  Clears  the  current  IoC  container  configuration. Deft.Injector.reset(); //  Throws  “Error  while  resolving  value  to  inject:  no  dependency  provider   found  for  'contactService'.” var  contactService  =  Deft.Injector.resolve(‘contactService’);
  15. MVC Model describes and manages application domain data behaviors and

    state, and responds to requests to retrieve or persist changes to that state. View presents model data to the user, accepts user input, and announces high-level user gestures such as clicks or selection changes. Controller mediates between the model and view, listening for user gestures to initiate actions on the Model, and instructing the View to reflect Model changes.
  16. MVC with Deft JS Model is expressed using ‘faceless’ business

    logic components, such as Ext.data.Store and Ext.data.Model, or custom Service classes View is realized using any of the rich suite of Ext JS or Sencha Touch containers and components Controller is implemented by creating view-specific controllers that extend the Deft.mvc.ViewController class
  17. View Controllers • Class annotation-driven association between a view and

    its View Controller • Clarifies the role of the controller - i.e. controlling a view and delegating work to injected services • Allows multiple independent instances of a view, each with its own View Controller instance
  18. View Controllers • Reduces memory usage by automatically creating and

    destroying view controllers in tandem with their associated views • Supports concise configuration for referencing view components and registering event listeners
  19. View Controllers • Integrates with the view destruction lifecycle to

    allow the view controller to potentially cancel removal and destruction • Simplifies clean-up by automatically removing view and view component references and event listeners
  20. controller: Ext.define('MyApp.view.ContactsView',  {        extend:  'Ext.panel.Panel',    

       controller:  'MyApp.controller.ContactsViewController',        ... });
  21. <View>.getController() Ext.define('MyApp.view.ContactsView',  {        extend:  'Ext.panel.Panel',    

       controller:  'MyApp.controller.ContactsViewController',        initComponent:  function  ()  {                this.items  =  [                        ...                        {                                xtype:  ‘gridpanel’,                                store:  this.getController().getContactStore(),                                ...                        },                        ...                ];                this.callParent(  arguments  );        },        ... });
  22. View Controller Ext.define('MyApp.controller.ContactsViewController',  {        extend:  'Deft.mvc.ViewController',  

             ...        control:  {                //  using  view-­‐relative  component  selectors!                submitButton:  'panel  >  button[text="Submit"]',                cancelButton:  'panel  >  button[text="Cancel"]',                ...        }        ... });
  23. Automatic Accessor Generation Ext.define('MyApp.controller.ContactsViewController',  {        extend:  'Deft.mvc.ViewController',

               ...        control:  {                submitButton:  'panel  >  button[text="Submit"]',                cancelButton:  'panel  >  button[text="Cancel"]',                ...        }        ...        init:  function()  {                //  getSubmitButton()  will  be  automatically  created.                this.getSubmitButton().disable();                return  this.callParent(  arguments  );        }        ... });
  24. itemId (by convention) Ext.define('MyApp.controller.ContactsViewController',  {        extend:  'Deft.mvc.ViewController',

               ...        control:  {                //  selector  may  be  omitted  if  identifier  matches  itemId                submitButton:  {},                  ...        } });
  25. Event Listeners Ext.define('MyApp.controller.ContactsViewController',  {        extend:  'Deft.mvc.ViewController',  

             ...        control:  {                submitButton:  {                        click:  'onSubmitButtonClick'                }        }        ...        onSubmitButtonClick:  function()  {                //  executed  in  the  view  controller's  scope                ...        }        ... });
  26. If you like complexity... Ext.define('MyApp.controller.ContactsViewController',  {        extend:

     'Deft.mvc.ViewController',            ...        control:  {                submitButton:  {                        //  can  still  specify  a  selector  with  this  syntax                        selector:  'panel  >  button[text="Submit"]'                        listeners:  {                                click:  'onSubmitButtonClick'                        }                }        }        ... });
  27. View reference and listeners Ext.define('MyApp.controller.ContactsViewController',  {        extend:

     'Deft.mvc.ViewController',            ...        control:  {                //  ‘view’  is  magic  -­‐  always  refers  to  the  controlled  view                view:  {                        show:  'onViewShow'                        hide:  'onViewHide'                }        }        ...        init:  function()  {                this.getView().show()                return  this.callParent(  arguments  );        } });
  28. init() Ext.define('MyApp.controller.ContactsViewController',  {        extend:  'Deft.mvc.ViewController',    

           ...        control:  {                ...        }        ...        init:  function()  {                //  all  accessors  will  have  been  created                //  all  event  listeners  will  have  been  added                return  this.callParent(  arguments  );        } });
  29. Cancelable destroy() Ext.define('MyApp.controller.ContactsViewController',  {        extend:  'Deft.mvc.ViewController',  

             ...        control:  {                ...        }        ...        destroy:  function()  {                if  (this.hasUnsavedChanges)  {                        //  cancel  destruction                        return  false                }                //  burn  baby  burn                return  this.callParent(  arguments  );        } });
  30. Observe Injected Services Ext.define('MyApp.controller.ContactsViewController',  {        extend:  'Deft.mvc.ViewController',

             inject:  ['contactStore']        ...        observe:  {              contactStore:  {                      refresh:  ‘onContactStoreRefresh’              }        },        ...        onContactStoreRefresh:  function  (store,  eOpts)  {                var  count  =  this.getContactStore().getTotalCount();                this.resultCountLabel().setText(count  +  ‘  records.’);        } });
  31. controllerConfig: var  selectedAccountId  =  12345; Ext.create(  'MyApp.controller.AccountView',  {    

       ...                //  passed  as  the  configuration  object  when  the  controller  is  created        controllerConfig:  {                accountId:  selectedAccountId        } });
  32. Promises • Provides an elegant way to represent a “future

    value” resulting from an asynchronous operation • Offers a consistent, readable API for registering success, failure, or progress callbacks • Allows chained transformation and processing of future values • Simplifies processing of a set of future values via utility functions including all(), any(), some(), map() and reduce()
  33. Create a Promise using a Deferred Ext.define(‘MyApp.service.AccountService’,  {    

       ...                load:  function  (accountId)  {                var  deferred  =  Ext.create(‘Deft.Deferred’);                                MyApp.model.AccountModel.load(accountId,  {                        success:  function  (record,  operation)  {                                deferred.resolve(record);                        },                        failure:  function  (record,  operation)  {                                deferred.reject(operation.getError());                        },                        scope:  this                });                                return  deferred.promise;        } });
  34. Registering callbacks via then() accountService        .load(accountId)  

         .then(                function  (account)  {                        //  do  something  with  the  account                },                function  (error)  {                        //  display  or  handle  error                },                function  (update)  {                        //  display  progress                },                this        );
  35. Registering callbacks via then() (with named parameters) accountService    

       .load(accountId)        .then({                success:  function  (account)  {                        //  do  something  with  the  account                },                failure:  function  (error)  {                        //  display  or  handle  error                },                progress:  function  (update)  {                        //  display  progress                },                scope:  this        });
  36. always() accountService        .load(accountId)        .then(

                   ...        )        .always(function  ()  {                //  clean-­‐up  logic  to  execute  regardless  of  outcome        });
  37. cancel() loadAccountPromise  =  accountService.load(accountId); loadAccountPromise.then({        failure:  function

     (error)  {                //  failure  handler  will  be  called  with  a  CancellationError        } }); loadAccountPromise.cancel();
  38. Propagation, Recovery and Logging var  accountPromise  =  accountService    

       .load(  accountId  )        .then({                success:  function  (result)  {                        if  (result.error)  {                                //  detect  and  throw  an  error                                throw  new  Error(result.error.message);                        }                        return  process(result);    //  transform  the  result                }        })        .then({                failure:  function  (error)  {                        return  defaultValue;          //  recover  from  error                }        })        .log('Load  Account:')        .done();
  39. all(), any(), some(), map(), reduce() //  Aggregation Deft.Promise.all([    

       technologic.buyIt(),        technologic.useIt(),        technologic.breakIt(),        technologic.fixIt(),        technologic.trashIt(),        technologic.changeIt(),        ... ]); //  Competitive  Race Deft.Promise.any(  promisesOrValues  ); Deft.Promise.some(  promisesOrValues  ); //  Transformations Deft.Promise.map(  promisesOrValues  ); Deft.Promise.reduce(  promisesOrValues  );
  40. Loosely Coupled MVC • Define business logic as Services •

    Use Promises to coordinate asynchronous operations • Configure the IoC Injector with Services • Inject Services into ViewControllers • Dynamically construct user interface using Views