{"action":"created","issue":{"url":"https://api.github.com/repos/dchambers/composerjs/issues/7","labels_url":"https://api.github.com/repos/dchambers/composerjs/issues/7/labels{/name}","comments_url":"https://api.github.com/repos/dchambers/composerjs/issues/7/comments","events_url":"https://api.github.com/repos/dchambers/composerjs/issues/7/events","html_url":"https://github.com/dchambers/composerjs/issues/7","id":54942585,"number":7,"title":"How does this differ from \"Composable Model\" approach?","user":{"login":"james-shaw-turner","id":5113519,"avatar_url":"https://avatars.githubusercontent.com/u/5113519?v=3","gravatar_id":"","url":"https://api.github.com/users/james-shaw-turner","html_url":"https://github.com/james-shaw-turner","followers_url":"https://api.github.com/users/james-shaw-turner/followers","following_url":"https://api.github.com/users/james-shaw-turner/following{/other_user}","gists_url":"https://api.github.com/users/james-shaw-turner/gists{/gist_id}","starred_url":"https://api.github.com/users/james-shaw-turner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/james-shaw-turner/subscriptions","organizations_url":"https://api.github.com/users/james-shaw-turner/orgs","repos_url":"https://api.github.com/users/james-shaw-turner/repos","events_url":"https://api.github.com/users/james-shaw-turner/events{/privacy}","received_events_url":"https://api.github.com/users/james-shaw-turner/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":4,"created_at":"2015-01-20T21:29:44Z","updated_at":"2015-01-21T11:29:24Z","closed_at":null,"body":"There are three core differences between ComposerJS and the pre-existing \"Composable Model\" (CM) library. \r\n\r\n1. ComposerJS handlers are called with input and output properties that it updates. ComposerJs is responsible for calling dependent handlers with the input values derived from previous handlers. CM handlers are  passed a model sandbox and registers event listeners.  It sets field values directly - that trigger cascaded events.\r\n2. ComposerJS has handler functions, CM has handler objects.\r\n3. ComposerJS fixes the shape of the entire object tree including sub-nodes on sealing. CM only fixes the shape of the fields of each node. \r\n\r\n# 1. General discussion of property chaining\r\nIn both proposals a handler may be  dependent on a field/property that is updated by another handler. In the general case property chaining is represented by a Directed Acyclic Graph (DAG).  Both proposals must cope with any arbitrary DAG.\r\n\r\n## Composable Model (CM) approach\r\nThe CM registers handler **objects**. The handler is passed a sandboxed instance of the model in its `init()` method. The handler then adds listeners to any field it wants to use as an input and calls `set(name, value)` to update its output fields. When a property is set externally or internally (by a handler) any interested handler listeners methods are invoked - chaining follows automatically. \r\n\r\n## ComposerJS approach\r\nComposerJS registers handler **functions**. The function is passed model **values** as input and output parameters. The handler function is called whenever one of the inputs changes.\r\nWhenever a property is updated externally the model must find any dependent handlers and execute those. It must then recursively call any other dependent handlers - ensuring all handlers are executed in the correct order. \r\nThe current example only shows a chain starting with an external `set(name, value)` method. It is not clear how it works when handlers are the source of updates. \r\n\r\n# 2. Object versus function handlers\r\n## CM has handler objects\r\nCM handlers are objects that implement the handler interface. As such they have two major life-cycle methods: `init()` which is called immediately after sealing, and `destory()`. This allows a handler to create any resources based on the fixed initial values of the model. e.g. a price rate provider must create a subscription that depends on currency-pair and amount. Similarly when the model is disposed of the `destroy()` method allows the subscription to be freed. \r\nHandlers may have \"business semantic\" methods that can be executed directly on the handler or exported onto the model object. e.g. `model.executeTrade(\"buy\")`; \r\n## ComposerJS has handler functions\r\nHow does ComposerJS copes the scenarios described above.\r\n# 3. Tree shape  \r\n## CM has opaque children\r\nSub-nodes are just (pre)sealed models that are added to a collection. These nodes can be totally independent of the parent node. Each sub-node is an independent model that was itself sealed. The parent acts as a container for opaque children.  Child instances (with different shapes) can be changed during the lifetime of the parent object. This is useful for orders where and order consists of a number of trade legs that could be of different trade types. \r\n\r\n## ComposerJS parent defines shape of children \r\nThe Composer design defines the shape of the entire node tree at seal time and lists must contain items of the same shape. This is overly restrictive.\r\n \r\n"},"comment":{"url":"https://api.github.com/repos/dchambers/composerjs/issues/comments/70822273","html_url":"https://github.com/dchambers/composerjs/issues/7#issuecomment-70822273","issue_url":"https://api.github.com/repos/dchambers/composerjs/issues/7","id":70822273,"user":{"login":"dchambers","id":283294,"avatar_url":"https://avatars.githubusercontent.com/u/283294?v=3","gravatar_id":"","url":"https://api.github.com/users/dchambers","html_url":"https://github.com/dchambers","followers_url":"https://api.github.com/users/dchambers/followers","following_url":"https://api.github.com/users/dchambers/following{/other_user}","gists_url":"https://api.github.com/users/dchambers/gists{/gist_id}","starred_url":"https://api.github.com/users/dchambers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dchambers/subscriptions","organizations_url":"https://api.github.com/users/dchambers/orgs","repos_url":"https://api.github.com/users/dchambers/repos","events_url":"https://api.github.com/users/dchambers/events{/privacy}","received_events_url":"https://api.github.com/users/dchambers/received_events","type":"User","site_admin":false},"created_at":"2015-01-21T11:29:24Z","updated_at":"2015-01-21T11:29:24Z","body":"Ah, very good point @james-shaw-turner!\r\n\r\nBut if I understand your solution 2 correctly, it need not be any more complicated than my _five-line_ solution, as it's only when the model is being sealed that a temporary DAG is required. For example, given that all handlers are ordered within an array at seal time in an order that respects the DAG (as `this.orderedHanders`), then these six lines of code will now give us what we want, where `handlers` is a map this time instead of an array as originally proposed:\r\n\r\n```js\r\nfor(var handler of this.orderedHanders) {\r\n\tif(handler.id in handlers) {\r\n\t\thandler();\r\n\t\thandlers.merge(handler.dependentHandlers);\r\n\t}\r\n}\r\n```\r\n\r\nAgain, this is much easier to debug through than a set of listener call-backs that occur based on change throughout the node tree.\r\n\r\nInterestingly, it's only possible to have a single list of handlers for the duration of the model due to the barring of particular nodes within node-lists being used as _input-properties_, which was necessary to satisfy the additional \"No subscription management code is needed as the number of nodes within the model changes\" criteria that ComposerJs adds over CM.\r\n\r\nOh, for reference, this is the incorrect _five-line_ version for comparison:\r\n\r\n```js\r\nwhile(handlers.length > 0) {\r\n\tvar handler = handlers.pop();\r\n\thandler();\r\n\thandlers.splice(handlers.length, 0, handler.dependentHandlers);\r\n}\r\n```\r\n"}}