{"id":35241,"date":"2026-02-16T17:17:35","date_gmt":"2026-02-16T17:17:35","guid":{"rendered":"https:\/\/streamhub.co.uk\/?p=35241"},"modified":"2026-02-17T16:36:31","modified_gmt":"2026-02-17T16:36:31","slug":"architecting-scalable-chart-modules-in-streamhub-analytics","status":"publish","type":"post","link":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/","title":{"rendered":"Architecting Scalable Chart Modules in Streamhub Analytics: Part I"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 4<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span><p>[et_pb_section fb_built=&#8221;1&#8243; _builder_version=&#8221;4.16&#8243; da_disable_devices=&#8221;off|off|off&#8221; global_colors_info=&#8221;{}&#8221; da_is_popup=&#8221;off&#8221; da_exit_intent=&#8221;off&#8221; da_has_close=&#8221;on&#8221; da_alt_close=&#8221;off&#8221; da_dark_close=&#8221;off&#8221; da_not_modal=&#8221;on&#8221; da_is_singular=&#8221;off&#8221; da_with_loader=&#8221;off&#8221; da_has_shadow=&#8221;on&#8221;][et_pb_row _builder_version=&#8221;4.16&#8243; background_size=&#8221;initial&#8221; background_position=&#8221;top_left&#8221; background_repeat=&#8221;repeat&#8221; global_colors_info=&#8221;{}&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.16&#8243; custom_padding=&#8221;|||&#8221; global_colors_info=&#8221;{}&#8221; custom_padding__hover=&#8221;|||&#8221;][et_pb_text admin_label=&#8221;Text&#8221; _builder_version=&#8221;4.27.0&#8243; background_size=&#8221;initial&#8221; background_position=&#8221;top_left&#8221; background_repeat=&#8221;repeat&#8221; global_colors_info=&#8221;{}&#8221;]<\/p>\n<h2 id=\"Part-1:-Problem-Space-&amp;-Design-Goals\" data-local-id=\"0746e3b35c27\" data-renderer-start-pos=\"1\"><strong>Problem Space &amp; Design Goals<\/strong><\/h2>\n<p data-renderer-start-pos=\"39\" data-local-id=\"a0496b3bbb21\">In modern web applications, data visualisation is a critical component that requires flexibility, maintainability, and extensibility. When building a charting system, supporting multiple chart types across heterogeneous rendering requirements quickly becomes an architectural challenge.<\/p>\n<p data-renderer-start-pos=\"327\" data-local-id=\"ce527decc7bb\">In our case, the system needed to support 15+ chart types that users could dynamically add to dashboards. Each chart could be powered by different charting libraries, and although the API responses were consistent, each library expected the input data in its own specific format. What initially seemed like a UI concern soon revealed itself to be a deeper issue involving data transformation, extensibility, and long-term maintainability.<\/p>\n<p data-renderer-start-pos=\"767\" data-local-id=\"fea892388c1a\">This post, focuses on <strong data-renderer-mark=\"true\">the challenges<\/strong> we faced and explains the architectural direction we chose. In the next posts we&#8217;ll see how we addressed them with a small set of design patterns.<\/p>\n<hr role=\"presentation\" \/>\n<p>&nbsp;<\/p>\n<h2 id=\"Technical-Context\" data-local-id=\"8ed94c0766b4\" data-renderer-start-pos=\"953\"><strong>Technical Context\u00a0<\/strong><\/h2>\n<p data-renderer-start-pos=\"973\" data-local-id=\"dde48a3bfe6f\">Our <a class=\"_ymio1r31 _ypr0glyw _zcxs1o36 _mizu1v1w _1ah3dkaa _ra3xnqa1 _128mdkaa _1cvmnqa1 _4davt94y _4bfu1r31 _1hms8stv _ajmmnqa1 _vchhusvi _kqswh2mm _ect4ttxp _syaz13af _1a3b1r31 _4fpr8stv _5goinqa1 _f8pj13af _9oik1r31 _1bnxglyw _jf4cnqa1 _30l313af _1nrm1r31 _c2waglyw _1iohnqa1 _9h8h12zz _10531ra0 _1ien1ra0 _n0fx1ra0 _1vhv17z1\" title=\"https:\/\/streamhub.co.uk\/Analytics\/\" href=\"https:\/\/streamhub.co.uk\/Analytics\/\" data-renderer-mark=\"true\">Analytics product<\/a> is built using Angular as the primary frontend framework. For visualisation, we support multiple libraries depending on the widget type, including <span data-highlighted=\"true\" data-vc=\"highlighted-text\"><span class=\"_kqswh2mm\"><span class=\"_5pioz8co _189e1dm9 _1il9buyh _19lc184f _d0altlke\" data-testid=\"definition-highlighter\">AG<\/span><\/span><\/span> Grid for tabular data and Highcharts for basic chart-based visualisations, along with a few custom-rendered charts.<\/p>\n<p data-renderer-start-pos=\"1262\" data-local-id=\"e376d8344e68\">While the API responses remain consistent, each library imposes its own expectations on how data should be structured. This divergence in data contracts across libraries became a central factor in shaping the overall architecture.<\/p>\n<hr role=\"presentation\" \/>\n<h2 data-local-id=\"641aa7d4df19\" data-renderer-start-pos=\"1495\">\u00a0<\/h2>\n<p>&nbsp;<\/p>\n<h2 id=\"The-Core-Mismatch:-Same-Data,-Different-Library-Expectations\" data-local-id=\"641aa7d4df19\" data-renderer-start-pos=\"1495\"><strong>The Core Mismatch: Same Data, Different Library <\/strong><\/h2>\n<h2 data-local-id=\"641aa7d4df19\" data-renderer-start-pos=\"1495\"><strong>Expectations<\/strong><\/h2>\n<p data-renderer-start-pos=\"1557\" data-local-id=\"715b22dd9ff1\">One of the key challenges was that different visualisation components required the same dataset to be shaped differently.<\/p>\n<p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\">For example, consider a dataset fetched from a consistent API:<\/p>\n<p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">[ <\/span><\/span><\/code><\/span><\/p>\n<p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">\u00a0 \u00a0 {\"device\": \"android\", \"views\": 12,000},<\/span><\/span><\/code><\/span><\/p>\n<p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">\u00a0 \u00a0 { \"device\": \"iphone\", \"views\": 15,000 }<\/span><\/span><\/code><\/span><\/p>\n<p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">]<\/span><\/span><\/code><\/span><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><\/code><\/span><\/p>\n<pre data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\">\u00a0<\/pre>\n<p data-renderer-start-pos=\"1835\" data-local-id=\"bc0fba4c4e06\">A data-grid widget powered by <span data-highlighted=\"true\" data-vc=\"highlighted-text\">ag<\/span>-Grid expects a flat row-based structure:<\/p>\n<p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">rowData = [ <\/span><\/span><\/code><\/span><\/p>\n<p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">\u00a0 \u00a0 {\"device\": \"android\", \"views\": 12,000},<\/span><\/span><\/code><\/span><\/p>\n<p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">\u00a0 \u00a0 { \"device\": \"iphone\", \"views\": 15,000 }<\/span><\/span><\/code><\/span><\/p>\n<p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">]<\/span><\/span><\/code><\/span><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-4\" data-ds--code--row=\"\"><span class=\"token punctuation\">;<\/span><\/span><\/code><\/p>\n<p data-renderer-start-pos=\"2012\" data-local-id=\"e1b268a452f6\">However, a bar chart rendered using Highcharts expects a categorized structure:<\/p>\n<div class=\"fabric-editor-breakout-mark fabric-editor-block-mark css-p8f2xz\" data-mode=\"wide\" data-has-width=\"true\" data-width=\"760\">\n<div class=\"code-block css-y5zsxb\" data-local-id=\"5356c64bd07d\">\n<p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\">series <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span><\/span><\/code><\/span><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">{<\/span><\/span><\/code><\/span><\/p>\n<p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">\u00a0 \u00a0 name<span class=\"token operator\">:<\/span> <span class=\"token string\">\"views\"<\/span>,<\/span><\/span><\/code><\/span><\/p>\n<p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">\u00a0 \u00a0 data<span class=\"token operator\">:<\/span> [<span class=\"token number\">12000<\/span>, <span class=\"token number\">15000<\/span>]<\/span><\/span><\/code><\/span><\/p>\n<p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">}];<\/span><\/span><\/code><\/span><\/p>\n<p><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-6\" data-ds--code--row=\"\">categories <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">\"android\"<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">\"iphone\"<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">;<\/span><\/span><\/code><\/p>\n<p><code class=\"language-typescript\"><\/code><\/p>\n<p>The underlying API data remains consistent, but each library requires a distinct transformation. As the number of supported chart types and libraries grows, this transformation layer becomes a central architectural concern rather than a simple formatting step.<\/p>\n<p>&nbsp;<\/p>\n<\/div>\n<\/div>\n<h2 data-local-id=\"bf94a3efa270\" data-renderer-start-pos=\"2449\">\u00a0<\/h2>\n<h2 id=\"Why-Naive-Approaches-Break-Down\" data-local-id=\"bf94a3efa270\" data-renderer-start-pos=\"2449\"><strong>Why Naive Approaches Break Down<\/strong><\/h2>\n<p data-renderer-start-pos=\"2482\" data-local-id=\"0b3afd866ccb\">A typical early implementation often resembles this:<\/p>\n<div class=\"fabric-editor-breakout-mark fabric-editor-block-mark css-p8f2xz\" data-mode=\"wide\" data-has-width=\"true\" data-width=\"760\">\n<div class=\"code-block css-y5zsxb\" data-local-id=\"dd7478330d91\">\n<p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token keyword control-flow\">switch<\/span> <span class=\"token punctuation\">(<\/span>chartType<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{ <\/span><\/span><\/code><\/span><\/p>\n<p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\"><span class=\"token keyword\">\u00a0 \u00a0 case<\/span> <span class=\"token string\">'bar'<\/span><span class=\"token operator\">:<\/span> <span class=\"token keyword control-flow\">return<\/span> <span class=\"token function\">transformToHighchartsBar<\/span>(data);<\/span><\/span><\/code><\/span><\/p>\n<p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\"><span class=\"token keyword\">\u00a0 \u00a0 case<\/span> <span class=\"token string\">'grid'<\/span><span class=\"token operator\">:<\/span> <span class=\"token keyword control-flow\">return<\/span> <span class=\"token function\">transformToAgGridRows<\/span>(data);<\/span><\/span><\/code><\/span><\/p>\n<p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\"><span class=\"token keyword\">\u00a0 \u00a0 case<\/span> <span class=\"token string\">'line'<\/span><span class=\"token operator\">:<\/span> <span class=\"token keyword control-flow\">return<\/span> <span class=\"token function\">transformToHighchartsLine<\/span>(data);<\/span><\/span><\/code><\/span><\/p>\n<p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">}<\/span><\/span><\/code><\/span><\/p>\n<p><code class=\"language-typescript\"><\/code><\/p>\n<p><code class=\"language-typescript\"><\/code><\/p>\n<\/div>\n<\/div>\n<p data-renderer-start-pos=\"2719\" data-local-id=\"ce80bc5bd0ca\">In practice, this <code class=\"_ca0qyh40 _u5f3m5ip _n3tdyh40 _19bvm5ip _2rkofajl _11c819w5 _1reo1wug _18m91wug _1dqoglyw _1e0c1nu9 _bfhk187e _16d9qvcn _syazi7uo _vwz41kw7 _1i4q1hna _o5721jtm\" data-renderer-mark=\"true\">switch<\/code> statement and the corresponding transformation logic lived inside a single class responsible for orchestrating chart rendering. Over time, this class accumulated multiple responsibilities:<\/p>\n<ul class=\"ak-ul\" data-local-id=\"4de539bc-860d-4140-a9ac-3f34ddff8c93\" data-indent-level=\"1\">\n<li>\n<p data-renderer-start-pos=\"2936\" data-local-id=\"4872371ee564\">Deciding which chart type to render<\/p>\n<\/li>\n<li>\n<p data-renderer-start-pos=\"2975\" data-local-id=\"9a1fc1693693\">Transforming raw API data<\/p>\n<\/li>\n<li>\n<p data-renderer-start-pos=\"3004\" data-local-id=\"64d530a0127b\">Handling library-specific formatting<\/p>\n<\/li>\n<\/ul>\n<p data-renderer-start-pos=\"3044\" data-local-id=\"b45a6f6e356f\">This violated the Single Responsibility Principle and tightly coupled transformation logic with rendering decisions. Every new chart type or library variation requires modifying existing logic, violating the Open\/Closed Principle and increasing the risk of regressions.<\/p>\n<hr role=\"presentation\" \/>\n<h2 data-local-id=\"d352e7a5ee7e\" data-renderer-start-pos=\"3316\">\u00a0<\/h2>\n<h2 id=\"Core-Challenges\" data-local-id=\"d352e7a5ee7e\" data-renderer-start-pos=\"3316\"><strong>Core Challenges<\/strong><\/h2>\n<p>&nbsp;<\/p>\n<h3 id=\"1.-Data-Transformation-Complexity\" data-local-id=\"2905b051e6a2\" data-renderer-start-pos=\"3333\"><strong>1. Data Transformation Complexity<\/strong><\/h3>\n<p data-renderer-start-pos=\"3368\" data-local-id=\"c3ce0a0ae977\">Although the API responses are consistent, each visualisation library enforces its own structural contract. Supporting multiple libraries means handling:<\/p>\n<ul class=\"ak-ul\" data-local-id=\"92f16474-289b-4eae-8ab1-9616e3dd1c05\" data-indent-level=\"1\">\n<li>\n<p data-renderer-start-pos=\"3525\" data-local-id=\"138093883242\">Structural reshaping (row-based vs series-based vs hierarchical)<\/p>\n<\/li>\n<li>\n<p data-renderer-start-pos=\"3593\" data-local-id=\"cb41cb68d508\">Aggregation or grouping logic specific to chart types<\/p>\n<\/li>\n<li>\n<p data-renderer-start-pos=\"3650\" data-local-id=\"ca7eeb99f314\">Consistent normalization so that transformations remain predictable<\/p>\n<\/li>\n<\/ul>\n<p data-renderer-start-pos=\"3721\" data-local-id=\"cdef8de81a7e\">The transformation layer must therefore be reusable and decoupled from the rendering library\u2019s internal expectations.<\/p>\n<hr role=\"presentation\" \/>\n<h3 data-local-id=\"cc001818e7fe\" data-renderer-start-pos=\"3841\">\u00a0<\/h3>\n<h3 id=\"2.-Extensibility-and-Maintainability\" data-local-id=\"cc001818e7fe\" data-renderer-start-pos=\"3841\"><strong>2. Extensibility and Maintainability<\/strong><\/h3>\n<p data-renderer-start-pos=\"3879\" data-local-id=\"0295c5085b10\">As new chart types or visualisation libraries are introduced, the system should extend without requiring modifications to existing transformation logic. Without a modular design, each addition risks breaking unrelated visualisations.<\/p>\n<p data-renderer-start-pos=\"4114\" data-local-id=\"f52722068cb8\">Extensibility is therefore not just desirable\u2014it is essential for long-term maintainability.<\/p>\n<hr role=\"presentation\" \/>\n<h3 data-local-id=\"05234803ccff\" data-renderer-start-pos=\"4209\">\u00a0<\/h3>\n<h3 id=\"3.-Shared-State-Interactions\" data-local-id=\"05234803ccff\" data-renderer-start-pos=\"4209\"><strong>3. Shared State Interactions<\/strong><\/h3>\n<p data-renderer-start-pos=\"4239\" data-local-id=\"1488c114d5cd\">Charts rarely operate in isolation. They react to shared dashboard inputs such as filters, metrics, and time ranges. Without clear architectural boundaries, state changes can trigger cascading re-computations that are difficult to trace and debug.<\/p>\n<p data-renderer-start-pos=\"4488\" data-local-id=\"7e2910a7a112\">A scalable solution must ensure that state orchestration does not leak into transformation logic.<\/p>\n<hr role=\"presentation\" \/>\n<h3 data-local-id=\"d53d8a739b72\" data-renderer-start-pos=\"4588\">\u00a0<\/h3>\n<h3 id=\"4.-Separation-of-Concerns\" data-local-id=\"d53d8a739b72\" data-renderer-start-pos=\"4588\"><strong>4. Separation of Concerns<\/strong><\/h3>\n<p data-renderer-start-pos=\"4615\" data-local-id=\"51329b5603b3\">These challenges highlighted the need for a clear separation of responsibilities across layers. Mixing decision-making, data shaping, and rendering logic within a single class created tight coupling and reduced clarity about where specific responsibilities belonged.<\/p>\n<p data-renderer-start-pos=\"4883\" data-local-id=\"e8879fbb49c1\">A more layered approach was necessary to ensure each concern could evolve independently.<\/p>\n<hr role=\"presentation\" \/>\n<h2 data-local-id=\"e9cd6e0bf7ac\" data-renderer-start-pos=\"4974\">\u00a0<\/h2>\n<h2 id=\"From-Naive-to-Scalable:-Direction-of-Evolution\" data-local-id=\"e9cd6e0bf7ac\" data-renderer-start-pos=\"4974\"><strong>From Naive to Scalable: Direction of Evolution<\/strong><\/h2>\n<p style=\"text-align: left;\" data-renderer-start-pos=\"5022\" data-local-id=\"bb419175377e\">These challenges made it clear that the existing structure could not scale. The architectural evolution looked like this:<span style=\"text-align: center;\">\u00a0 <\/span><span style=\"text-align: center;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<\/span><\/p>\n<p style=\"text-align: center;\" data-renderer-start-pos=\"5022\" data-local-id=\"bb419175377e\">\u00a0 <img loading=\"lazy\" decoding=\"async\" class=\"wp-image-35273 size-full aligncenter\" src=\"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2026\/02\/charts-naive-approach-2.png\" alt=\"\" width=\"551\" height=\"111\" srcset=\"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2026\/02\/charts-naive-approach-2.png 551w, https:\/\/streamhub.co.uk\/wp-content\/uploads\/2026\/02\/charts-naive-approach-2-480x97.png 480w\" sizes=\"auto, (min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) 551px, 100vw\" \/><\/p>\n<p style=\"text-align: center;\" data-renderer-start-pos=\"5022\" data-local-id=\"bb419175377e\">Naive Approach<\/p>\n<p style=\"text-align: left;\" data-renderer-start-pos=\"5022\" data-local-id=\"bb419175377e\">In the naive approach, both decision-making and data shaping were embedded in a single class. This made the system rigid and prone to errors as new chart types were introduced.<\/p>\n<p data-renderer-start-pos=\"5164\" data-local-id=\"68207e2bf0c3\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<img loading=\"lazy\" decoding=\"async\" class=\"wp-image-35270 size-full aligncenter\" src=\"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2026\/02\/charts-flow-diagram-1-1.png\" alt=\"\" width=\"601\" height=\"61\" srcset=\"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2026\/02\/charts-flow-diagram-1-1.png 601w, https:\/\/streamhub.co.uk\/wp-content\/uploads\/2026\/02\/charts-flow-diagram-1-1-480x49.png 480w\" sizes=\"auto, (min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) 601px, 100vw\" \/><\/p>\n<div class=\"rich-media-item mediaSingleView-content-wrap image-center css-22y7n9\" data-layout=\"center\" data-width=\"601\" data-width-type=\"pixel\" data-node-type=\"mediaSingle\" data-vc=\"media-single\" data-renderer-start-pos=\"5338\" data-media-vc-wrapper=\"true\">\n<div class=\"css-1vybh4\">\n<div data-type=\"file\" data-node-type=\"media\" data-width=\"601\" data-height=\"61\" data-id=\"16d913ed-ad9d-4beb-8afb-cd958b2287f0\" data-collection=\"contentId-4632969234\" data-file-name=\"charts flow diagram.png\" data-file-size=\"11799\" data-file-mime-type=\"image\/png\" data-alt=\"charts flow diagram.png\" data-renderer-start-pos=\"5339\" data-local-id=\"56ef16b27bec\" data-context-id=\"4632969234\">\n<div id=\"newFileExperienceWrapper\" class=\"_2rko18qm _vchhusvi _kqswh2mm _ect4ttxp _p12f1osq _c71l1osq _1bsb1qmm _4t3ine4n _1hlmd0i9 _1rquusvi _eg541i5c _mts3kb7n _1ntskb7n _yfmhtlke _5sb1v00u new-file-experience-wrapper\" data-testid=\"media-card-view\" data-media-vc-wrapper=\"true\">\n<div class=\"_1reo15vq _18m915vq _2rko18qm _1e0c1txw _kqswh2mm _p12f1osq _1bsb1osq _4t3i1osq _c71l1osq media-file-card-view\" data-testid=\"media-file-card-view\" data-test-media-name=\"charts flow diagram.png\" data-test-status=\"complete\" data-cursor=\"pointer\" data-test-progress=\"1\" data-test-source=\"remote\">\n<div data-testid=\"ImageRendererWrapper\">\u00a0<\/div>\n<div class=\"_kqswstnw _1bsb1osq _4t3i1osq _1e0c1txw _2lx21bp4 _1bah1h6o _4cvr1h6o\" style=\"text-align: center;\" data-testid=\"ImageRendererWrapper\">Target Direction<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div data-media-caption=\"true\" data-testid=\"media-caption\" data-renderer-start-pos=\"5340\">\u00a0<\/div>\n<div data-media-caption=\"true\" data-testid=\"media-caption\" data-renderer-start-pos=\"5340\">\u00a0<\/div>\n<p data-renderer-start-pos=\"5359\" data-local-id=\"94f9a509fc25\">The target direction separates responsibilities into distinct layers, allowing each concern to evolve independently while reducing coupling and technical debt.<\/p>\n<hr role=\"presentation\" \/>\n<h2 data-local-id=\"8d1ae8373d40\" data-renderer-start-pos=\"5521\">\u00a0<\/h2>\n<h2 id=\"Design-Goals-and-Evaluation-Criteria\" data-local-id=\"8d1ae8373d40\" data-renderer-start-pos=\"5521\"><strong>Design Goals and Evaluation Criteria<\/strong><\/h2>\n<p data-renderer-start-pos=\"5559\" data-local-id=\"135811466296\">Before settling on an architectural approach, we defined a set of guiding criteria:<\/p>\n<ul class=\"ak-ul\" data-local-id=\"9902ce23-e24b-4f1f-b687-4e53d6ed264c\" data-indent-level=\"1\">\n<li>\n<p data-renderer-start-pos=\"5646\" data-local-id=\"a221c34ebe33\"><strong data-renderer-mark=\"true\">Extensibility<\/strong>: Supporting a new chart type should not require changing existing transformations<\/p>\n<\/li>\n<li>\n<p data-renderer-start-pos=\"5745\" data-local-id=\"4d2fd8a41218\"><strong data-renderer-mark=\"true\">Reusability<\/strong>: Common data shaping logic should be shareable across chart variants<\/p>\n<\/li>\n<li>\n<p data-renderer-start-pos=\"5829\" data-local-id=\"0fb027b05bb4\"><strong data-renderer-mark=\"true\">Library Agnosticism<\/strong>: Switching or adding charting libraries should not affect core transformation logic<\/p>\n<\/li>\n<li>\n<p data-renderer-start-pos=\"5936\" data-local-id=\"4c2fa8775172\"><strong data-renderer-mark=\"true\">Testability<\/strong>: Data transformation steps should be independently unit-testable<\/p>\n<\/li>\n<li>\n<p data-renderer-start-pos=\"6016\" data-local-id=\"fe9810565992\"><strong data-renderer-mark=\"true\">Predictability<\/strong>: Shared dashboard state should not cause uncontrolled recomputations<\/p>\n<\/li>\n<\/ul>\n<p data-renderer-start-pos=\"6103\" data-local-id=\"cde346b03548\">These constraints ruled out monolithic conditional logic and pushed us toward a more modular, pattern-oriented architecture.<\/p>\n<hr role=\"presentation\" \/>\n<h2 data-local-id=\"e47a4bcd432e\" data-renderer-start-pos=\"6230\">\u00a0<\/h2>\n<h2 id=\"Architectural-Direction-(High-Level)\" data-local-id=\"e47a4bcd432e\" data-renderer-start-pos=\"6230\"><strong>Architectural Direction (High-Level)<\/strong><\/h2>\n<p data-renderer-start-pos=\"6268\" data-local-id=\"fe2ab30b08a5\">Based on these goals, we converged on three guiding ideas:<\/p>\n<ol class=\"ak-ol\" start=\"1\" data-local-id=\"2c116497-24eb-438b-b4c7-dfca928ca67d\" data-indent-level=\"1\">\n<li data-renderer-start-pos=\"6268\" data-local-id=\"fe2ab30b08a5\">A strategy-driven transformation layer to encapsulate chart-specific data shaping<\/li>\n<li data-renderer-start-pos=\"6268\" data-local-id=\"fe2ab30b08a5\">A factory-based resolution mechanism to select appropriate transformations dynamically<\/li>\n<li data-renderer-start-pos=\"6268\" data-local-id=\"fe2ab30b08a5\">An adapter layer to isolate charting library dependencies from core logic<\/li>\n<\/ol>\n<p data-renderer-start-pos=\"6582\" data-local-id=\"fe1b3913064c\">This direction allows the system to support new chart types and libraries incrementally, without rewriting existing components.<\/p>\n<p data-renderer-start-pos=\"6711\" data-local-id=\"d728791455eb\">In next post, we will see how we implemented right transformation logic for each chart type.<\/p>\n<p data-renderer-start-pos=\"6711\" data-local-id=\"d728791455eb\">[\/et_pb_text][et_pb_team_member name=&#8221;Ravindra Soman&#8221; position=&#8221;Senior Full Stack Data Engineer&#8221; image_url=&#8221;https:\/\/streamhub.co.uk\/wp-content\/uploads\/2026\/02\/Ravindra-Soman-01-150&#215;150.jpg&#8221; linkedin_url=&#8221;https:\/\/www.linkedin.com\/in\/somanravindra\/&#8221; admin_label=&#8221;Person&#8221; _builder_version=&#8221;4.27.0&#8243; _module_preset=&#8221;default&#8221; link_option_url_new_window=&#8221;on&#8221; hover_enabled=&#8221;0&#8243; global_colors_info=&#8221;{}&#8221; title_text=&#8221;Ravindra Soman 01&#8243; sticky_enabled=&#8221;0&#8243;]<\/p>\n<p>Ravindra is a Senior Full Stack Data Engineer specialising in frontend architecture and complex data visualisation systems. In his spare time, he likes to cook new dishes for his family, read fiction and travel the world.<\/p>\n<p>[\/et_pb_team_member][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Reading Time: <\/span> <span class=\"rt-time\"> 4<\/span> <span class=\"rt-label rt-postfix\">minutes<\/span><\/span><\/p>\n<p>Problem Space &amp; Design Goals In modern web applications, data visualisation is a critical component that requires flexibility, maintainability, and extensibility. When building a charting system, supporting multiple chart types across heterogeneous rendering requirements quickly becomes an architectural challenge. In our case, the system needed to support 15+ chart types that users could dynamically add [&hellip;]<\/p>\n","protected":false},"author":21,"featured_media":34720,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"<h2 id=\"Part-1:-Problem-Space-&-Design-Goals\" data-local-id=\"0746e3b35c27\" data-renderer-start-pos=\"1\"><strong>Part 1: Problem Space & Design Goals<\/strong><\/h2><p>\u00a0<\/p><p data-renderer-start-pos=\"39\" data-local-id=\"a0496b3bbb21\">In modern web applications, data visualisation is a critical component that requires flexibility, maintainability, and extensibility. When building a charting system, supporting multiple chart types across heterogeneous rendering requirements quickly becomes an architectural challenge.<\/p><p data-renderer-start-pos=\"327\" data-local-id=\"ce527decc7bb\">In our case, the system needed to support 15+ chart types that users could dynamically add to dashboards. Each chart could be powered by different charting libraries, and although the API responses were consistent, each library expected the input data in its own specific format. What initially seemed like a UI concern soon revealed itself to be a deeper issue involving data transformation, extensibility, and long-term maintainability.<\/p><p data-renderer-start-pos=\"767\" data-local-id=\"fea892388c1a\">This post, focuses on <strong data-renderer-mark=\"true\">the challenges<\/strong> we faced and explains the architectural direction we chose. In the next posts we'll see how we addressed them with a small set of design patterns.<\/p><hr role=\"presentation\" \/><h2 data-local-id=\"8ed94c0766b4\" data-renderer-start-pos=\"953\">\u00a0<\/h2><h2 id=\"Technical-Context\" data-local-id=\"8ed94c0766b4\" data-renderer-start-pos=\"953\"><strong>Technical Context\u00a0<\/strong><\/h2><p data-renderer-start-pos=\"973\" data-local-id=\"dde48a3bfe6f\">Our <a class=\"_ymio1r31 _ypr0glyw _zcxs1o36 _mizu1v1w _1ah3dkaa _ra3xnqa1 _128mdkaa _1cvmnqa1 _4davt94y _4bfu1r31 _1hms8stv _ajmmnqa1 _vchhusvi _kqswh2mm _ect4ttxp _syaz13af _1a3b1r31 _4fpr8stv _5goinqa1 _f8pj13af _9oik1r31 _1bnxglyw _jf4cnqa1 _30l313af _1nrm1r31 _c2waglyw _1iohnqa1 _9h8h12zz _10531ra0 _1ien1ra0 _n0fx1ra0 _1vhv17z1\" title=\"https:\/\/streamhub.co.uk\/Analytics\/\" href=\"https:\/\/streamhub.co.uk\/Analytics\/\" data-renderer-mark=\"true\">Analytics product<\/a> is built using Angular as the primary frontend framework. For visualisation, we support multiple libraries depending on the widget type, including <span data-highlighted=\"true\" data-vc=\"highlighted-text\"><span class=\"_kqswh2mm\"><span class=\"_5pioz8co _189e1dm9 _1il9buyh _19lc184f _d0altlke\" data-testid=\"definition-highlighter\">AG<\/span><\/span><\/span> Grid for tabular data and Highcharts for basic chart-based visualisations, along with a few custom-rendered charts.<\/p><p data-renderer-start-pos=\"1262\" data-local-id=\"e376d8344e68\">While the API responses remain consistent, each library imposes its own expectations on how data should be structured. This divergence in data contracts across libraries became a central factor in shaping the overall architecture.<\/p><hr role=\"presentation\" \/><h2 data-local-id=\"641aa7d4df19\" data-renderer-start-pos=\"1495\">\u00a0<\/h2><h2 id=\"The-Core-Mismatch:-Same-Data,-Different-Library-Expectations\" data-local-id=\"641aa7d4df19\" data-renderer-start-pos=\"1495\"><strong>The Core Mismatch: Same Data, Different Library <\/strong><\/h2><h2 data-local-id=\"641aa7d4df19\" data-renderer-start-pos=\"1495\"><strong>Expectations<\/strong><\/h2><p data-renderer-start-pos=\"1557\" data-local-id=\"715b22dd9ff1\">One of the key challenges was that different visualisation components required the same dataset to be shaped differently.<\/p><p data-renderer-start-pos=\"1680\" data-local-id=\"9713e7bdbea9\">For example, consider a dataset fetched from a consistent API:<\/p><div class=\"fabric-editor-breakout-mark fabric-editor-block-mark css-p8f2xz\" data-mode=\"wide\" data-has-width=\"true\" data-width=\"760\"><div class=\"code-block css-y5zsxb\" data-local-id=\"4690b3e3ddf7\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"json\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-json\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token punctuation\">[<\/span><br \/>\r\n<\/span><\/code><\/span><\/div><div class=\"code-block css-y5zsxb\" data-local-id=\"4690b3e3ddf7\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"json\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\">\u00a0\u00a0<code class=\"language-json\"><span class=\"\" data-testid=\"renderer-code-block-line-2\" data-ds--code--row=\"\">  <span class=\"token punctuation\">{<\/span> <span class=\"token property\">\"device\"<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">\"android\"<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token property\">\"views\"<\/span><span class=\"token operator\">:<\/span> <span class=\"token number\">12<\/span><span class=\"token punctuation\">,<\/span><span class=\"token number\">000<\/span> <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span><\/span><\/code><\/span><\/div><div class=\"code-block css-y5zsxb\" data-local-id=\"4690b3e3ddf7\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"json\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\">\u00a0 <code class=\"language-json\"><span class=\"\" data-testid=\"renderer-code-block-line-2\" data-ds--code--row=\"\">\u00a0<\/span><span class=\"\" data-testid=\"renderer-code-block-line-3\" data-ds--code--row=\"\"><span class=\"token punctuation\">{<\/span> <span class=\"token property\">\"device\"<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">\"iphone\"<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token property\">\"views\"<\/span><span class=\"token operator\">:<\/span> <span class=\"token number\">15<\/span><span class=\"token punctuation\">,<\/span><span class=\"token number\">000<\/span> <span class=\"token punctuation\">}<\/span><br \/>\r\n<\/span><\/code><\/span><\/div><div class=\"code-block css-y5zsxb\" data-local-id=\"4690b3e3ddf7\"><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"json\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-json\"><span class=\"\" data-testid=\"renderer-code-block-line-4\" data-ds--code--row=\"\"><span class=\"token punctuation\">]<\/span><\/span><\/code><\/span><\/div><\/div><p data-renderer-start-pos=\"1835\" data-local-id=\"bc0fba4c4e06\">A data-grid widget powered by <span data-highlighted=\"true\" data-vc=\"highlighted-text\">ag<\/span>-Grid expects a flat row-based structure:<\/p><div class=\"fabric-editor-breakout-mark fabric-editor-block-mark css-p8f2xz\" data-mode=\"wide\" data-has-width=\"true\" data-width=\"760\"><div class=\"code-block css-y5zsxb\" data-local-id=\"f3551c1b7ff5\"><p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\">rowData <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-2\" data-ds--code--row=\"\"><span class=\"token punctuation\">\u00a0 \u00a0 {<\/span> <span class=\"token string-property property\">\"device\"<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">\"android\"<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string-property property\">\"views\"<\/span><span class=\"token operator\">:<\/span> <span class=\"token number\">12<\/span><span class=\"token punctuation\">,<\/span><span class=\"token number\">000<\/span> <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">,<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-3\" data-ds--code--row=\"\"><span class=\"token punctuation\">\u00a0 \u00a0 {<\/span> <span class=\"token string-property property\">\"device\"<\/span><span class=\"token operator\">:<\/span> <span class=\"token string\">\"iphone\"<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string-property property\">\"views\"<\/span><span class=\"token operator\">:<\/span> <span class=\"token number\">15<\/span><span class=\"token punctuation\">,<\/span><span class=\"token number\">000<\/span> <span class=\"token punctuation\">}<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-4\" data-ds--code--row=\"\"><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">;<\/span><\/span><\/code><\/span><\/p><\/div><\/div><p data-renderer-start-pos=\"2012\" data-local-id=\"e1b268a452f6\">However, a bar chart rendered using Highcharts expects a categorized structure:<\/p><div class=\"fabric-editor-breakout-mark fabric-editor-block-mark css-p8f2xz\" data-mode=\"wide\" data-has-width=\"true\" data-width=\"760\"><div class=\"code-block css-y5zsxb\" data-local-id=\"5356c64bd07d\"><p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\">series <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token punctuation\">{<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-2\" data-ds--code--row=\"\">\u00a0 \u00a0 name<span class=\"token operator\">:<\/span> <span class=\"token string\">\"views\"<\/span><span class=\"token punctuation\">,<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-3\" data-ds--code--row=\"\">\u00a0 \u00a0 data<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token number\">12000<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">15000<\/span><span class=\"token punctuation\">]<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-4\" data-ds--code--row=\"\"><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">;<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-6\" data-ds--code--row=\"\">categories <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">\"android\"<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token string\">\"iphone\"<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">;<\/span><\/span><\/code><\/span><\/p><\/div><\/div><p data-renderer-start-pos=\"2187\" data-local-id=\"141e1d86cb89\">The underlying API data remains consistent, but each library requires a distinct transformation. As the number of supported chart types and libraries grows, this transformation layer becomes a central architectural concern rather than a simple formatting step.<\/p><h2 data-local-id=\"bf94a3efa270\" data-renderer-start-pos=\"2449\">\u00a0<\/h2><h2 id=\"Why-Naive-Approaches-Break-Down\" data-local-id=\"bf94a3efa270\" data-renderer-start-pos=\"2449\"><strong>Why Naive Approaches Break Down<\/strong><\/h2><p data-renderer-start-pos=\"2482\" data-local-id=\"0b3afd866ccb\">A typical early implementation often resembles this:<\/p><div class=\"fabric-editor-breakout-mark fabric-editor-block-mark css-p8f2xz\" data-mode=\"wide\" data-has-width=\"true\" data-width=\"760\"><div class=\"code-block css-y5zsxb\" data-local-id=\"dd7478330d91\"><p><span class=\"prismjs _11c819w5 _2rko12b0 _1dqoglyw _1e0c1txw _vwz4gktf _1reo1wug _o572qvpr _1eimjvyg _bfhk187e _syazi7uo _1ozdn7od _7xinn7od _t7aun7od _r28du2gc _tajqu2gc _1ohiu2gc _m802u2gc _i6ntu2gc _1w2xu2gc _1hmyegat _vblregat _vbulegat _196q1xv3 _1vbw1xv3 _1v9c1xv3 _1srnt0uh _18r6myb0 _vyvc1n1a _1d4j1y44 _1f8gstnw _1pzyb3bt _ra6gsb9t _13cdh2mm _1pp0126e _zvy9f705 _qcxof705 _qzn01a66 _j0l11wug _1weckb7n _1na21hna _vsnzgrf3 _x7c815vq _lh0y15vq _1m3815vq _qk1e15vq _12l6ysn8 _uga3ysn8 _mx8b7mnp _1kr87mnp _xo19t94y _1bemt94y _nalpstnw _151dstnw _1exb1q9c _1hgu1q9c _1mgnt94y _nhket94y _h909i8nm _scgayz1z _ipl81e17 _40uk1l04 _i81p1a66 _1gx21e5h _1ls01ule _vm2c1rh5 _12ok1rh5 _rude1ule _1q16glyw _1io6glyw _juomusic _lcwuusic _pyovu2gc _ccm6u2gc _1ascu2gc _1yuau2gc _xr0w1a66 _4io21a66 _euyxusvi _cahfusvi _zhnuidpf _1amdidpf _mbgc124n _bu7z124n _131n1giz _gy101giz _1wfuwrk5 _16kzwrk5 _9kk3wu06 _cjus1w1g _9k2r1m30 _nhmw1m30 _yl021m30 _eihtd5cb _t9zbd5cb _mqok1w1g _3hsg1w1g _i7ngn7od _9wu1fb2s _1xcoh55r _1t36i7uo _137bh55r _1k7di7uo _97li16jw _12nh9lu1 _1g0517qg _i2igqmo9 _326zi7uo _113p1rpy _1n6t16jw _tgu817qg _1k4716jw _g0lxi7uo _ys4e1rpy _7gp8h55r _1yvqqmo9 _1vwwqmo9 _1rjuqmo9 _1v0lh55r _wmyy17qg _748n17qg _1mfn17qg _1d7e17qg _p2vr17qg _19o6qmo9 _kxov17qg _1np517qg _m2f517qg _1b9t16jw _1tq616jw _1rd216jw _1pbk16jw _k3li16jw _13zt1rpy _2g12fb2s _k86bqmo9 _b5iy1rpy _gti31rpy _1f0g16jw _9d3e17qg _qdia16jw _72uv16jw _13dgkb7n _1707efft _1i3h1txw _16noidpf _h4fuidpf _pp6yidpf _1g4tidpf _11wmidpf _1bx8idpf\" data-code-lang=\"typescript\" data-ds--code--code-block=\"\" data-testid=\"renderer-code-block\"><code class=\"language-typescript\"><span class=\"\" data-testid=\"renderer-code-block-line-1\" data-ds--code--row=\"\"><span class=\"token keyword control-flow\">switch<\/span> <span class=\"token punctuation\">(<\/span>chartType<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-2\" data-ds--code--row=\"\"><span class=\"token keyword\">\u00a0 \u00a0 case<\/span> <span class=\"token string\">'bar'<\/span><span class=\"token operator\">:<\/span> <span class=\"token keyword control-flow\">return<\/span> <span class=\"token function\">transformToHighchartsBar<\/span><span class=\"token punctuation\">(<\/span>data<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-3\" data-ds--code--row=\"\"><span class=\"token keyword\">\u00a0 \u00a0 case<\/span> <span class=\"token string\">'grid'<\/span><span class=\"token operator\">:<\/span> <span class=\"token keyword control-flow\">return<\/span> <span class=\"token function\">transformToAgGridRows<\/span><span class=\"token punctuation\">(<\/span>data<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-4\" data-ds--code--row=\"\"><span class=\"token keyword\">\u00a0 \u00a0 case<\/span> <span class=\"token string\">'line'<\/span><span class=\"token operator\">:<\/span> <span class=\"token keyword control-flow\">return<\/span> <span class=\"token function\">transformToHighchartsLine<\/span><span class=\"token punctuation\">(<\/span>data<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br \/>\r\n<\/span><span class=\"\" data-testid=\"renderer-code-block-line-5\" data-ds--code--row=\"\"><span class=\"token punctuation\">}<\/span><\/span><\/code><\/span><\/p><\/div><\/div><p data-renderer-start-pos=\"2719\" data-local-id=\"ce80bc5bd0ca\">In practice, this <code class=\"_ca0qyh40 _u5f3m5ip _n3tdyh40 _19bvm5ip _2rkofajl _11c819w5 _1reo1wug _18m91wug _1dqoglyw _1e0c1nu9 _bfhk187e _16d9qvcn _syazi7uo _vwz41kw7 _1i4q1hna _o5721jtm\" data-renderer-mark=\"true\">switch<\/code> statement and the corresponding transformation logic lived inside a single class responsible for orchestrating chart rendering. Over time, this class accumulated multiple responsibilities:<\/p><ul class=\"ak-ul\" data-local-id=\"4de539bc-860d-4140-a9ac-3f34ddff8c93\" data-indent-level=\"1\"><li><p data-renderer-start-pos=\"2936\" data-local-id=\"4872371ee564\">Deciding which chart type to render<\/p><\/li><li><p data-renderer-start-pos=\"2975\" data-local-id=\"9a1fc1693693\">Transforming raw API data<\/p><\/li><li><p data-renderer-start-pos=\"3004\" data-local-id=\"64d530a0127b\">Handling library-specific formatting<\/p><\/li><\/ul><p data-renderer-start-pos=\"3044\" data-local-id=\"b45a6f6e356f\">This violated the Single Responsibility Principle and tightly coupled transformation logic with rendering decisions. Every new chart type or library variation requires modifying existing logic, violating the Open\/Closed Principle and increasing the risk of regressions.<\/p><hr role=\"presentation\" \/><h2 data-local-id=\"d352e7a5ee7e\" data-renderer-start-pos=\"3316\">\u00a0<\/h2><h2 id=\"Core-Challenges\" data-local-id=\"d352e7a5ee7e\" data-renderer-start-pos=\"3316\"><strong>Core Challenges<\/strong><\/h2><p>\u00a0<\/p><h3 id=\"1.-Data-Transformation-Complexity\" data-local-id=\"2905b051e6a2\" data-renderer-start-pos=\"3333\"><strong>1. Data Transformation Complexity<\/strong><\/h3><p data-renderer-start-pos=\"3368\" data-local-id=\"c3ce0a0ae977\">Although the API responses are consistent, each visualisation library enforces its own structural contract. Supporting multiple libraries means handling:<\/p><ul class=\"ak-ul\" data-local-id=\"92f16474-289b-4eae-8ab1-9616e3dd1c05\" data-indent-level=\"1\"><li><p data-renderer-start-pos=\"3525\" data-local-id=\"138093883242\">Structural reshaping (row-based vs series-based vs hierarchical)<\/p><\/li><li><p data-renderer-start-pos=\"3593\" data-local-id=\"cb41cb68d508\">Aggregation or grouping logic specific to chart types<\/p><\/li><li><p data-renderer-start-pos=\"3650\" data-local-id=\"ca7eeb99f314\">Consistent normalization so that transformations remain predictable<\/p><\/li><\/ul><p data-renderer-start-pos=\"3721\" data-local-id=\"cdef8de81a7e\">The transformation layer must therefore be reusable and decoupled from the rendering library\u2019s internal expectations.<\/p><hr role=\"presentation\" \/><h3 data-local-id=\"cc001818e7fe\" data-renderer-start-pos=\"3841\">\u00a0<\/h3><h3 id=\"2.-Extensibility-and-Maintainability\" data-local-id=\"cc001818e7fe\" data-renderer-start-pos=\"3841\"><strong>2. Extensibility and Maintainability<\/strong><\/h3><p data-renderer-start-pos=\"3879\" data-local-id=\"0295c5085b10\">As new chart types or visualisation libraries are introduced, the system should extend without requiring modifications to existing transformation logic. Without a modular design, each addition risks breaking unrelated visualisations.<\/p><p data-renderer-start-pos=\"4114\" data-local-id=\"f52722068cb8\">Extensibility is therefore not just desirable\u2014it is essential for long-term maintainability.<\/p><hr role=\"presentation\" \/><h3 data-local-id=\"05234803ccff\" data-renderer-start-pos=\"4209\">\u00a0<\/h3><h3 id=\"3.-Shared-State-Interactions\" data-local-id=\"05234803ccff\" data-renderer-start-pos=\"4209\"><strong>3. Shared State Interactions<\/strong><\/h3><p data-renderer-start-pos=\"4239\" data-local-id=\"1488c114d5cd\">Charts rarely operate in isolation. They react to shared dashboard inputs such as filters, metrics, and time ranges. Without clear architectural boundaries, state changes can trigger cascading re-computations that are difficult to trace and debug.<\/p><p data-renderer-start-pos=\"4488\" data-local-id=\"7e2910a7a112\">A scalable solution must ensure that state orchestration does not leak into transformation logic.<\/p><hr role=\"presentation\" \/><h3 data-local-id=\"d53d8a739b72\" data-renderer-start-pos=\"4588\">\u00a0<\/h3><h3 id=\"4.-Separation-of-Concerns\" data-local-id=\"d53d8a739b72\" data-renderer-start-pos=\"4588\"><strong>4. Separation of Concerns<\/strong><\/h3><p data-renderer-start-pos=\"4615\" data-local-id=\"51329b5603b3\">These challenges highlighted the need for a clear separation of responsibilities across layers. Mixing decision-making, data shaping, and rendering logic within a single class created tight coupling and reduced clarity about where specific responsibilities belonged.<\/p><p data-renderer-start-pos=\"4883\" data-local-id=\"e8879fbb49c1\">A more layered approach was necessary to ensure each concern could evolve independently.<\/p><hr role=\"presentation\" \/><h2 data-local-id=\"e9cd6e0bf7ac\" data-renderer-start-pos=\"4974\">\u00a0<\/h2><h2 id=\"From-Naive-to-Scalable:-Direction-of-Evolution\" data-local-id=\"e9cd6e0bf7ac\" data-renderer-start-pos=\"4974\"><strong>From Naive to Scalable: Direction of Evolution<\/strong><\/h2><p style=\"text-align: left;\" data-renderer-start-pos=\"5022\" data-local-id=\"bb419175377e\">These challenges made it clear that the existing structure could not scale. The architectural evolution looked like this:<span style=\"text-align: center;\">\u00a0 <\/span><span style=\"text-align: center;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<\/span><\/p><p data-renderer-start-pos=\"5022\" data-local-id=\"bb419175377e\"><img class=\"wp-image-35273 size-full alignleft\" src=\"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2026\/02\/charts-naive-approach-2.png\" alt=\"\" width=\"551\" height=\"111\" \/><\/p><p style=\"text-align: left;\" data-renderer-start-pos=\"5022\" data-local-id=\"bb419175377e\"><span style=\"text-align: center;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<\/span><\/p><p style=\"text-align: left;\" data-renderer-start-pos=\"5022\" data-local-id=\"bb419175377e\"><span style=\"text-align: center;\"> \u00a0 <\/span><\/p><p style=\"text-align: left;\" data-renderer-start-pos=\"5022\" data-local-id=\"bb419175377e\"><span style=\"text-align: center;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0Naive Approach<\/span><\/p><p data-renderer-start-pos=\"5164\" data-local-id=\"68207e2bf0c3\">In the naive approach, both decision-making and data shaping were embedded in a single class. This made the system rigid and prone to errors as new chart types were introduced.<\/p><p data-renderer-start-pos=\"5164\" data-local-id=\"68207e2bf0c3\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<img class=\"wp-image-35270 size-full alignleft\" src=\"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2026\/02\/charts-flow-diagram-1-1.png\" alt=\"\" width=\"601\" height=\"61\" \/><\/p><div class=\"rich-media-item mediaSingleView-content-wrap image-center css-22y7n9\" data-layout=\"center\" data-width=\"601\" data-width-type=\"pixel\" data-node-type=\"mediaSingle\" data-vc=\"media-single\" data-renderer-start-pos=\"5338\" data-media-vc-wrapper=\"true\"><div class=\"css-1vybh4\"><div data-type=\"file\" data-node-type=\"media\" data-width=\"601\" data-height=\"61\" data-id=\"16d913ed-ad9d-4beb-8afb-cd958b2287f0\" data-collection=\"contentId-4632969234\" data-file-name=\"charts flow diagram.png\" data-file-size=\"11799\" data-file-mime-type=\"image\/png\" data-alt=\"charts flow diagram.png\" data-renderer-start-pos=\"5339\" data-local-id=\"56ef16b27bec\" data-context-id=\"4632969234\"><div id=\"newFileExperienceWrapper\" class=\"_2rko18qm _vchhusvi _kqswh2mm _ect4ttxp _p12f1osq _c71l1osq _1bsb1qmm _4t3ine4n _1hlmd0i9 _1rquusvi _eg541i5c _mts3kb7n _1ntskb7n _yfmhtlke _5sb1v00u new-file-experience-wrapper\" data-testid=\"media-card-view\" data-media-vc-wrapper=\"true\"><div class=\"_1reo15vq _18m915vq _2rko18qm _1e0c1txw _kqswh2mm _p12f1osq _1bsb1osq _4t3i1osq _c71l1osq media-file-card-view\" data-testid=\"media-file-card-view\" data-test-media-name=\"charts flow diagram.png\" data-test-status=\"complete\" data-cursor=\"pointer\" data-test-progress=\"1\" data-test-source=\"remote\"><div class=\"_kqswstnw _1bsb1osq _4t3i1osq _1e0c1txw _2lx21bp4 _1bah1h6o _4cvr1h6o\" data-testid=\"ImageRendererWrapper\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<\/div><div data-testid=\"ImageRendererWrapper\">\u00a0<\/div><div class=\"_kqswstnw _1bsb1osq _4t3i1osq _1e0c1txw _2lx21bp4 _1bah1h6o _4cvr1h6o\" data-testid=\"ImageRendererWrapper\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0Target Direction<\/div><\/div><\/div><\/div><\/div><\/div><div data-media-caption=\"true\" data-testid=\"media-caption\" data-renderer-start-pos=\"5340\">\u00a0<\/div><div data-media-caption=\"true\" data-testid=\"media-caption\" data-renderer-start-pos=\"5340\">\u00a0<\/div><p data-renderer-start-pos=\"5359\" data-local-id=\"94f9a509fc25\">The target direction separates responsibilities into distinct layers, allowing each concern to evolve independently while reducing coupling and technical debt.<\/p><hr role=\"presentation\" \/><h2 data-local-id=\"8d1ae8373d40\" data-renderer-start-pos=\"5521\">\u00a0<\/h2><h2 id=\"Design-Goals-and-Evaluation-Criteria\" data-local-id=\"8d1ae8373d40\" data-renderer-start-pos=\"5521\"><strong>Design Goals and Evaluation Criteria<\/strong><\/h2><p data-renderer-start-pos=\"5559\" data-local-id=\"135811466296\">Before settling on an architectural approach, we defined a set of guiding criteria:<\/p><ul class=\"ak-ul\" data-local-id=\"9902ce23-e24b-4f1f-b687-4e53d6ed264c\" data-indent-level=\"1\"><li><p data-renderer-start-pos=\"5646\" data-local-id=\"a221c34ebe33\"><strong data-renderer-mark=\"true\">Extensibility<\/strong>: Supporting a new chart type should not require changing existing transformations<\/p><\/li><li><p data-renderer-start-pos=\"5745\" data-local-id=\"4d2fd8a41218\"><strong data-renderer-mark=\"true\">Reusability<\/strong>: Common data shaping logic should be shareable across chart variants<\/p><\/li><li><p data-renderer-start-pos=\"5829\" data-local-id=\"0fb027b05bb4\"><strong data-renderer-mark=\"true\">Library Agnosticism<\/strong>: Switching or adding charting libraries should not affect core transformation logic<\/p><\/li><li><p data-renderer-start-pos=\"5936\" data-local-id=\"4c2fa8775172\"><strong data-renderer-mark=\"true\">Testability<\/strong>: Data transformation steps should be independently unit-testable<\/p><\/li><li><p data-renderer-start-pos=\"6016\" data-local-id=\"fe9810565992\"><strong data-renderer-mark=\"true\">Predictability<\/strong>: Shared dashboard state should not cause uncontrolled recomputations<\/p><\/li><\/ul><p data-renderer-start-pos=\"6103\" data-local-id=\"cde346b03548\">These constraints ruled out monolithic conditional logic and pushed us toward a more modular, pattern-oriented architecture.<\/p><hr role=\"presentation\" \/><h2 data-local-id=\"e47a4bcd432e\" data-renderer-start-pos=\"6230\">\u00a0<\/h2><h2 id=\"Architectural-Direction-(High-Level)\" data-local-id=\"e47a4bcd432e\" data-renderer-start-pos=\"6230\"><strong>Architectural Direction (High-Level)<\/strong><\/h2><p data-renderer-start-pos=\"6268\" data-local-id=\"fe2ab30b08a5\">Based on these goals, we converged on three guiding ideas:<\/p><ol class=\"ak-ol\" start=\"1\" data-local-id=\"2c116497-24eb-438b-b4c7-dfca928ca67d\" data-indent-level=\"1\"><li><p data-renderer-start-pos=\"6330\" data-local-id=\"f2ea1a079b4e\">A strategy-driven transformation layer to encapsulate chart-specific data shaping<\/p><\/li><li><p data-renderer-start-pos=\"6415\" data-local-id=\"8bb400e2cc63\">A factory-based resolution mechanism to select appropriate transformations dynamically<\/p><\/li><li><p data-renderer-start-pos=\"6505\" data-local-id=\"a87afcfda1a3\">An adapter layer to isolate charting library dependencies from core logic<\/p><\/li><\/ol><p data-renderer-start-pos=\"6582\" data-local-id=\"fe1b3913064c\">This direction allows the system to support new chart types and libraries incrementally, without rewriting existing components.<\/p><p data-renderer-start-pos=\"6711\" data-local-id=\"d728791455eb\">In next post, we will see how we implemented right transformation logic for each chart type.<\/p><p data-renderer-start-pos=\"6711\" data-local-id=\"d728791455eb\">[author] [author_image timthumb='on']https:\/\/streamhub.co.uk\/wp-content\/uploads\/2023\/07\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-07-27-16.02.20.png[\/author_image] [author_info]Content goes here[\/author_info] [\/author]<\/p>","_et_gb_content_width":"","content-type":"","_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[11,13,75],"tags":[],"class_list":["post-35241","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blogs","category-news","category-tech"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Architecting Scalable Chart Modules in Streamhub Analytics: Part I - Streamhub.co.uk<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Architecting Scalable Chart Modules in Streamhub Analytics: Part I - Streamhub.co.uk\" \/>\n<meta property=\"og:description\" content=\"Reading Time:  4 minutesProblem Space &amp; Design Goals In modern web applications, data visualisation is a critical component that requires flexibility, maintainability, and extensibility. When building a charting system, supporting multiple chart types across heterogeneous rendering requirements quickly becomes an architectural challenge. In our case, the system needed to support 15+ chart types that users could dynamically add [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/\" \/>\n<meta property=\"og:site_name\" content=\"Streamhub.co.uk\" \/>\n<meta property=\"article:published_time\" content=\"2026-02-16T17:17:35+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-02-17T16:36:31+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2023\/12\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2336\" \/>\n\t<meta property=\"og:image:height\" content=\"1512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Ravindra Soman\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ravindra Soman\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/\"},\"author\":{\"name\":\"Ravindra Soman\",\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/#\\\/schema\\\/person\\\/337c25120a9db280c15bf34b2faec85a\"},\"headline\":\"Architecting Scalable Chart Modules in Streamhub Analytics: Part I\",\"datePublished\":\"2026-02-16T17:17:35+00:00\",\"dateModified\":\"2026-02-17T16:36:31+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/\"},\"wordCount\":1075,\"publisher\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/streamhub.co.uk\\\/wp-content\\\/uploads\\\/2023\\\/12\\\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png\",\"articleSection\":[\"Blogs\",\"News\",\"Tech\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/\",\"url\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/\",\"name\":\"Architecting Scalable Chart Modules in Streamhub Analytics: Part I - Streamhub.co.uk\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/streamhub.co.uk\\\/wp-content\\\/uploads\\\/2023\\\/12\\\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png\",\"datePublished\":\"2026-02-16T17:17:35+00:00\",\"dateModified\":\"2026-02-17T16:36:31+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/#primaryimage\",\"url\":\"https:\\\/\\\/streamhub.co.uk\\\/wp-content\\\/uploads\\\/2023\\\/12\\\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png\",\"contentUrl\":\"https:\\\/\\\/streamhub.co.uk\\\/wp-content\\\/uploads\\\/2023\\\/12\\\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png\",\"width\":2336,\"height\":1512},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/architecting-scalable-chart-modules-in-streamhub-analytics\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/streamhub.co.uk\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Architecting Scalable Chart Modules in Streamhub Analytics: Part I\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/#website\",\"url\":\"https:\\\/\\\/streamhub.co.uk\\\/\",\"name\":\"Streamhub.co.uk\",\"description\":\"Streamhub.co.uk\",\"publisher\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/streamhub.co.uk\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/#organization\",\"name\":\"Streamhub.co.uk\",\"url\":\"https:\\\/\\\/streamhub.co.uk\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/streamhub.co.uk\\\/wp-content\\\/uploads\\\/2020\\\/05\\\/SH-Logo.png\",\"contentUrl\":\"https:\\\/\\\/streamhub.co.uk\\\/wp-content\\\/uploads\\\/2020\\\/05\\\/SH-Logo.png\",\"width\":1397,\"height\":361,\"caption\":\"Streamhub.co.uk\"},\"image\":{\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.linkedin.com\\\/company\\\/3006156\\\/admin\\\/feed\\\/posts\\\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/streamhub.co.uk\\\/#\\\/schema\\\/person\\\/337c25120a9db280c15bf34b2faec85a\",\"name\":\"Ravindra Soman\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a071c552a8b71da6cc43160899d11d9ebd32c357f7b566d4df2179d088c43704?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a071c552a8b71da6cc43160899d11d9ebd32c357f7b566d4df2179d088c43704?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a071c552a8b71da6cc43160899d11d9ebd32c357f7b566d4df2179d088c43704?s=96&d=mm&r=g\",\"caption\":\"Ravindra Soman\"},\"description\":\"Senior Full Stack Engineer specialising in frontend architecture and complex data visualisation systems. He currently works on architecting visualisation solutions at Streamhub, focusing on modular frontend design.\",\"sameAs\":[\"https:\\\/\\\/www.linkedin.com\\\/in\\\/somanravindra\\\/\"],\"url\":false}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Architecting Scalable Chart Modules in Streamhub Analytics: Part I - Streamhub.co.uk","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/","og_locale":"en_US","og_type":"article","og_title":"Architecting Scalable Chart Modules in Streamhub Analytics: Part I - Streamhub.co.uk","og_description":"Reading Time:  4 minutesProblem Space &amp; Design Goals In modern web applications, data visualisation is a critical component that requires flexibility, maintainability, and extensibility. When building a charting system, supporting multiple chart types across heterogeneous rendering requirements quickly becomes an architectural challenge. In our case, the system needed to support 15+ chart types that users could dynamically add [&hellip;]","og_url":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/","og_site_name":"Streamhub.co.uk","article_published_time":"2026-02-16T17:17:35+00:00","article_modified_time":"2026-02-17T16:36:31+00:00","og_image":[{"width":2336,"height":1512,"url":"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2023\/12\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png","type":"image\/png"}],"author":"Ravindra Soman","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Ravindra Soman","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/#article","isPartOf":{"@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/"},"author":{"name":"Ravindra Soman","@id":"https:\/\/streamhub.co.uk\/#\/schema\/person\/337c25120a9db280c15bf34b2faec85a"},"headline":"Architecting Scalable Chart Modules in Streamhub Analytics: Part I","datePublished":"2026-02-16T17:17:35+00:00","dateModified":"2026-02-17T16:36:31+00:00","mainEntityOfPage":{"@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/"},"wordCount":1075,"publisher":{"@id":"https:\/\/streamhub.co.uk\/#organization"},"image":{"@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/#primaryimage"},"thumbnailUrl":"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2023\/12\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png","articleSection":["Blogs","News","Tech"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/","url":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/","name":"Architecting Scalable Chart Modules in Streamhub Analytics: Part I - Streamhub.co.uk","isPartOf":{"@id":"https:\/\/streamhub.co.uk\/#website"},"primaryImageOfPage":{"@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/#primaryimage"},"image":{"@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/#primaryimage"},"thumbnailUrl":"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2023\/12\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png","datePublished":"2026-02-16T17:17:35+00:00","dateModified":"2026-02-17T16:36:31+00:00","breadcrumb":{"@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/#primaryimage","url":"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2023\/12\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png","contentUrl":"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2023\/12\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png","width":2336,"height":1512},{"@type":"BreadcrumbList","@id":"https:\/\/streamhub.co.uk\/architecting-scalable-chart-modules-in-streamhub-analytics\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/streamhub.co.uk\/"},{"@type":"ListItem","position":2,"name":"Architecting Scalable Chart Modules in Streamhub Analytics: Part I"}]},{"@type":"WebSite","@id":"https:\/\/streamhub.co.uk\/#website","url":"https:\/\/streamhub.co.uk\/","name":"Streamhub.co.uk","description":"Streamhub.co.uk","publisher":{"@id":"https:\/\/streamhub.co.uk\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/streamhub.co.uk\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/streamhub.co.uk\/#organization","name":"Streamhub.co.uk","url":"https:\/\/streamhub.co.uk\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/streamhub.co.uk\/#\/schema\/logo\/image\/","url":"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2020\/05\/SH-Logo.png","contentUrl":"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2020\/05\/SH-Logo.png","width":1397,"height":361,"caption":"Streamhub.co.uk"},"image":{"@id":"https:\/\/streamhub.co.uk\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.linkedin.com\/company\/3006156\/admin\/feed\/posts\/"]},{"@type":"Person","@id":"https:\/\/streamhub.co.uk\/#\/schema\/person\/337c25120a9db280c15bf34b2faec85a","name":"Ravindra Soman","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/a071c552a8b71da6cc43160899d11d9ebd32c357f7b566d4df2179d088c43704?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/a071c552a8b71da6cc43160899d11d9ebd32c357f7b566d4df2179d088c43704?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a071c552a8b71da6cc43160899d11d9ebd32c357f7b566d4df2179d088c43704?s=96&d=mm&r=g","caption":"Ravindra Soman"},"description":"Senior Full Stack Engineer specialising in frontend architecture and complex data visualisation systems. He currently works on architecting visualisation solutions at Streamhub, focusing on modular frontend design.","sameAs":["https:\/\/www.linkedin.com\/in\/somanravindra\/"],"url":false}]}},"views":36,"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/streamhub.co.uk\/wp-content\/uploads\/2023\/12\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2023-12-07-15.40.23.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/posts\/35241","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/comments?post=35241"}],"version-history":[{"count":43,"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/posts\/35241\/revisions"}],"predecessor-version":[{"id":35346,"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/posts\/35241\/revisions\/35346"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/media\/34720"}],"wp:attachment":[{"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/media?parent=35241"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/categories?post=35241"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/streamhub.co.uk\/wp-json\/wp\/v2\/tags?post=35241"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}