The data dependencies mapping part sounds a little bit like FB's relay proposal https://facebook.github.io/react/blog/2015/02/20/introducing...
Yes. We had a query language that let us string together AND/OR/NOT filters as a tree and then the backend could decompose and query off of it. We'd have a data service per document type being queried. In our situation, customer feedback aggregates, vertical aggregates and customer feedback datums were different. So a page containing components using both would necessarily have two data services working independently.
In my app's scenario, we made a render decision based on the input and output for the component render functions. Prior to rendering the view, if the data was identical to what already existed, no updates occurred. cljs made this cheap. After constructing the template (hiccup-style, more cljs data), if it wasn't different, we'd also skip the DOM update.
For methods on the component (handling various events and such), they all take a channel where they could send whatever events it was that they needed to send.
The data service managing a query for one or more components just blocked its goroutine until it received an update to its query data structure, resolved it, and if it was different than the existing query data structure, would debounce for more changes before sending off the request. Requests back to the server were ordered by an atomic counter (atom 0) such that out of order responses could be processed sanely. Everything was one-way.
Additionally, while we could dynamically render everything, pages were a layout data structure containing 0 or more components with associated data services. These were all described in EDN and thus could be serialized, queried, and rendered server side if necessary.
All of this machinery was pretty small, no more than about 1800 LOC building on top of some excellent cljs libraries.