Glossary Of This Article
I'll need to refine a few of the commonly used terms, so this article stands a chance of making sense...
A module is a set of functionality, information or structures. What is considered a module though, can vastly vary among developers. It is safe to say that a module is an abstract way of describing reusable functionality. In the same spirit, It is safe to say that a module can span through a number of files. Or several modules can be defined in one file, or a module can be in a single file.
Let's entertain ourselves a bit. Consider these two files, i'll use CommonJS just to give them the Modular scent:
So, these are two modules right? But hey, we can combine them together:
So now we have a new module that includes the functionality of both the
subtraction module. Is this a file that contains two modules? Is this a single module? What i am trying to illustrate here is that a module, is really an abstract term and not something specific and absolute.
Having said that, i'll buy into the general notion that a module is represented by a file in hopes of getting my other messages through, the Built-Module and what a modular application or library really is.
A "module" is a thing that can be require()'d. A "package" is a thing that can be npm'd.
A required()'d "package" is... a module, in the context of the requiring application, or to better elaborate, a Built-Module.
Web Applications and Libraries
A web application is an actual website (i.e. facebook.com). it is consumed by the actual users.
A web library is a package that is intended only for web developers (i.e. jQuery). It is consumed by other libraries or applications. The term includes platform agnostic libraries, what we care about is that they work on the web.
Modular Applications and Libraries
A modular web application, at its production state, will only load a small portion of the codebase, and then, at a later time, dynamically load the parts (built-modules) that are required. Using various techniques like eager or lazy loading.
A modular web library, at its published state, provides multiple bundles that include the whole library or parts of it. For example jQuery now offers a way to create custom builds with only the modules that you need.
Another example of a Modular Library is Modernizr. (Edit 3/24/14: As it turns out Modernizr isn't a Modular application.) There are two ways a Modular Library can be consumed:
- Consume from source, which is only possible if the same Dependency System is used.
- Consume using the provided built-modules.
The second case, is what actually happens in reality, no matter the underlying Dependency System. It is what we call Vendor Libraries and handle them as third-party dependencies that follow a separate flow in our build process.
That is the reason why if your library is to be considered Modular, it has to provide multiple Built-Modules which offer the whole or parts of functionality.
The secret to building large apps is never build them. Break them into smaller pieces then assemble into your big app. @justinbmeyer, 2010
When developing a Library for the web, you publish Packages. A Package is the final end-product and is typically produced by a build operation. Modular Applications can have a single package containing multiple built-modules or provide multiple Packages.
Patterns in Creating Modular Applications
There are primarily two ways to create a Modular Application, using Static or Dynamic Linking. Both ways assume that a core exists, which provides the foundational stack for your library, along with any utility functions or commonly used modules. If a core stack does not exist (i.e. Modernizr does not have a core module) then by default it belongs in the Dynamic Linking camp.
To better understand how Modularity can be performed, our sample application has the following Modules, names represend namespaces:
appIs the bootstrap Module, essentially a file that only contains require statements, gluing all the independent modules together.
app.coreIs the core Module that is your Library's main stack, provides facilities that other built-modules can reuse to interact with each other.
app.utilsIs the Module that provides helper functions which are needed by all the built-modules.
app.moduleAIs a Module that doubles both as a typical Module when in development, and as a built-module when it is built.
app.moduleBIs another Module that has the same modular properties as app.moduleA.
With Static Linking every build-module that is produced contains the core stack and helping libraries. In our case that would be 4 build targets that produce these four files:
app.min.jsIs a built-module that includes the
app.utilsModules. Can be considered a Package.
app.modA.min.jsIs a built-module that includes the
app.moduleAModules. Can be considered a Package.
app.modB.min.jsIs a built-module that includes the
app.moduleBModules. Can be considered a Package.
app.full.min.jsIs a built-module that includes all the Modules. Can be considered a Package.
Static Linking dictates that you require for
With Dynamic Linking, every built-module is being built in isolation, without containing the core stack. In our case, this results in these three files:
app.core.min.jsIs a built-module that includes the
app.utilsModules. Can be considered a package.
app.modA.min.jsIs a built-module that only includes the
app.moduleAModule. It is not a package, it requires to be bundled with
app.core.min.jsin order to operate.
app.modB.min.jsIs a built-module that only includes the
app.moduleBModule. It is not a package, it requires to be bundled with
app.core.min.jsin order to operate.
This pattern does not allow the
app.moduleA Module to directly require
app.utils, by convention. If
app.utils was required by
app.moduleA then the utils code would also exist in the
app.moduleA.min.js file which would result in code duplication.
The Dynamic Linking pattern is like creating as many projects as are your built-modules. When using this pattern, you should think about how these separate projects will interact with each other and how they will perform in harmony as a whole. In our case, we have to make sure the utility functions that all the built-modules depend on, are properly exposed by the Core built-module.
Dynamic Linking is an advanced concept and requires a lot of effort and infrastructure. A typical use case for a Library is when it has several built-modules (10+) and needs to dynamically create Packages based on what the developer needs. Creating all possible combinations statically, would be impractical (~10^10). So what the library author does instead is produce 10 built-modules and concatenate them on demand on the Library's webpage. The built-modules are already built, so concatenation is as good as a build process.
Web applications can only use the Dynamic Linking pattern. The produced built-modules are expected to contain only a specific functionality and not the core stack. That's important, read it again please.
It is important to think through which Modular pattern you will follow when developing your library. The dilemma between Static or Dynamic linking can be rendered moot if you can afford the building stack on your webserver. In that case you can run the build operation at your webserver on-demand and produce custom packages. That however is not always possible, especially if your project's page is hosted on Github or the build operation is very cpu and fs expensive (which is very common).
In the early stages, Static Linking can be more appealing and enable faster development. Require statements are spread all over the app, creating an intertwined mesh of dependencies. When your codebase grows too large though, it can be a challenge to isolate built-modules and move to the Dynamic Linking pattern.
Authoring modular libraries or applications means that you make the effort to use the Dynamic Linking pattern. Enforcing isolation between the core and the various built-modules. That's what a modular library or application is.