Just some thoughts on how this can be constructed in a little differently, but with large ramifications for re-use and optimization.
@MikeTschudi - If you are willing to give this a go, @jgravois and I are more than willing to help add the various modules into ArcGIS Rest JS, as well as help with "zen of functionalization" questions, and ideas around how to construct the "orchestrator" code that would remain in this repo.
On to our story... let's start w/ a famous quote about programming and reusability...
I think the lack of reusability comes in object-oriented languages, not functional languages. Because the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
If you have referentially transparent code, if you have pure functions — all the data comes in its input arguments and everything goes out and leave no state behind — it’s incredibly reusable.
-- Peter Seibel in Coders at Work
The ArcGIS Rest JS and Hub.js libraries have taken this to heart, and with few exceptions, those libraries are simply collections of functions. While technically not "pure functions" because they communicate with the Portal API, aside from that we follow the tenets of writing functions who's output is directly tied to their inputs - there is no "shared state" involved.
What's great about this, is that from the perspective of a developer consuming the modules, they just import
the functions they need.
So - let's suppose we build a module for working with feature services - say feature-service-admin
, and we start with two main functions: serializeService
and constructService
The serialize
function looks at an existing service, and returns a hash that can be used as a template to construct copies of the service. And the construct
takes one of those hashes and constructs the Item.
And the arcgis-clone-js
module has some class (or just more functions) that "orchestrate" the calls to those functions. This pattern keeps the "stateful" part of the solution (the orchestrator) as small as possible , while also allowing the functions to be composed into other solutions.
What's more, then I can come along and add some function like addFieldToService
, and removeFieldFromService
to our feature-service-admin
module.
All cool... what's great is that my adding those function, does not change the size of your built application. I could add 200 more functions in there... and only apps that import
those functions will be impacted. This is possible because modern build tools like webpack or rollup can optimize the packaged code by simply dropping all functions that are not directly import
-ed (or used by the imports).
In contrast, using an OOP inheritance hierarchy, if app just needs is one function on a one class... aka the "banana"... well... we know where that ends up.