<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Vishal Vijay</title>
    <description>The latest articles on Forem by Vishal Vijay (@vishalvijay).</description>
    <link>https://forem.com/vishalvijay</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F279212%2F24c54d7c-b558-4a06-abf7-3b51cae4d0e6.jpeg</url>
      <title>Forem: Vishal Vijay</title>
      <link>https://forem.com/vishalvijay</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vishalvijay"/>
    <language>en</language>
    <item>
      <title>DataRepoArch - The path we traveled to reach react-query</title>
      <dc:creator>Vishal Vijay</dc:creator>
      <pubDate>Sun, 27 Sep 2020 17:01:24 +0000</pubDate>
      <link>https://forem.com/vishalvijay/datarepoarch-the-path-we-traveled-to-reach-react-query-l9l</link>
      <guid>https://forem.com/vishalvijay/datarepoarch-the-path-we-traveled-to-reach-react-query-l9l</guid>
      <description>&lt;p&gt;For the last few months, I was busy working with &lt;a href="https://www.kappitaan.com/"&gt;Kappitaan.com&lt;/a&gt;. And I was primarily responsible for building their website. And at the core, we used &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;, &lt;a href="https://redux.js.org/"&gt;Redux&lt;/a&gt;, a custom design system based on &lt;a href="https://material-ui.com/"&gt;MaterialUI&lt;/a&gt; and &lt;a href="https://www.typescriptlang.org/"&gt;Typescript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I'm going to cover how and why we invented &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc"&gt;DataRepoArch&lt;/a&gt; and later found a better and mature opensource alternative for the same.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/vishalvijay/data-repo-arch-poc"&gt;DataRepoArch&lt;/a&gt; is a client-side architecture designed to use with &lt;a href="https://reactjs.org/"&gt;react&lt;/a&gt; for better server state handling. This architecture also guarantees a very neat, highly pluggable, and maintainable codebase.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.kappitaan.com/"&gt;Kappitaan.com&lt;/a&gt; help empower students and healthcare professionals by making it easier to train and get placement for work abroad.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  So the story starts here
&lt;/h3&gt;

&lt;p&gt;Though in the initial phase we didn't have many global state management requirements, we still integrated redux to our framework (without compromising code-splitting benefits).  We were sure that we have a lot of server state handling coming for the dashboard part of the website.&lt;/p&gt;

&lt;p&gt;Before we continue, please check the sample screenshots of the dashboard to get an understanding. &lt;a href="https://raw.githubusercontent.com/vishalvijay/data-repo-arch-poc/master/sample/sample-1.png"&gt;Sample 1&lt;/a&gt;, &lt;a href="https://raw.githubusercontent.com/vishalvijay/data-repo-arch-poc/master/sample/sample-2.png"&gt;Sample 2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the help of screenshots, we can understand the below points and requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each page of the dashboard is consistent with multiple data cards.&lt;/li&gt;
&lt;li&gt;Some of the cards require data from the same source.&lt;/li&gt;
&lt;li&gt;Every page requires common dashboard related metadata to start loading.&lt;/li&gt;
&lt;li&gt;Data can get modified from anywhere on the dashboard.&lt;/li&gt;
&lt;li&gt;There are pages that require the same data which we fetched on other pages.&lt;/li&gt;
&lt;li&gt;A single data card might require data from multiple sources.&lt;/li&gt;
&lt;li&gt;If one of the API fails, users don't have to click retry on every card which uses the same data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So to accomplish these requirements, we started the development plan with the traditional redux approach. This means, page component handles the fetching of the data and distributes it across the data cards based on its data requirements.&lt;/p&gt;

&lt;p&gt;Listing the problems which we identified with this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The page component needs to know what all data cards are there on the page and its data requirements.&lt;/li&gt;
&lt;li&gt;Everything has to be routed through the page component as it handles the data fetching and caching using redux.&lt;/li&gt;
&lt;li&gt;API error handling and retry also became challenging.&lt;/li&gt;
&lt;li&gt;Lot of boilerplate code to handle data fetching.&lt;/li&gt;
&lt;li&gt;On page navigation, it triggers multiple API calls if the previous API hasn't finished loading for the same data.&lt;/li&gt;
&lt;li&gt;Data components are unable to abstract the business logic and it became really difficult to reuse the components.&lt;/li&gt;
&lt;li&gt;So many props to be passed in each data card to handle different states.&lt;/li&gt;
&lt;li&gt;Mutating the data also leads to a lot of boilerplates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We understood that, if we take this approach, it is going to make our code difficult to maintain and adds a lot of boilerplates. Eventually, every feature additions are going to consume time in both developing and testing.&lt;/p&gt;

&lt;p&gt;So, we decided to come with different approaches. Another suggestion was to move data loading inside a specific data card component. And that will help us solve many of the above problems mentioned such as data card can be reused, need not depend on parent or page component, etc.&lt;/p&gt;

&lt;p&gt;But it still doesn't solve the problem of duplicate API calls, ease of development, and boilerplate code.&lt;/p&gt;

&lt;p&gt;But we all agreed that the above is the way forward but we need a better framework in place. So we did our R&amp;amp;D and found a few libraries and architecture like react-query. When we initially looked at react-query we thought that this is another data fetching library similar to fetch. The fact is, we didn't do deep dive into the details of it.&lt;/p&gt;

&lt;p&gt;So after the R&amp;amp;D, we didn't identify something which suits our requirements. So we decided to innovate ourselves.&lt;/p&gt;

&lt;h3&gt;
  
  
  DataRepoArch begins here
&lt;/h3&gt;

&lt;p&gt;As we already have a redux system integrated, we decided to build something which is pluggable to it. And we started listing down the problems which we want to solve and the features we want to support.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It must allow using any data fetching logic. Can be from a remote source, local data, fake data, or even from already downloaded data.&lt;/li&gt;
&lt;li&gt;All data should be by default in-memory cached and should get re-fetched if intentionally triggered (usually after mutation).&lt;/li&gt;
&lt;li&gt;It should not allow duplicate API calls if different data cards make the same request when already one is being fetched.&lt;/li&gt;
&lt;li&gt;It should easily allow handling data fetching states.&lt;/li&gt;
&lt;li&gt;It should allow fetching and handle multiple data together for the same data card.&lt;/li&gt;
&lt;li&gt;It should allow retrying API call if the previous one failed. And on retry, it should refresh all the data cards depended on the same data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  So we started architecting it
&lt;/h4&gt;

&lt;p&gt;A working POC application can be found &lt;a href="https://data-repo-arch-poc.vercel.app/"&gt;here&lt;/a&gt;. Randomly try navigating and refreshing the pages and experience the features like data fetching, caching, retry, parallel fetching, etc.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We are throwing errors randomly in the data fetching logic, to make it easier to test retry behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before we talk about the internal details of the architecture, let's see how a developer can use this architecture.&lt;/p&gt;

&lt;p&gt;So let's look at &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/features/Page1/index.tsx"&gt;Page 1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Page1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;...&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserDetails&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;...&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserDetails&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;...&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="p"&gt;...&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="p"&gt;...&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="p"&gt;...&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserAndProduct&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Page 1 is consists of 3 unique data cards. In it, we are showing user details card 2 times, and 3 different product card. Also an example of fetching user and product details in a single card. Looking at the above sample you will understand, how pluggable are each card and all its business logic have been abstracted in itself.&lt;/p&gt;

&lt;h4&gt;
  
  
  Let's look at the code of all 3 unique data cards here
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Before we continue, a quick note about &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/features/DataLoadingHandler.tsx"&gt;DataLoadingHandler&lt;/a&gt;. It is a simple component that takes care of showing loader and API error based on the fetching state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/features/DataCards/UserDetails.tsx"&gt;UserDetails&lt;/a&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserDetails&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userDetailsRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useUserDetailsRepo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Paper&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Box&lt;/span&gt; &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataLoadingHandler&lt;/span&gt;
          &lt;span class="na"&gt;successCode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;userDetailsRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;successCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Failed to load user details"&lt;/span&gt;
          &lt;span class="na"&gt;onRetry&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;userDetailsRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refresh&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Element&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;gutterBottom&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                Name: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;userDetailsRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;gutterBottom&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                Email: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;userDetailsRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;gutterBottom&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                Total votes: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;userDetailsRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;totalVotes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Paper&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The DataRepo API is as simple as this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the custom data repo hook.&lt;/li&gt;
&lt;li&gt;Pass &lt;code&gt;successCode&lt;/code&gt; to &lt;code&gt;DataLoadingHandler&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use the data the way you want in the render method.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/features/DataCards/Product.tsx"&gt;Product&lt;/a&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useProductRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;productRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Product&lt;/code&gt; card implementation is also similar to &lt;code&gt;UserDetails&lt;/code&gt; the only difference is, we are passing &lt;code&gt;productId&lt;/code&gt; to &lt;code&gt;useProductRepo&lt;/code&gt; hook to fetch independent product details.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/features/DataCards/UserAndProduct.tsx"&gt;UserAndProduct&lt;/a&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserAndProduct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userDetailsRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useUserDetailsRepo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useProductRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aggregatedRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAggregatedRepo&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;userDetailsRepo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productRepo&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Paper&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Box&lt;/span&gt; &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataLoadingHandler&lt;/span&gt;
          &lt;span class="na"&gt;successCode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;aggregatedRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;successCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="err"&gt;);&lt;/span&gt;
    &lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;UserAndProduct&lt;/code&gt; example demonstrates the use case of &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/dataRepo/hooks/useAggregatedRepo.ts"&gt;useAggregatedRepo&lt;/a&gt;. This helps to aggregate multiple custom data repo hooks fetching state to a single state for better loading and API error handling.&lt;/p&gt;

&lt;h4&gt;
  
  
  Now let's look into custom data repo hooks
&lt;/h4&gt;

&lt;p&gt;There are 2 data repo hooks we wrote for this POC&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/data/useUserDetailsRepo.ts"&gt;useUserDetailsRepo&lt;/a&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchUserDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;synchronisedPromiseMaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserDetails&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// eslint-disable-next-line no-console&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetching user details...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to load user details&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hi@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sample name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;totalVotes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useUserDetailsRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserDetails&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;useRepo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserDetails&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userDetails&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchUserDetails&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/dataRepo/utils/synchronisedPromiseMaker.ts"&gt;synchronisedPromiseMaker&lt;/a&gt; is a utility created to prevent duplicate API calls at the same time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key to DataRepoArch is &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/dataRepo/hooks/useRepo.ts"&gt;useRepo&lt;/a&gt;, this is where all the magic happens. You simply have to provide a repo name, a synchronized promise (which takes care of data fetching the way the developer wants), and the rest of the arguments will be forwarded to the data fetching method.&lt;/p&gt;

&lt;p&gt;That's it, the data repo is ready.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/data/useProductRepo.ts"&gt;useProductRepo&lt;/a&gt;
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;synchronisedPromiseMaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// eslint-disable-next-line no-console&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetching product...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to load product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Sample product &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;quantityAvailable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sample category&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useProductRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;useRepo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchProduct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useProductRepo&lt;/code&gt; is also similar to &lt;code&gt;userDetailsRepo&lt;/code&gt; but it accepts &lt;code&gt;productId&lt;/code&gt; as an argument.&lt;/p&gt;

&lt;p&gt;That's all... developer need not worry about anything else. We solved all the problems we discussed above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now let's look at the core components of DataRepoArch
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://redux.js.org/"&gt;Redux&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The current POC implementation is an addon for Redux. We did it because it can be easily plugged into our current project. But this redux dependency for &lt;code&gt;DataRepoArch&lt;/code&gt; was unnecessary. We had only a little time to implement this all architecture. So for this version, we didn't want to reinvent the wheel and we want to take the advantage of the already existing architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/dataRepo/hooks/useRepo.ts"&gt;useRepo&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is the core of &lt;code&gt;DataRepoArch&lt;/code&gt;. It abstracts: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redux communication&lt;/li&gt;
&lt;li&gt;Promise execution&lt;/li&gt;
&lt;li&gt;Loading state management&lt;/li&gt;
&lt;li&gt;Data caching based on arguments&lt;/li&gt;
&lt;li&gt;Reset data functionality&lt;/li&gt;
&lt;li&gt;Refresh data functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Using the &lt;code&gt;reset&lt;/code&gt; or &lt;code&gt;refresh&lt;/code&gt; method you can ask to clear or refresh the data which is already cached in redux. This will come handy when you want to mutate any dependent data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc/blob/master/src/dataRepo/hooks/useAggregatedRepo.ts"&gt;useAggregatedRepo&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This hook will help to handle multiple data dependency loading. You can avoid a lot of duplicate code in your component.&lt;/p&gt;

&lt;p&gt;Yes... that's all about &lt;code&gt;DataRepoArch&lt;/code&gt;. It's as simple as that.&lt;/p&gt;

&lt;h2&gt;
  
  
  But we think &lt;a href="https://github.com/tannerlinsley/react-query"&gt;react-query&lt;/a&gt; is much better
&lt;/h2&gt;

&lt;p&gt;Our current &lt;a href="https://www.kappitaan.com/"&gt;Kappitaan.com&lt;/a&gt; website uses &lt;code&gt;DataRepoArch&lt;/code&gt; in production and we are really happy with the current product deployed. But we know that there is a lot of opportunities for improvements.&lt;/p&gt;

&lt;p&gt;When we initially looked at react-query, we were not sure about all the problems which we have to solve in the current project. Also, we didn't understand the full potential of react-query and it was quite new at that time (Major development of the library happened in early 2020). Our implementation of &lt;code&gt;DataRepoArch&lt;/code&gt; started around March 2020.&lt;/p&gt;

&lt;p&gt;Along with the project, &lt;code&gt;DataRepoArch&lt;/code&gt; also started evolving and we enhanced the architecture to allow the developer to write better code. At the later stage of the project, we had a chance to read more about react-query and that is when we started comparing our feature with react-query. Listing some of the core features supported by it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transport/protocol/backend agnostic data fetching (REST, GraphQL, promises, whatever!)&lt;/li&gt;
&lt;li&gt;Auto Caching + Refetching (stale-while-revalidate, Window Refocus, Polling/Realtime)&lt;/li&gt;
&lt;li&gt;Parallel + Dependent Queries&lt;/li&gt;
&lt;li&gt;Mutations + Reactive Query Refetching&lt;/li&gt;
&lt;li&gt;Multi-layer Cache + Automatic Garbage Collection&lt;/li&gt;
&lt;li&gt;Paginated + Cursor-based Queries&lt;/li&gt;
&lt;li&gt;Load-More + Infinite Scroll Queries w/ Scroll Recovery&lt;/li&gt;
&lt;li&gt;Request Cancellation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://reactjs.org/docs/concurrent-mode-suspense.html"&gt;React Suspense&lt;/a&gt; + Fetch-As-You-Render Query Prefetching&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tannerlinsley/react-query-devtools"&gt;Dedicated Devtools (React Query Devtools)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Though we were sad that we didn't realize the potential of &lt;a href="https://github.com/tannerlinsley/react-query"&gt;react-query&lt;/a&gt; and reinvented similar features in &lt;a href="https://github.com/vishalvijay/data-repo-arch-poc"&gt;DataRepoArch&lt;/a&gt;. But we were happy that the problems which we tried to solve are valid and the thought process and solutions which we put to it are aligned with a quality library like react-query. So we are proud of what we made 💪.&lt;/p&gt;

&lt;h5&gt;
  
  
  The future plans
&lt;/h5&gt;

&lt;p&gt;We are not going to invest more in &lt;code&gt;DataRepoArch&lt;/code&gt;. Instead, we are planning to migrate to react-query. And our team will look into potential contributions to it.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>redux</category>
      <category>reactquery</category>
    </item>
    <item>
      <title>The story behind salesforce-orm Gem</title>
      <dc:creator>Vishal Vijay</dc:creator>
      <pubDate>Thu, 24 Sep 2020 11:52:28 +0000</pubDate>
      <link>https://forem.com/vishalvijay/the-story-behind-salesforce-orm-gem-391h</link>
      <guid>https://forem.com/vishalvijay/the-story-behind-salesforce-orm-gem-391h</guid>
      <description>&lt;p&gt;The story behind &lt;a href="https://github.com/NestAway/salesforce-orm"&gt;salesforce-orm&lt;/a&gt; Gem, a successful hack we used to speed up &lt;a href="https://www.nestaway.com/"&gt;Nestaway&lt;/a&gt;'s &lt;a href="https://www.salesforce.com/"&gt;Salesforce&lt;/a&gt; migration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post was migrated from my old personal blog. This was originally published on Jan 2018&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is Salesforce ORM
&lt;/h2&gt;

&lt;p&gt;An ActiveRecord like ORM for Salesforce which can be used in ruby projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The story
&lt;/h2&gt;

&lt;p&gt;In early 2017, Nestaway decided to move many of its processes to the Salesforce platform. As part of this, across the teams, many core components from the backend services have to be migrated to Salesforce. Few services became just a proxy to Salesforce, and for many services, the source of truth for data became either Salesforce or our backend systems, with the proper syncing mechanisms in place.&lt;/p&gt;

&lt;p&gt;So in short, our backend systems have to play with a lot of Salesforce data.&lt;/p&gt;

&lt;p&gt;Our devs had very little experience working Salesforce. So the initial learning curve for the team was very high.&lt;/p&gt;

&lt;p&gt;As this migration process was huge, so the entire process was divided across teams base on service ownership. My team (Tenant experience) was the initial team who started working on this process. We had 2 main responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Completly reengineer and migrate service request market place (SRM) from Freshdesk to Salesforce service cloud.&lt;/li&gt;
&lt;li&gt;Migrate Nestaway users (both tenants &amp;amp; owners) to Salesforce and build a 2-way sync mechanism.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we started with the SRM implementation. As the entire SRM system was being reengineered, we had decided to keep both Freshdesk and Service cloud to be active until we archive the data from Freshdesk. So we architected the system with the bridge pattern and built drivers for Freshdesk, service cloud, and Google cloud data store (for archival).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At Nestaway, most of the backend service was written in ruby and ROR.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So now on let's focus on the driver we built for the service cloud.&lt;/p&gt;

&lt;p&gt;As I told earlier, our team had only very little experience working with Salesforce APIs. So as expected our initial code was full of boilerplates. We also did a 2nd time refactor to reduce the boilerplate. But still as and when the service started growing the boilerplate started increasing which made unit testing and code maintenance horrible.&lt;/p&gt;

&lt;p&gt;For this implementation, we used a great Gem called &lt;a href="https://github.com/restforce/restforce"&gt;restforce&lt;/a&gt; to work with Salesforce REST APIs.&lt;/p&gt;

&lt;p&gt;That is when we actually came to know that, Salesforce supports a query language called &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm"&gt;SOQL&lt;/a&gt;. When we looked at it, we found that it is very similar to SQL syntax, and they support all CRUD operation similar to SQL. Also, we understood that restforce has direct API to execute SOQL queries.&lt;/p&gt;

&lt;p&gt;That was a Eureka moment, we immediately started working on a basic SQL to SOQL converter. And tested it. With that positive impression on that same night, we built a basic working MVP that has a similar API like ActiveRecord. The next morning we demoed it to the team and everyone loved it. That gave us a green sign for building a more concrete Gem. But we were already halfway through the project and now we had to build this new Gem and migrate the existing code to use it (available time was less).&lt;/p&gt;

&lt;p&gt;But with 3 to 4 days effort, we built the first usable version of the gem and we started integrating it into the running project. Surprisingly, that migration was super easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the salesforce-orm
&lt;/h2&gt;

&lt;p&gt;This Gem was providing an ActiveRecord like API to work with Salesforce objects. Any rails developer can easily learn this with their ActiveRecord experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the real hack behind it, which helped us build this entire stuff within 4 days?
&lt;/h3&gt;

&lt;p&gt;In simple words, its the power of ruby, Metaprogramming.&lt;/p&gt;

&lt;p&gt;With a &lt;a href="https://github.com/NestAway/salesforce-orm/blob/master/lib/salesforce-orm/object_base.rb"&gt;proxy&lt;/a&gt;, we actually used ActiveRecord itself to do the trick. The trick was, how we generated the SQL query. With the help of &lt;a href="https://github.com/nulldb/nulldb"&gt;nulldb&lt;/a&gt; we created a fake ActiveRecord instance, which made it generate SQL query.&lt;/p&gt;

&lt;p&gt;Later this SQL query is converted to SOQL with a simple converter and forwarded it to Salesforce using restforce gem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Listing the core modules of the Gem
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/NestAway/salesforce-orm/blob/master/lib/salesforce-orm/object_base.rb"&gt;ObjectBase&lt;/a&gt;: Like ActiveRecord::Base, we have to create each Salesforce object (model) by inheriting this class. This class becomes, the primary proxy to bring in APIs similar to ActiveRecord.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/NestAway/salesforce-orm/blob/master/lib/salesforce-orm/base.rb"&gt;Base&lt;/a&gt;: This is the brain of this Gem. It takes care of building the query, making API requests to Salesforce, and all required data conversions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/NestAway/salesforce-orm/blob/master/lib/salesforce-orm/query_builder.rb"&gt;QueryBuilder&lt;/a&gt;: The real hack for generating SQL query.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/NestAway/salesforce-orm/blob/master/lib/salesforce-orm/sql_to_soql.rb"&gt;SqlToSoql&lt;/a&gt;: A simple SQL to SOQL converter. We know that it is not a solid converter. With the available time, we made it support most of the regular scenarios. And in most cases, we didn't have any issues with this converter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The decision to build salesforce-orm came out really valuable for Nestaway because it helped us to speed up the future releases and it got widely adopted across Nestaway teams. Developers loved it because it was providing ActiveRecord like API. And they didn't have to worry about, what is happening underneath.&lt;/p&gt;

&lt;p&gt;Overall this project was a huge success, and we were really happy to contribute the project to the community. \m/&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>restforce</category>
      <category>orm</category>
    </item>
  </channel>
</rss>
