- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 09-03-2018 12:52 PM
This article will be about loading a basic set of functionality into memory that will then be used by subsequent business rules; hence, it's name Bootstrapping Business Rules.
Using the same practice as OOTB by which various objects are readily available when Business Rules run (Prototype, Current, Previous, gs, etc), this technique will append custom objects to the running context, normally a Singleton.
This is done to reduce clutter, decrease JavaScript ticks or cycles, load precise functionality required by the specific type of Business Rule, take advantage of JavaScripts implicit access of variable scope, shorter and less complicated lexical context, to concentrate functionality in libraries that can be reused, performance boost (when possible) and increase readability. In turn, maintenance of a smaller and easier to read code base leads to reduced operational costs.
To me, the beauty isn't really in all of the above but that the architecture is extremely flexible where taking it a step further: turn application behavior configurable, flip a switch functionality, refactoring, introduction of modules or adapters, etc, which ends up meaning more time freed to concentrate on cleaner business implementations.
The Strategy is as follows.
1. Create a Script Include that corresponds to each specific stage/state of the executing Object; say, Incident, Change Request, or Custom Object during New, Assigned, PIR, In Route, etc. The Script Include loaded into memory will not contain functionality to be used at any other time except that relevant to a very specific stage such as on Before Insert (New) Business Rule.
The Script Include for functionality that belongs to Before Insert Context for an Incident in the New state will look like:
/**
* @description
* The Script Include name is Prefix with the Object sys_class_name
* as to maintain object in logical order when searching the script include list view
*/
var IncidentBeforeInsertNew = {
/**
* @description
* Adjust Short Description for configured assignment gruops
*/
adjustShortDescriptionForAssignmentGroup: function () {
var assignmentGroup = current.getValue('assignment_group');
var shortDescriptionPrefix = AssignmentGroupProperty.ShortDescriptionPrefix[assignmentGroup];
//only assignment group with a required prefix gets loaded executed. this may also be achieved through conditions.
if (shortDescriptionPrefix) {
var short_description = short_ + current.getValue('short_description');
current.short_description = shortDescriptionPrefix + short_description;
}
},
/**
* @description
* Flag Incident for tracked C Level employees
* and set assignment group to correct executive group
**/
handleOpenedByExecutive: function () {
if (current.caller_id.u_isExecutive) {
if (SysUserBAL.isTrackableExecutive(current.caller_id)) { //SysUserBAL.isTrackableExecutive carries out business logic for what it takes for an executive to be tracked
current.u_executive_opened = true;
var sys_class_name = current.getValue('sys_class_name');
//SysUserExecutiveProperty contains Configuration for executive users
var executiveAssignmentGroup = SysUserExecutiveProperty[sys_class_name].assignment_group;
if (executiveAssignmentGroup) {
current.assignment_group = executiveAssignmentGroup;
}
}
}
}
}
Common functionality throughout all stages is then placed inside context specific Script Includes such as IncidetBAL for functionality available at anytime form anywhere (as it grows it gets refactored), IncidentAfter (for functionality used after in either update or insert but not specific to either Update or Insert), IncidentAfterInsert (for functionality only available after insert), etc. Limiting the amount of behavior inside Script Includes is done to address the Banana/Gorilla problem with Object Oriented Programming Languages where a caller function asks for a Banana yet gets back the Gorilla along with the entire stem (Banana Bunch). Then, if so wished, the prototype property can be used for more complex functionality that instead of thinking about parent/child relationships, concentrates on Objects and Behaviors by creating objects that links functionality together through the prototype property. Think about behavior rather than relationship can be very helpful: what Object requires what functionality.
2. Bootstrap the business Rule
The business rule will run for a very specific behavior. In the example for this article it will only run Before Insert, which for all purposes means current.isNewRecord()
Name: Bootstrap Before Insert Incident
When: Before -> Insert
Order: 1 (or whenever your bootstrapping must happen)
Advanced: true
Condition -> Whatever Constitutes new to all of your business processes. Say, current.state == incidentState.DRAFT. or if its truly current.isNewRecord().
the Business Rules looks like:
var BeforeInsertingNew = BeforeInsertingNew || IncidentBeforeInsertNew;
I tend to like to set the Singletons in the bootstrapping business rule, even if unnecessary. I have a fetish for the explicitness and at a glance information for the very first business rule.
The Business Rule could have been like loading a plugin by calling the Script Include such below, then adding documentation in the Description field of the business rule:
IncidentBeforeInsertNew;
Also notice that the IIFE used to control variable collisions is gone in favor of the Singleton declaration from above
(function executeRule(current, previous /*null when async*/) {
// Add your code here
})(current, previous);
Why has the IIFE been removed?
To bypass what I lazily refer to as Tick or Cycle and universally known as Event Loop Cycle. I want to reduce traversing the lexical scope when loading the function into the stack, calling it, etc. Basically, to reduce everything that happens under the JavaScript hood.
When the Rhino engine runs it builds the lexical scope, performance adjusting our code, then when code is called, put in a stack and so on. When it sees an IIFE, JavaScript executes it; that means that one cycle has been given up to executing, then the code inside the IIFE is itself executed, taking two cycles to execute the functionality. And, because inside the IIFE can be a new IncidentUtil, a third cycle is added to the execution of the BusinessRule. It doesn't stop there, another cycle is again taken up by executing whatever functionality is needed inside new IncientUtil. Multiply that by as many business rules as there are, and there is the reason why I favor Singletons over IIFEs for BusinessRules. A second point is that Business Rules seem purely Mediators more than actual handlers. They are traffic cops for code, rather than executing code themselves.
With a bootstrapped singleton, the weight of re-instantiating IncidentUtil inside each Business Rule is reduced to once, regardless of the number of times called anywhere else there will be just one singleton, therefore loaded only once. This also boosts performance as the Engine finds one Object in one location faster to fetch than many at different lexical locations.
Note that variable collision is no longer a problem as functionality has been moved into precise Script Includes and exposed by a single entry point, the Bootstrapped Singleton BeforeInsertingNew. If someone forgets to use the bootstrapped function, it doesn't matter, the Singleton is already in memory and ready to be used.
Once the Bootstrapped Business Rule is completed, it can be called for all subsequent rules:
3. Use Bootstrapped Singleton from Subsequent Business Rules
Name: AdjustShortDescriptionForAssignmentGroup
When: Before -> Insert
Advanced = true
Condition: current.assignment_group.changes()
Content:
BeforeInsertingNew.adjustShortDescriptionForAssignmentGroup();
Key to note is that the explicit passing of the objects current and previous to script includes is omitted when calling AfterInsertingNew.adjustShortDescriptionForAssignmentGroup. This works because of JavaScript Scoping. Just like a properly constructed Prototype chain will always point to the correct this, inside the hood, Current will always be Current. It is then up to the developer how to architect its usage.
4. Call Another Function in Bootstrapped Script Include
Name: Flag & Assign when Opened By Exec
When: Before -> Insert
Advanced: true
Condition: current.caller_id.vip == true
Content:
BeforeInsertingNew.handleOpenedByExecutive();
In Conclusion
The above strategy is my default architecture as I feel that code is written for human consumption, which causes me to ignore OOTB practices in favor of more established JavaScript design patterns. In turn I feel that my code is easier to read and maintain, thereby the more beneficial it is to those who interact with it in the future. Including my future self.
By transferring unwarranted lines of code from Business Rules to Script Includes, code structure can be reused, refactored, pulled out, or plugged in trivially. This enables the coder to better react when, say, implementation specific performance issues are encountered, or an Implementation architect changes to a new architecture to address a specific need.
An example would be introduce an Event Driven architecture to separate event consumption in very large volume systems.
Also, though not demonstrated in this article, a Release Business Rule run as the very last Business Rule destroys the Singletons created in the Bootstrap Business Rule. This is done to address some lingering memory issues that are inherent to JavaScript garbage collection.
- 551 Views