Dependencies

In this section, we will explain how dependency management is handled by the framework. This is a very important feature, especially in case of big projects in which you will have to split your source code in several files.

ES6 modules

Dana uses the Javascript ES6 modules notation to load classes and configuration files for the application.

A class is declared in a module as follows:

import $Class from "@Class";

export default $Class.declare("MyClass", { /* class def */ });

Did you know?

  • A module should contain only one class (don’t write a module with multiple classes inside it)
  • The _MyClass variable is an alias (i.e. the tooling is creating the variable with the full path of the class)
  • Class names are unique (two classes can not have the same name)
  • @Class refers to the class named Class (i.e. you don’t need to know the full path of a class to load it)

Aliases

The framework provides class names and class constructors aliases for all framework classes. This mechanism allows to move classes to different packages without any impact on the application side.

There are two different kinds of aliases:

  • _Class which is referencing the fully qualified name of a Class.
  • $Class as a reference to the Class constructor from which you can create instance.

Caution

This implies that all class names are unique.

Injection

The framework comes with a dependency injection (DI) mechanism where the implementation of components used in the application, are resolved by the configuration. A Component declares an abstract class or an abstract model that must be implemented. For a component, there may be multiple implementations. You never need to import a specific implementation (otherwise, you need to import the component by its name and the tooling will inject the right implementation depending on your configuration).

First, you need to declare your component like this:

import $Abstract from "@Abstract";
/**
 * @component
 */
export default $Abstract.declare("AbstractSearch", {
    search: function() {
       return this._search();
    },
    /**
     * @abstract
     */
    _search: function() {
       return this._notImplementedPromise("_search");
    }
);

Caution

  • You MUST annotate the class with the @component in the JSDoc of the class header.
  • The class name of a Component must begin with Abstract as prefix.

Then, here is a typical implementation of the component:

import $AbstractSearch from "@AbstractSearch";
/**
 * @implementation
 */
export default $AbstractSearch.declare("DefaultSearch", {
    /**
     * @override
     */
    _search: function() {
       // do your implementation !
    }
);

Caution

  • You must annotate the class with the @implementation in the JSDoc of the class header.
  • Most of the time the implementations are offered by the vendors.
  • The core may sometimes offer default implementation (in such case, they are called Core<ComponentName>).

And finally, you can use the “Search” component in your application:

import $Search from "@Search";

// $Search will be resolved to right implementation
// In this example, $Search === $DefaultSearch

);