Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

Structure of Applications

Save for later
  • 21 min read
  • 21 Apr 2015

article-image

In this article by Colin Ramsay, author of the book Ext JS Application Development Blueprints, we will learn that one of the great things about imposing structure is that it automatically gives predictability (a kind of filing system in which we immediately know where a particular piece of code should live).

The same applies to the files that make up your application. Certainly, we could put all of our files in the root of the website, mixing CSS, JavaScript, configuration and HTML files in a long alphabetical list, but we'd be losing out on a number of opportunities to keep our application organized. In this article, we'll look at:

  • Ideas to structure your code
  • The layout of a typical Ext JS application
  • Use of singletons, mixins, and inheritance
  • Why global state is a bad thing

Structuring your application is like keeping your house in order. You'll know where to find your car keys, and you'll be prepared for unexpected guests.

(For more resources related to this topic, see here.)

Ideas for structure

One of the ways in which code is structured in large applications involves namespacing (the practice of dividing code up by naming identifiers). One namespace could contain everything relating to Ajax, whereas another could contain classes related to mathematics. Programming languages (such as C# and Java) even incorporate namespaces as a first-class language construct to help with code organization. Separating code from directories based on namespace becomes a sensible extension of this:

structure-applications-img-0

From left: Java's Platform API, Ext JS 5, and .NET Framework

A namespace identifier is made up of one or more name tokens, such as "Java" or "Ext", "Ajax" or "Math", separated by a symbol, in most cases a full stop/period. The top level name will be an overarching identifier for the whole package (such as "Ext") and will become less specific as names are added and you drill down into the code base.

The Ext JS source code makes heavy use of this practice to partition UI components, utility classes, and all the other parts of the framework, so let's look at a real example. The GridPanel component is perhaps one of the most complicated in the framework; a large collection of classes contribute to features (such as columns, cell editing, selection, and grouping). These work together to create a highly powerful UI widget. Take a look at the following files that make up GridPanel:

structure-applications-img-1

The Ext JS grid component's directory structure

The grid directory reflects the Ext.grid namespace. Likewise, the subdirectories are child namespaces with the deepest namespace being Ext.grid.filters.filter.

The main Panel and View classes: Ext.grid.Grid and Ext.grid.View respectively are there in the main director. Then, additional pieces of functionality, for example, the Column class and the various column subclasses are further grouped together in their own subdirectories. We can also see a plugins directory, which contains a number of grid-specific plugins.

Ext JS actually already has an Ext.plugins namespace. It contains classes to support the plugin infrastructure as well as plugins that are generic enough to apply across the entire framework. In the event of uncertainty regarding the best place in the code base for a plugin, we might mistakenly have put it in Ext.plugins. Instead, Ext JS follows best practice and creates a new, more specific namespace underneath Ext.grid.

Going back to the root of the Ext JS framework, we can see that there are only a few files at the top level. In general, these will be classes that are either responsible for orchestrating other parts of the framework (such as EventManager or StoreManager) or classes that are widely reused across the framework (such as Action or Component). Any more specific functionality should be namespaced in a suitably specific way.

As a rule of thumb, you can take your inspiration from the organization of the Ext JS framework, though as a framework rather than a full-blown application. It's lacking some of the structural aspects we'll talk about shortly.

Getting to know your application

When generating an Ext JS application using Sencha Cmd, we end up with a code base that adheres to the concept of namespacing in class names and in the directory structure, as shown here:

structure-applications-img-2

The structure created with Sencha Cmd's "generate app" feature

We should be familiar with all of this, as it was already covered when we discussed MVVM in Ext JS. Having said that, there are some parts of this that are worth examining further to see whether they're being used to the full.

/overrides

This is a handy one to help us fall into a positive and predictable pattern. There are some cases where you need to override Ext JS functionality on a global level. Maybe, you want to change the implementation of a low-level class (such as Ext.data.proxy.Proxy) to provide custom batching behavior for your application. Sometimes, you might even find a bug in Ext JS itself and use an override to hotfix until the next point release. The overrides directory provides a logical place to put these changes (just mirror the directory structure and namespacing of the code you're overriding). This also provides us with a helpful rule, that is, subclasses go in /app and overrides go in /overrides.

/.sencha

This contains configuration information and build files used by Sencha Cmd. In general, I'd say try and avoid fiddling around in here too much until you know Sencha Cmd inside out because there's a chance you'll end up with nasty conflicts if you try and upgrade to a newer version of Sencha Cmd.

bootstrap.js, bootstrap.json, and bootstrap.css

The Ext JS class system has powerful dependency management through the requires feature, which gives us the means to create a build that contains only the code that's in use. The bootstrap files contain information about the minimum CSS and JavaScript needed to run your application as provided by the dependency system.

/packages

In a similar way to something like Ruby has RubyGems and Node.js has npm, Sencha Cmd has the concept of packages (a bundle which can be pulled into your application from a local or remote source).

This allows you to reuse and publish bundles of functionality (including CSS, images, and other resources) to reduce copy and paste of code and share your work with the Sencha community. This directory is empty until you configure packages to be used in your app.

/resources and SASS

SASS is a technology that aids in the creation of complex CSS by promoting reuse and bringing powerful features (such as mixins and functions) to your style sheets. Ext JS uses SASS for its theme files and encourages you to use it as well.

index.html

We know that index.html is the root HTML page of our application. It can be customized as you see fit (although, it's rare you'll need to). There's one caveat and it's written in a comment in the file already:

<!-- The line below must be kept intact for Sencha Cmd to build 
your application -->
<script id="microloader" type="text/javascript"
src="bootstrap.js"></script>

We know what bootstrap.js refers to (loading up our application and starting to fulfill its dependencies according to the current build). So, heed the comment and leave this script tag, well, alone!

/build and build.xml

The /build directory contains build artifacts (the files created when the build process is run). If you run a production build, then you'll get a directory inside /build called production and you should use only these files when deploying. The build.xml file allows you to avoid tweaking some of the files in /.sencha when you want to add some extra functionality to a build process. If you want to do something before, during, or after the build, this is the place to do it.

app.js

This is the main JavaScript entry point to your application. The comments in this file advise avoiding editing it in order to allow Sencha Cmd to upgrade it in the future. The Application.js file at /app/Application.js can be edited without fear of conflicts and will enable you to do the majority of things you might need to do.

app.json

This contains configuration options related to Sencha Cmd and to boot your application.

When we refer to the subject of this article as a JavaScript application, we need to remember that it's just a website composed of HTML, CSS, and JavaScript as well. However, when dealing with a large application that needs to target different environments, it's incredibly useful to augment this simplicity with tools that assist in the development process. At first, it may seem that the default application template contains a lot of cruft, but they are the key to supporting the tools that will help you craft a solid product.

Cultivating your code

As you build your application, there will come a point at which you create a new class and yet it doesn't logically fit into the directory structure Sencha Cmd created for you. Let's look at a few examples.

I'm a lumberjack – let's go log in

Many applications have a centralized SessionManager to take care of the currently logged in user, perform authentication operations, and set up persistent storage for session credentials. There's only one SessionManager in an application. A truncated version might look like this:

/**
* @class CultivateCode.SessionManager
* @extends extendsClass
* Description
*/
Ext.define('CultivateCode.SessionManager', {
   singleton: true,
   isLoggedIn: false,
 
   login: function(username, password) {
       // login impl
   },
 
 
   logout: function() {
       // logout impl
   },
 
 
   isLoggedIn() {
       return isLoggedIn;
   }
});

We create a singleton class. This class doesn't have to be instantiated using the new keyword. As per its class name, CultivateCode.SessionManager, it's a top-level class and so it goes in the top-level directory. In a more complicated application, there could be a dedicated Session class too and some other ancillary code, so maybe, we'd create the following structure:

structure-applications-img-3

The directory structure for our session namespace

What about user interface elements? There's an informal practice in the Ext JS community that helps here. We want to create an extension that shows the coordinates of the currently selected cell (similar to cell references in Excel). In this case, we'd create an ux directory—user experience or user extensions—and then go with the naming conventions of the Ext JS framework:

Ext.define('CultivateCode.ux.grid.plugins.CoordViewer', {
   extend: 'Ext.plugin.Abstract',
   alias: 'plugin.coordviewer',
 
   mixins: {
       observable: 'Ext.util.Observable'
   },
 
   init: function(grid) {
       this.mon(grid.view, 'cellclick', this.onCellClick, this);
   },
 
   onCellClick: function(view, cell, colIdx, record, row, rowIdx, 
e) {        var coords = Ext.String.format('Cell is at {0}, {1}',
colIdx, rowIdx)          Ext.Msg.alert('Coordinates', coords);    } });

It looks a little like this, triggering when you click on a grid cell:

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime

structure-applications-img-4

Also, the corresponding directory structure follows directly from the namespace:

structure-applications-img-5

You can probably see a pattern emerging already.

We've mentioned before that organizing an application is often about setting things up to fall into a position of success. A positive pattern like this is a good sign that you're doing things right.

We've got a predictable system that should enable us to create new classes without having to think too hard about where they're going to sit in our application. Let's take a look at one more example of a mathematics helper class (one that is a little less obvious).

Again, we can look at the Ext JS framework itself for inspiration. There's an Ext.util namespace containing over 20 general classes that just don't fit anywhere else. So, in this case, let's create CultivateCode.util.Mathematics that contains our specialized methods for numerical work:

Ext.define('CultivateCode.util.Mathematics', {
   singleton: true,
 
   square: function(num) {
       return Math.pow(num, 2);
   },
 
   circumference: function(radius) {
       return 2 * Math.PI * radius;
   }
});

There is one caveat here and it's an important one. There's a real danger that rather than thinking about the namespace for your code and its place in your application, a lot of stuff ends up under the utils namespace, thereby defeating the whole purpose. Take time to carefully check whether there's a more suitable location for your code before putting it in the utils bucket.

This is particularly applicable if you're considering adding lots of code to a single class in the utils namespace. Looking again at Ext JS, there are lots of specialized namespaces (such as Ext.state or Ext.draw. If you were working with an application with lots of mathematics, perhaps you'd be better off with the following namespace and directory structure:

Ext.define('CultivateCode.math.Combinatorics', {
   // implementation here!
});
Ext.define('CultivateCode.math.Geometry', {
   // implementation here!
});

The directory structure for the math namespace is shown in the following screenshot:

structure-applications-img-6

This is another situation where there is no definitive right answer. It will come to you with experience and will depend entirely on the application you're building. Over time, putting together these high-level applications, building blocks will become second nature.

Money can't buy class

Now that we're learning where our classes belong, we need to make sure that we're actually using the right type of class. Here's the standard way of instantiating an Ext JS class:

var geometry = Ext.create('MyApp.math.Geometry');

However, think about your code. Think how rare it's in Ext JS to actually manually invoke Ext.create. So, how else are the class instances created?

Singletons

A singleton is simply a class that only has one instance across the lifetime of your application. There are quite a number of singleton classes in the Ext JS framework. While the use of singletons in general is a contentious point in software architecture, they tend to be used fairly well in Ext JS.

It could be that you prefer to implement the mathematical functions (we discussed earlier) as a singleton. For example, the following command could work:

var area = CultivateCode.math.areaOfCircle(radius);

However, most developers would implement a circle class:

var circle = Ext.create('CultivateCode.math.Circle', { radius: 
radius }); var area = circle.getArea();

This keeps the circle-related functionality partitioned off into the circle class. It also enables us to pass the circle variable round to other functions and classes for additional processing.

On the other hand, look at Ext.Msg. Each of the methods here are fired and forget, there's never going to be anything to do further actions on. The same is true of Ext.Ajax. So, once more we find ourselves with a question that does not have a definitive answer. It depends entirely on the context.

This is going to happen a lot, but it's a good thing! This article isn't going to teach you a list of facts and figures; it's going to teach you to think for yourself. Read other people's code and learn from experience. This isn't coding by numbers!

The other place you might find yourself reaching for the power of the singleton is when you're creating an overarching manager class (such as the inbuilt StoreManager or our previous SessionManager example). One of the objections about singletons is that they tend to be abused to store lots of global state and break down the separation of concerns we've set up in our code as follows:

Ext.define('CultivateCode.ux.grid.GridManager', {
  
   singleton: true,
   currentGrid: null,
   grids: [],
 
   add: function(grid) {
       this.grids.push(grid);
   },
 
   setCurrentGrid: function(grid) {
       this.focusedGrid = grid;
   }
});

No one wants to see this sort of thing in a code base. It brings behavior and state to a high level in the application. In theory, any part of the code base could call this manager with unexpected results. Instead, we'd do something like this:

Ext.define('CultivateCode.view.main.Main', {
   extend: 'CultivateCode.ux.GridContainer',
 
   currentGrid: null,
   grids: [],
 
   add: function(grid) {
       this.grids.push(grid);
   },
 
   setCurrentGrid: function(grid) {
       this.currentGrid = grid;
   }
});

We still have the same behavior (a way of collecting together grids), but now, it's limited to a more contextually appropriate part of the grid. Also, we're working with the MVVM system. We avoid global state and organize our code in a more correct manner. A win all round.

As a general rule, if you can avoid using a singleton, do so. Otherwise, think very carefully to make sure that it's the right choice for your application and that a standard class wouldn't better fit your requirements. In the previous example, we could have taken the easy way out and used a manager singleton, but it would have been a poor choice that would compromise the structure of our code.

Mixins

We're used to the concept of inheriting from a subclass in Ext JS—a grid extends a panel to take on all of its functionality. Mixins provide a similar opportunity to reuse functionality to augment an existing class with a thin slice of behavior.

An Ext.Panel "is an" Ext.Component, but it also "has a" pinnable feature that provides a pin tool via the Ext.panel.Pinnable mixin.

In your code, you should be looking at mixins to provide a feature, particularly in cases where this feature can be reused. In the next example, we'll create a UI mixin called shakeable, which provides a UI component with a shake method that draws the user's attention by rocking it from side to side:

Ext.define('CultivateCode.util.Shakeable', {
   mixinId: 'shakeable',
 
   shake: function() {
       var el = this.el,
           box = el.getBox(),
           left = box.x - (box.width / 3),
           right = box.x + (box.width / 3),
           end = box.x;
 
       el.animate({
           duration: 400,
           keyframes: {
               33: {  
                   x: left
               },
               66: {
                   x: right
               },
                100: {
                   x: end
               }
           }
       });
   }
});

We use the animate method (which itself is actually mixed in Ext.Element) to set up some animation keyframes to move the component's element first left, then right, then back to its original position. Here's a class that implements it:

Ext.define('CultivateCode.ux.button.ShakingButton', {
   extend: 'Ext.Button',
   mixins: ['CultivateCode.util.Shakeable'],
   xtype: 'shakingbutton'
});

Also it's used like this:

var btn = Ext.create('CultivateCode.ux.button.ShakingButton', {
   text: 'Shake It!'
});
btn.on('click', function(btn) {
   btn.shake();
});

The button has taken on the new shake method provided by the mixin. Now, if we'd like a class to have the shakeable feature, we can reuse this mixin where necessary.

In addition, mixins can simply be used to pull out the functionality of a class into logical chunks, rather than having a single file of many thousands of lines. Ext.Component is an example of this. In fact, most of its core functionality is found in classes that are mixed in Ext.Component.

This is also helpful when navigating a code base. Methods that work together to build a feature can be grouped and set aside in a tidy little package. Let's take a look at a practical example of how an existing class could be refactored using a mixin. Here's the skeleton of the original:

Ext.define('CultivateCode.ux.form.MetaPanel', {
   extend: 'Ext.form.Panel',
 
   initialize: function() {
       this.callParent(arguments);
       this.addPersistenceEvents();
   },
 
   loadRecord: function(model) {
       this.buildItemsFromRecord(model);
       this.callParent(arguments);
   },
 
   buildItemsFromRecord: function(model) {
       // Implementation
   },
 
   buildFieldsetsFromRecord: function(model){
       // Implementation
   },
 
   buildItemForField: function(field){
       // Implementation
   },
 
   isStateAvailable: function(){
       // Implementation
   },
 
   addPersistenceEvents: function(){
     // Implementation
   },
 
   persistFieldOnChange: function(){
       // Implementation
   },
 
   restorePersistedForm: function(){
       // Implementation
   },
 
   clearPersistence: function(){
       // Implementation
   }
});

This MetaPanel does two things that the normal FormPanel does not:

  • It reads the Ext.data.Fields from an Ext.data.Model and automatically generates a form layout based on these fields. It can also generate field sets if the fields have the same group configuration value.
  • When the values of the form change, it persists them to localStorage so that the user can navigate away and resume completing the form later. This is useful for long forms.

In reality, implementing these features would probably require additional methods to the ones shown in the previous code skeleton. As the two extra features are clearly defined, it's easy enough to refactor this code to better describe our intent:

Ext.define('CultivateCode.ux.form.MetaPanel', {
   extend: 'Ext.form.Panel',
 
   mixins: [
       // Contains methods:
       // - buildItemsFromRecord
       // - buildFieldsetsFromRecord
       // - buildItemForField
       'CultivateCode.ux.form.Builder',
 
       // - isStateAvailable
       // - addPersistenceEvents
       // - persistFieldOnChange
       // - restorePersistedForm
       // - clearPersistence
       'CultivateCode.ux.form.Persistence'
   ],
 
   initialize: function() {
       this.callParent(arguments);
       this.addPersistenceEvents();
   },
 
   loadRecord: function(model) {
       this.buildItemsFromRecord(model);
       this.callParent(arguments);
   }
});

We have a much shorter file and the behavior we're including in this class is described a lot more concisely. Rather than seven or more method bodies that may span a couple of hundred lines of code, we have two mixin lines and the relevant methods extracted to a well-named mixin class.

Summary

This article showed how the various parts of an Ext JS application can be organized into a form that eases the development process.

Resources for Article:


Further resources on this subject: