A module's Factory is actually the namespace englobing it, the properties are initialized when using the decorators Flux, BuilderView, RenderView.
The factory can hold any other data as it actually is the namespace of the module and any variables can be defined in it. For instance it can be useful to define some static variables of your module you need in some places where the Factory is provided.
For consuming applications, data can be stored/retrieved using the consumersData attribute.
๐ค In regular scenarios, most of the properties belonging to Factory are generated using the decorators mentioned above. You can still turn a namespace into module's factory by providing all of them explicitly, without using decorators.
Builder view constructor, inheriting ModuleRendererBuild
Issued from the decorator BuilderView
๐ฉ Should not be declared as 'any'
Configuration (ModuleConfiguration)
Issued from the decorator Flux
Module constructor, inheriting ModuleFlux, explicitly written.
๐ฉ Should not be declared as 'any'
Persistent data constructor, explicitly written.
To get automatic schema generation in Flux, this class should be decorated with Schema.
๐ฉ Should not be declared as 'any'
Render view constructor, inheriting ModuleRendererRun
Issued from the decorators RenderView
๐ฉ Should not be declared as 'any'
Dynamic data the consumer (host application) may want to associate to the factory.
display name.
Issued from the decorator Flux
id of the module's factory.
Issued from decorator Flux
Whether or not the factory generate a module of type PluginFlux.
Issued from the decorator [[@Flux]]
A unique id that references your package.
Issued from decorator Flux.
mapping <name, url> of resources
Issued from the decorators Flux
combination of [[id]] & packId: uniquely identifies your module in the ecosystem Flux.
A message is what is transmitted from modules to modules through a connection.
Messages have three components: data, configuration and context.
The data part of the message vehicles the core of the information. This is what is emitted by the modules in their output slots.
The configuration part of the message includes some properties meant to override the default configuration of the destination module.
Setting the configuration component of the message is most of the time achieved using an adaptor.
The context part of the message is a mapping {[key:string]: any} that the builder of a Flux application provide (here again, usually using an adaptor).
It is an append only data-structure transferred through the execution paths of flux applications that gets completed along the flow.
It serves at transmitting information from a starting point to a destination point away from multiple connections. The modules are not supposed to use it, they are just in charge to forward it in the most meaningful way (most of the times it is transparent for the modules' developers).
type of the data part in the message
A pipe is the data-structure used by the modules to emit data:
A typical usage:
export class Module extends ModuleFlux {
output$ : Pipe<number>
constructor( params ){
super(params)
this.output$ = this.addOutput({id:'output'})
//...
}
someFunction(value: number, context: Context){
this.output$.next({data:5, context})
}
}
! ๐ค a Pipe is actually a RxJs Subject templated by the Message type
See Message
Generated using TypeDoc
Introduction ๐
Flux is a low code solution to create interactive applications from the description of data-flows and views. This library is the foundation of this low code solution. The flux-builder web-application enables building the applications using a user friendly interface, while flux-runner is available to run them.
Contrasting with other low-code solutions, Flux ecosystem not only allows to define attractive UIs but also enables the description of complex data flows between processing units (called modules). These processing units can be running either on your computer or on remote severs - the latter being especially relevant for long-running computations.
A workflow is organized around modules and connections, an hypothetical workflow can be represented like this:
This describes the data flow of the application. Modules features one or multiple inputs and/or outputs. They can react on incoming messages and emit output messages. Connections support the transmission of messages, always from upstream modules to downstream modules. Any messages emitted by a module can not be altered afterward whatsoever.
In the above workflow, two modules - sliderMdle and viewerMdle - are associated to a view, they can be arranged and styled in a layout editor in a so called rendering-panel, e.g.:
In the above 'application' the data-flow is:
The elementary building blocks of Flux applications are modules, they are exposed by javascript (npm) packages. In practice, one package usually gathers multiple modules related to the same domain - it is called a flux-pack.
We start in the next section the description of Flux-pack creation, and start to correlate the concepts described above with associated classes/functions. The example above is used as guideline through the discussions and examples.
Creation of a flux-pack ๐ฆ
Let's start by implementing a flux-pack containing a (rather empty) module. Here is the declaration of the flux-pack:
Below is the implementation of the module:
Those two files define a valid flux-pack, you can build & publish (in the CDN) the package and start using it in a Flux application.
Module's namespace ๐ญ
All the data related to one module are encapsulated in a namespace, it includes the definition of:
The namespace acts as the Factory of the module: having it includes everything needed to create instances of the various objects related to the module (e.g. configuration, builder view, render view, etc). You can have a look to the unit tests if you are interested about coding flux applications (see also instantiateModules).
Let's move forward and include some logic in the module
Defining inputs & outputs โ
Let's move on the simulator module and add a fake simulation triggered when a message comes in:
The method ModuleFlux.addInput is used to add an input to the module. The key point is to provide a callback to be triggered at message reception.
The method ModuleFlux.addOutput is used to add an output to the module. It returns a Pipe: it is an handle to emit messages.
A word about contract
In the above snippet the type of the data is unknown. It can't be otherwise with no additional information as any modules can have been used to provide the incoming message. That being said, modules are not supposed to work with any kind of incoming data.
The developer can provide expectations in terms of accepted data-structures using the concept of contract:
In this case, the contract defines two expectations on the incoming messages:
Providing a contract to an input helps in terms of pre-conditions checks, data normalization and errors-reporting. You can find out more on contract in this section
What's next ? ๐ง
Adding a configuration ๐
In most of the case, a module relies on some parameters with default values (e.g. a threshold property for the simulatorMdle), which should be editable by the builder of a flux application and persisted with it. This is the purpose of a module's configuration.
The library provides a simple way to define configuration. The developer needs to include a class called PersistentData into the module's namespace and decorate the properties to expose, e.g.:
This is a minimalist example, an advanced use case is presented here.
The above data-structure is automatically injected in the triggered function as a named property configuration:
This configuration argument is called the dynamic configuration as it may have been updated at run time from values included in a message; read more about configuration here.
Defining a rendering view ๐
As presented in the introduction, some modules have interactive views displayed in the rendering-panel. The simplest approach to define such views is to add a new decorator RenderView to the Module class definition.
Providing execution feedbacks ๐ฐ
Module's execution can (and often should) provides feedbacks about their processes, it helps understanding errors, performance bottlenecks, spurious results, etc.
The library introduces the concept of journal for this purpose, they:
Find out more about journal and its underlying companion context here.
Creating plugins ๐
Plugins are like modules, except they can mess up with a companion ๐. It is a feature used quite often in various use case, e.g.:
Some documentation about plugins can be found here
Optimizing performances ๐
The functional nature of Flux (close to nothing is allowed to mutate) allows to provide a simple, yet powerful, caching mechanism that enabled important optimization in common scenarios in scientific computing. Find out more here.
Going beyond simple builder view
It is possible to go beyond a simple representation of the modules in the builder panel. It is possible for instance:
The ModuleRendererBuild documentation is a good place to start if you are interested.