Why we bundle assets of a similar type together
Traditional monolithic applications typically organize the assets that form the app (e.g. HTML templates, CSS stylesheets, etc) into horizontal seams of the same asset-type, so that the app can be efficiently deployed to the browser. Conversely, BladeRunnerJS apps organize their assets into a set of vertical feature-based slices, that we call Blades. This way of organizing code makes adding and removing business features, and testing business features in isolation much easier.
The problem with splitting your app into vertical, feature-based slices is that, for efficiency reasons, the application must be re-composed into horizontal technology-based seams before it can be deployed to the browser. And, since having a production environment that is subtly different to your development environment is detrimental to productivity, this bundling activity must occur on-the-fly, every time the page is refreshed.
This is what BladeRunnerJS does; it allows you to compose your application as a set of feature blades, while deploying your application as a set of bundles of the same asset-type.
When we bundle assets
There are a number of scenarios in which it is necessary to deploy a set of bundles to the browser. In each of these scenarios, what is actually sent to the browser will vary; for example, we don't bundle workbench components or test code in the production application. These scenarios are as follows:
- Aspect Page (so that app can be viewed in a browser)
- Blade Workbench Page (so that a blade can be interacted with in isolation)
- Aspect Test (so that code used by the aspect can be tested)
- Blade Test (so that a single blade can be tested in isolation)
- BladeSet Test (so that common code used by multiple blades can be tested)
- Library Test (so that a re-usable library can be tested)
- Blade Workbench Test (so that any workbench widgets can be tested)
What assets actually are
We use the generic word Assets to describe all of the different types of content that can comprise an application. This includes:
- JavaScript Source
- HTML Templates
- CSS Stylesheets
- CSS Resources (e.g. Images, Typefaces, etc)
- I18N Properties Files
- XML Configuration
For more information see the concept section on Assets.
Assets that contain references to other assets that should also be bundled are known as linked-assets, and in BladeRunnerJS these include the following asset types:
- JavaScript Source
- HTML Templates
- XML Configuration
JavaScript Source is a special type of linked-asset known as a source-module. Source-Modules are addressable using a require path, and usually relate to a single JavaScript file on disk, but a single source-module may also be composed from multiple JavaScript files too.
Asset Containers & Asset Locations
Asset-Containers represent the broad areas on disk where assets can be found by the BRJS model. The following are all asset-containers:
- Aspect
- BladeSet
- Blade
- Workbench
- Test Pack
- Library
Asset-Locations on the other hand represent the exact directories within an asset-container where assets can be found. The following are all asset-locations for example:
resources
src
src-test
Whereas the discovery of the available asset-containers is a model concern, the discovery of the asset-locations within each asset-container is a plug-in concern (the AssetLocationPlugin
in this case).
Some asset-containers are bundlable, since there is a corresponding scenario for which it is useful to create a bundle for them (see 'When we bundle assets'). Such bundlable asset-containers provide a list of the related asset-containers that should also be included when a bundle is to be generated.
How assets are bundled together
The bundling process starts by determining a list of seed linked-assets for the bundlable asset-container in question, and adding these to the backlog of linked-assets that need to be processed. One linked-asset from the backlog is processed at a time, as follows:
- Any source-modules referred to by the linked-asset are added to the backlog (remembering that source-modules are by definition linked-assets).
- The asset-location within which the source-module resides, and any dependent asset-locations of that asset-location, are scanned for more linked-assets to be added to the backlog.
- Only source-modules, linked-assets and asset-locations that have never been processed before are re-processed, so that each is only processed once, and so that processing does not continue indefinitely.
- linked-assets that contain references to aliases are processed in much the same way as if they had direct source-module references, except that the alias is swapped for the concrete source-module it points as part of the bundling process.
- Once the backlog is empty, the final bundle-set has effectively been generated, containing the list of all source-modules, aliases and asset-locations that could potentially be bundled.
- Exactly which bits of the bundle-set will actually be sent to the client depends on the tags used within the index page, and whether the
HTMLResourceService
andXMLResourceService
classes are used within the application.
How bundlers work in practice
There are three basic scenarios in which you will run your application, and bundling works slightly differently in each of them:
Displaying in a browser during development Running tests on your code Building a deployable application
Scenario 1 – Displaying in a Browser during Development
During development, you would want to display a blade in a browser, either in isolation (in a workbench page, while you are working on it) or as part of the whole application, which you would do by viewing the index.html
page of a particular aspect.
In either case, every time the browser requests a page, the bundlers run as servlets within BRJS’s embedded app server. They treat the requested page as a "seed" file and perform a recursive dependency analysis (which is to say that they check which resources are going to be needed to display the page, and then go and fetch them), before producing bundle files that contain all the code needed to run the application.
This means that any time a line of source code is changed, that affects any part of the page being displayed, the result can be seen just by refreshing your browser. The bundlers are written with performance in mind, and workbench pages typically take just a few tenths of a second to reload.
Scenario 2 – Running Tests on Your Code
Test code is placed in the tests
directory of the appropriate aspect, bladeset or blade (depending on exactly what you are testing at the time). BRJS uses js-test-driver to run tests, and the JsTestDriver.conf file – a copy of which resides in the tests folder being used at the time – determines which tests to run. The bundlers are executed by js-test-driver every time tests are run.
See the Tests concepts page, Writing Tests and Running Tests.
Scenario 3 - Building a Deployable Application
When deploying an app, the bundlers generate static bundle files (which can optionally be archived in a WAR) and can be deployed to a production environment. BRJS and thus Bundlers are not used in a production environment.
For more information see Building and Deploying Apps.
Where next?
The Bundlers generally rely on the BRJS Domain Model and Dependency Analysis in order to build their bundles.
The BRJS CLI comes with a bundle-deps
that allows you to visualize the dependencies of a bundle.