Module Management - Part 0 - Analysis
I was going to do a single post on how JavaScript module management is done on Peso, however it ended up being a rather long post. So instead I thought it would be more manageable to break it down into a number of smaller posts.
This first post covers, in a sense, the background to how I came to make the decision to develop within the framework module management capabilities. This introductory blog post is still rather large, so I have tried to clarify my thoughts under the following headings.
A couple of issues need to be mentioned up front.
This is a well known issue and a number of attempts have been made to define standards in this area. However, these tend to be implementation specific and/or framework specific and none have reached the level of a de facto standard - AMD and Node.js are prominent examples. Specifically with regards to EA neither of these are supported.
Note however, that there are two main ways of establishing a scope boundary within the Javascript language - through the use of an object or through the use of a function. If using an object, then components within the same object can refer to things within the object through the "this" reserved word. If using a function, then any components defined within the scope of a function are accessible only within the function. It's also important to note that objects can be captured within the scope of a function, that is a Javascript function can be used as a closure.
The common way to deal with this is to use a hierarchical object structure to implement a namespace path. For example if you had a namespace identified by "company.department.product", then you could have a global (or top level) object/module for "company". Then within it an attribute called "department" which referred to an object/module for that namespace and so on and so forth. This isn't without some issues, but I will discuss them in a future post.
From what can be gathered from reading the EA user guide and having a look through the examples provided, the way to break up code in model addins is as follows.
Firstly a single "JavascriptAddin" stereotyped class element is created, which provides an entry point for the various events that addins can listen for. A developer can then add "signals" to the element for each of these. Furthermore, a developer can create attributes and operations for the element which translate to Javascript attributes and methods. Additional code can be placed by creating additional class elements and using attributes and methods to contain the required code. And then dependency connectors can be drawn from the "JavascriptAddin" element to the other elements. Having the dependency connector in place has the effect of making the Javascript for the depended on class element be available to the addin code through a standard creator function (e.g. new SomeClass()).
This works fine for small addins, however the approach has the following constraints.
The model addin functionality is very new, hence it is likely that there might be changes by Sparx Systems brought in which would help with these issues. This would also have the effect of invalidating any custom work. I would prefer not to have to support this type of functionality within the framework, therefore I would welcome any product supported changes in this area.
And following up from the previous two points, and I guess the main but implicit conclusion is that, because of a combination of factors, a relatively sophisticated model addin framework for Javascript will necessarily need some sort of custom functionality for module management.
[Originally posted here.]
This first post covers, in a sense, the background to how I came to make the decision to develop within the framework module management capabilities. This introductory blog post is still rather large, so I have tried to clarify my thoughts under the following headings.
- Requirements
- JavaScript Quick Tour
- EA Capabilities and Constraints
- Some Conclusions
Requirements
My recent programming background is in languages like Java and C# which have well developed namespacing and module import control capabilities. I'm very used to organising code in this way and it feels very natural to me.A couple of issues need to be mentioned up front.
- Javascript doesn't have any namespacing capabilities, and pre ECMA2015 it did not have any sophisticated module import/export capabilities.
- In the current version of EA, there doesn't seem to be any obvious namespacing capabilities for Javascript in the in-model addin functionality. Imports are modeled by creating Dependency connectors between a JavascriptAddin stereotyped element and any other elements containing Javascript code.
- I don't want to pollute the global namespace.
- Namespacing should be based on the common dot (.) notation. e.g. company.department.application.class.
- Managing namespaces should not be an arduous or difficult task.
- Minimum amount of code should be involved to indicate imports and exports
- The framework should cater for the use of third party modules and to enable doing do so with minimal modification to the 3rd party modules.
Javascript Quick Tour
As mentioned above Javascript does not have any explicit mechanism by which a number of language components (variables, classes, object, functions) can be packaged within a defined module boundary. By which I mean that there isn't an explicit builtin language feature that is specifically to be used to define a boundary or scope space that can be used to define a module.This is a well known issue and a number of attempts have been made to define standards in this area. However, these tend to be implementation specific and/or framework specific and none have reached the level of a de facto standard - AMD and Node.js are prominent examples. Specifically with regards to EA neither of these are supported.
Note however, that there are two main ways of establishing a scope boundary within the Javascript language - through the use of an object or through the use of a function. If using an object, then components within the same object can refer to things within the object through the "this" reserved word. If using a function, then any components defined within the scope of a function are accessible only within the function. It's also important to note that objects can be captured within the scope of a function, that is a Javascript function can be used as a closure.
The Module Pattern
Over time common patterns have been developed by the Javascript community to provide module capabilities. The most common is what is referred to as the Module Pattern. There are many places on the blogosphere explaining what it is and how it works, see this link for a good and comprehensive explanation. A very basic description is that an immediately executable anonymous function is used to define a scope boundary and then an object is returned from the function, which defines the module for calling code to use. Below is a very simple example.INC Cloudy Models Blog.common var aModule = (function () { var prompt = '> '; function setPromptInternal(newPrompt) { prompt = newPrompt; } function outputInternal(output) { dbg(prompt + output); } var module = {}; module.setPrompt = setPromptInternal; module.output = outputInternal; return module; })(); aModule.output('hello'); // Outputs '> hello' aModule.setPrompt('bob >'); aModule.output('hello'); // Outputs 'bob > hello'A variation on the above code is to pass a variable to the anonymous function which gives the module a way to self-register itself within a namespace scheme. See example for an example.
(function (root) { <snip - code left out to maintain brevity> var module = {}; module.setPrompt = setPromptInternal; module.output = outputInternal; root.bModule = module; })(this); this.bModule.output('hello'); // Outputs '> hello' this.bModule.setPrompt('bob >'); this.bModule.output('hello'); // Outputs 'bob > hello'Note that "this" is passed to the anonymous function as the parameter "root".
Namespace Hierarchies
The use of the Module Pattern works fairly well in that module variables and other names don't pollute the global scope, however if the number of modules increases we end up with a lot of modules being defined at the global scope, and then the module names themselves can clash.The common way to deal with this is to use a hierarchical object structure to implement a namespace path. For example if you had a namespace identified by "company.department.product", then you could have a global (or top level) object/module for "company". Then within it an attribute called "department" which referred to an object/module for that namespace and so on and so forth. This isn't without some issues, but I will discuss them in a future post.
ECMA2015 Import/Export
With ECMA2015 a sophisticated way of performing module imports has become part of the standard, however, this is still relatively new and much of the 3rd party modules do not support it. For more information on this topic, see the entries on import and export, respectively, on the Mozilla website. More importantly, however, the 'import' keyword doesn't seem to work in EA, and hence these are not available to us as an option.EA Capabilities and Constraints
I will restrict the discussion on module management to the new Model Addin functionality. The traditional Scripting functionality is as it was in previous versions and it remains the same with regards to scripts, script groups, use of the !INC directive, etc. With regards to the Model Addin functioanilty, EA15 doesn't have any explicit module management capabilities, And in particular does not seem to have support for the Module Pattern which is ubiquitous in Javascript.From what can be gathered from reading the EA user guide and having a look through the examples provided, the way to break up code in model addins is as follows.
Firstly a single "JavascriptAddin" stereotyped class element is created, which provides an entry point for the various events that addins can listen for. A developer can then add "signals" to the element for each of these. Furthermore, a developer can create attributes and operations for the element which translate to Javascript attributes and methods. Additional code can be placed by creating additional class elements and using attributes and methods to contain the required code. And then dependency connectors can be drawn from the "JavascriptAddin" element to the other elements. Having the dependency connector in place has the effect of making the Javascript for the depended on class element be available to the addin code through a standard creator function (e.g. new SomeClass()).
This works fine for small addins, however the approach has the following constraints.
- It's inferred (my interpretation) from the documented approach that it is expected that developers would create one class element for each Javascript class/object. It's a common approach in object oriented programming to have many classes with a few methods, rather than a few classes with many methods. Consequently, the number of elements needing to be created, and have dependencies created for them can become unwieldy.
- EA15 does not provide a way to customise the creator function that gets generated for a depended on class. A remedy to the previous point might have been to return a module (as in the module pattern) from the creator function, however there doesn't seem to be any options to customize this in EA and hence this option is not available to us.
- The ECMA2015 "import" reserved word is not available in EA - the use of which results in an error. Therefore third party libraries cannot be readily used, without considerable modification.
- There's no support for transitive dependencies. That is, we can't have a situation where A depends on B and B depends on C. Assuming A is the JavascriptAddin element, then to have C included in the code available to the addin, we have to have A depend on B and also have A depend on C.
- All the creator functions are available at the global scope, so every class knows about every other class, hence polluting the global namespace. Naming of classes needs to be carefully considered to avoid clashes.
- There is no namespacing available. Different classes with the same name can be placed in different packages, however this is ignored by the model addin functionality. When this situation arises, only one implementation for the class name is available. It is unknown how EA chooses which implementation to use given two depended on classes with the same name.
Some Conclusions
I don't foresee that the Javascript standards will move in the direction of providing greater support in this area, or at least not in the near future.The model addin functionality is very new, hence it is likely that there might be changes by Sparx Systems brought in which would help with these issues. This would also have the effect of invalidating any custom work. I would prefer not to have to support this type of functionality within the framework, therefore I would welcome any product supported changes in this area.
And following up from the previous two points, and I guess the main but implicit conclusion is that, because of a combination of factors, a relatively sophisticated model addin framework for Javascript will necessarily need some sort of custom functionality for module management.
[Originally posted here.]
Comments
Post a Comment