<?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>DEV Community: behnam rahimpour</title>
    <description>The latest articles on DEV Community by behnam rahimpour (@behnamrhp).</description>
    <link>https://dev.to/behnamrhp</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%2F1425610%2Fdf29c262-891e-4349-a47d-67cce8207228.jpg</url>
      <title>DEV Community: behnam rahimpour</title>
      <link>https://dev.to/behnamrhp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/behnamrhp"/>
    <language>en</language>
    <item>
      <title>How I Monitor My Career Performance</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Sat, 25 Oct 2025 19:19:54 +0000</pubDate>
      <link>https://dev.to/behnamrhp/how-i-monitor-my-career-performance-1f5f</link>
      <guid>https://dev.to/behnamrhp/how-i-monitor-my-career-performance-1f5f</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This article is the result of several years of reading, experimenting, and trying to build a system that helps me stay on track with my &lt;strong&gt;goals, habits, and long-term roadmap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I’ll share my mindset, the tools I use, and how I put everything together into a system that actually works for me.&lt;/p&gt;

&lt;p&gt;Before we start, it’s important to say — &lt;strong&gt;there’s no one-size-fits-all system&lt;/strong&gt;. Everyone’s personality, lifestyle, and situation are different. What works for me might not work for you exactly, but maybe it’ll give you a few ideas to use for your own system.&lt;/p&gt;

&lt;p&gt;At the end of the day, our life is like a system. If we don’t design it intentionally — for our habits, goals, and progress — things get messy fast. Building a system increases your chances of success in literally anything you aim for.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Chain of Efforts Rule
&lt;/h2&gt;

&lt;p&gt;For me, one of the biggest keys to success is what I call the &lt;a href="https://www.amazon.se/-/en/Jeff-Olson/dp/1626340463" rel="noopener noreferrer"&gt;&lt;strong&gt;Chain of Efforts&lt;/strong&gt;&lt;/a&gt; rule.&lt;/p&gt;

&lt;p&gt;It basically means:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Success isn’t one big event — it’s the result of small, boring daily efforts that you keep doing for a long time without breaking the chain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This idea is talked about beautifully in &lt;em&gt;The Slight Edge&lt;/em&gt; book, which I totally recommend.  &lt;/p&gt;

&lt;p&gt;That’s why I’ve built my personal system around this concept — creating and monitoring a structure that helps me keep my daily chain of efforts strong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Monitoring Matters
&lt;/h2&gt;

&lt;p&gt;Think about it — any system, whether it’s a &lt;strong&gt;software&lt;/strong&gt;, &lt;strong&gt;rocket&lt;/strong&gt;, or &lt;strong&gt;business&lt;/strong&gt;, needs constant monitoring.&lt;/p&gt;

&lt;p&gt;A rocket that’s off by just one degree can end up kilometers away from its target.&lt;br&gt;&lt;br&gt;
It’s the same with our goals and habits — even small misalignments can take us in the wrong direction over time.&lt;/p&gt;

&lt;p&gt;So if we don’t monitor our roadmap and daily actions, we might be working hard... but toward the wrong target.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Build My Roadmap and Habits
&lt;/h2&gt;

&lt;p&gt;Once I choose a specific goal, I &lt;strong&gt;break it down&lt;/strong&gt; into smaller, clearer timeframes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5-year vision
&lt;/li&gt;
&lt;li&gt;3-year milestone
&lt;/li&gt;
&lt;li&gt;1-year goal
&lt;/li&gt;
&lt;li&gt;This season
&lt;/li&gt;
&lt;li&gt;This month
&lt;/li&gt;
&lt;li&gt;This week
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At each level, I describe the goal &lt;strong&gt;as if it’s already achieved&lt;/strong&gt;. This helps me visualize success and build the steps backward.&lt;/p&gt;

&lt;p&gt;Once the roadmap is clear, I figure out &lt;strong&gt;which daily habits&lt;/strong&gt; will help me move forward toward those goals.&lt;/p&gt;




&lt;h2&gt;
  
  
  Creating the Right Habits
&lt;/h2&gt;

&lt;p&gt;If you’re new to this, I really recommend starting with &lt;em&gt;Atomic Habits&lt;/em&gt; — it’s a classic for a reason.&lt;/p&gt;

&lt;p&gt;But the key thing is this:&lt;br&gt;&lt;br&gt;
Your habits should be realistic enough to stick with daily, but meaningful enough to push you toward your next goal.&lt;/p&gt;

&lt;p&gt;Don't forget that we need to do our habits for a long run and our brain hates sudden, hard activities, so start small, make it consistent, and let it grow naturally over time to shape the habit.  &lt;/p&gt;

&lt;h2&gt;
  
  
  That’s how I build the “chain of efforts” we talked about earlier.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Monitoring the Roadmap and Goals
&lt;/h2&gt;

&lt;p&gt;After breaking down your goals and setting your habits, it’s time to &lt;strong&gt;track your progress&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At every checkpoint (monthly, seasonal, yearly), I review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What percentage of goals I’ve achieved
&lt;/li&gt;
&lt;li&gt;What worked well
&lt;/li&gt;
&lt;li&gt;Where I struggled
&lt;/li&gt;
&lt;li&gt;What I can improve next time
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reflection part is critical — it’s where you actually learn and adjust your system. Because we always need to update our system and habits through the time based on our new visions, ideas and philosophy of living.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Favorite Tool for Goal Monitoring: &lt;strong&gt;Obsidian&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;There are a lot of goal-tracking tools out there, but if you’re a bit techy and like flexibility, &lt;strong&gt;Obsidian&lt;/strong&gt; is amazing.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://obsidian.md/" rel="noopener noreferrer"&gt;download it here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At first glance, it’s just a note-taking app for Markdown files.&lt;br&gt;&lt;br&gt;
But what makes it powerful is its &lt;strong&gt;plugin ecosystem&lt;/strong&gt; — you can turn it into almost anything you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Key Plugins
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dataview&lt;/strong&gt; – to pull data and stats from notes
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Charts&lt;/strong&gt; – to visualize progress using the data
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kanban&lt;/strong&gt; – to organize tasks visually in boards
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  My Folder Structure
&lt;/h3&gt;

&lt;p&gt;Here’s how I’ve structured folder and files inside of Obsidian:&lt;br&gt;
.&lt;br&gt;
└── Goals/&lt;br&gt;
    ├── Week Goals/&lt;br&gt;
    │   ├── 2025-06-01 (FIN).md&lt;br&gt;
    │   ├── 2025-06-08 (FIN).md&lt;br&gt;
    │   └── 2025-06-16.md&lt;br&gt;
    ├── What to be/&lt;br&gt;
    │   ├── 2030-01-01 (5 years).md&lt;br&gt;
    │   └── 2035-01-10 (10 years).md&lt;br&gt;
    ├── 2025-03-31 End of March.md&lt;br&gt;
    ├── 2025-04-30 End of April.md&lt;br&gt;
    ├── 2025-05-29 End of Spring.md&lt;br&gt;
    ├── 2025-08-29 End of Summer.md&lt;br&gt;
    ├── 2025-11-28 End of Fall.md&lt;br&gt;
    └── 2025-02-27 End of Winter.md&lt;/p&gt;

&lt;p&gt;Each goal file includes goals for one aspect of my life (career, health, learning, etc.), plus a &lt;strong&gt;dynamic progress percentage&lt;/strong&gt; that updates automatically with Dataview and a small JavaScript snippet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yy1w54isaxx7lrhpx1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yy1w54isaxx7lrhpx1x.png" alt="Goals file" width="800" height="688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my “Statistics” file, I gather the results from all the smaller goals and visualize them using the Charts plugin — it’s super motivating to see progress in one place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67mr6tfvqrqjen75f3ki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67mr6tfvqrqjen75f3ki.png" alt="Statistic file" width="657" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For weekly goals, I use the same structure — and mark completed weeks as &lt;code&gt;(FIN)&lt;/code&gt;. Those get picked up by the weekly statistics automatically.&lt;/p&gt;

&lt;p&gt;You can find my example scripts in this gist &lt;a href="https://gist.github.com/behnamrhp/c6ca739ced773e3c9dd140410995186e" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
You can freely use and modify my code to fit your own workflow.&lt;br&gt;
Just copy each code and put in related file like this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpg5t0yjot5wxph3jwq60.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpg5t0yjot5wxph3jwq60.png" alt="Goals with script" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Bonus tip: I also use the Kanban plugin to connect roadmap items with related goals and tasks. It helps me see everything more clearly without extra tools.&lt;/p&gt;

&lt;p&gt;Also for syncing obsidian data I use &lt;a href="https://syncthing.net/" rel="noopener noreferrer"&gt;synthing&lt;/a&gt; and I'm share the folders between pc and mobile. But you can use many other ways or apps like this to share obsidian and document folder between your pc and other platforms or use other community plugins.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Tracking Habits
&lt;/h2&gt;

&lt;p&gt;You can use Obsidian for habits too, but personally, I prefer a dedicated app for simplicity.&lt;/p&gt;

&lt;p&gt;My favorite is &lt;strong&gt;Habit Tracker&lt;/strong&gt; on Android (you can find it &lt;a href="https://play.google.com/store/apps/details?id=habittracker.todolist.tickit.daily.planner&amp;amp;hl=en" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;br&gt;&lt;br&gt;
It gives you clean weekly and daily statistics — perfect for seeing your “chain of efforts” visually.&lt;/p&gt;

&lt;p&gt;And for doing habits or focused work sessions, I use the &lt;strong&gt;Pomodoro technique&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
My go-to app is &lt;a href="https://www.focustodo.cn/" rel="noopener noreferrer"&gt;Focus To-Do&lt;/a&gt; — simple, synced, and effective.&lt;/p&gt;




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

&lt;p&gt;A lot of people focus on &lt;strong&gt;making effort&lt;/strong&gt;, but I’ve learned that the real power comes from &lt;strong&gt;building a system&lt;/strong&gt; that helps you make those efforts wisely and consistently.&lt;/p&gt;

&lt;p&gt;Your system doesn’t have to be perfect — it just has to be yours.&lt;/p&gt;

&lt;p&gt;I hope sharing mine gives you some new ideas to experiment with and build your own version.&lt;/p&gt;

&lt;p&gt;I’d love to hear from you:&lt;br&gt;&lt;br&gt;
👉 What kind of systems do &lt;em&gt;you&lt;/em&gt; use to monitor your goals or career progress?&lt;br&gt;&lt;br&gt;
👉 How could I improve mine?&lt;/p&gt;

&lt;p&gt;If this article helped you or gave you new ideas, I’d really appreciate your interaction — it helps me share this with more people.&lt;/p&gt;

&lt;p&gt;Thanks for reading — and don’t break the chain 💪&lt;/p&gt;

</description>
      <category>programming</category>
      <category>softwareengineering</category>
      <category>career</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>A Practical Guide to GraphQL N+1 Solutions in NestJS</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Sat, 23 Aug 2025 19:41:38 +0000</pubDate>
      <link>https://dev.to/behnamrhp/a-practical-guide-to-graphql-n1-solutions-in-nestjs-5g80</link>
      <guid>https://dev.to/behnamrhp/a-practical-guide-to-graphql-n1-solutions-in-nestjs-5g80</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;At Comfortel, we developed a large-scale admin panel to manage a complex system with tightly coupled domains and bounded contexts. We used a GraphQL gateway to abstract the intricate communications between these domains.&lt;/p&gt;

&lt;p&gt;Initially, the team was pleased with this abstraction, as many are when first adopting GraphQL. However, as in all software development—and indeed, in life—every decision is a trade-off. Our choice of abstraction soon led to new challenges in performance and scalability, primarily the infamous N+1 problem.&lt;/p&gt;

&lt;p&gt;This case study details our problem-solving approach, the design of our implementation based on our technologies with Nestjs as our Gateway, and the final results.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the N+1 problem (why GraphQL often triggers it)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is&lt;/strong&gt;: N+1 happens when we fetch a list of N parent records, then for each parent we issue an additional query to fetch nested data. That becomes 1 (for the list) + N extra queries. With deeper nesting it multiplies quickly (e.g., per-nested item queries).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why GraphQL triggers it&lt;/strong&gt;: GraphQL resolvers are field-based. Without batching, each field on each returned parent can cause its own data fetch. For lists, this leads to an explosion in calls to databases/brokers if left unoptimized.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Concrete example (Places → Members → Users)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query: list 50 &lt;code&gt;places&lt;/code&gt;, and for each place resolve &lt;code&gt;members&lt;/code&gt;, and for each member resolve their &lt;code&gt;user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Naive calls:&lt;/li&gt;
&lt;li&gt;1 call: list places (DB/broker)&lt;/li&gt;
&lt;li&gt;+50 calls: for each place, fetch members&lt;/li&gt;
&lt;li&gt;+Σ calls: for each place's members fetch user. If average 10 members per place, +500 calls.&lt;/li&gt;
&lt;li&gt;Total naive example with 50 places and 10 members per place: 1 + 50 + 500 = &lt;strong&gt;551 calls&lt;/strong&gt; per request.&lt;/li&gt;
&lt;li&gt;Complexity: O(P + P + P·M) ≈ O(P·M). With additional nested levels, this compounds.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solutions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Apollo Connectors batching (schema-level change)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Batch resolver calls at the Graph schema layer using Connectors.&lt;/li&gt;
&lt;li&gt;Requires updating the GraphQL schema with &lt;code&gt;@connect&lt;/code&gt; mappings.&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://www.apollographql.com/docs/graphos/connectors/requests/batching" rel="noopener noreferrer"&gt;Apollo Connectors: Batch Requests&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Example (IDs batched via query params):
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;ecom&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"/&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;maxSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DataLoader (application-level wrapper)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Batch and cache per-request fetches in application resolvers using a &lt;code&gt;DataLoader&lt;/code&gt; abstraction.&lt;/li&gt;
&lt;li&gt;No schema changes required. Fits where we control resolver implementation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution Choice
&lt;/h2&gt;

&lt;p&gt;We are operating at the application level; therefore, &lt;strong&gt;DataLoader&lt;/strong&gt; is a better fit for us than schema-level connectors. based on the time and efficiency of our goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Context-based DataLoader Management
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Our requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: a shared, domain-scoped batching layer that can be reused across resolvers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic nested batching&lt;/strong&gt;: convert N+1 per nested field into 1 batched request per nested layer:

&lt;ul&gt;
&lt;li&gt;1 request for the parent list&lt;/li&gt;
&lt;li&gt;1 request for each nested entity layer (e.g., members, ancestors, parents)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Request-scoped caching&lt;/strong&gt;: Each GraphQL request gets its own DataLoader instances to prevent cache pollution between requests and remove duplicated nested entities ids.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Context-based approach?&lt;/strong&gt;&lt;br&gt;
We need to initialize DataLoader in each GraphQL request separately to handle caches of DataLoader for each request separately. As each domain batch loader can be used in many other domains and managing dependencies, normal injecting and initializing DataLoader causes cache issues of singleton instances between requests. Therefore, we use the context callback of GraphQL with &lt;code&gt;forRootAsync&lt;/code&gt; and initialize DataLoader in each incoming GraphQL request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encapsulation Strategy&lt;/strong&gt;&lt;br&gt;
To encapsulate the details of managing DataLoaders and their initialization and their modules, we created &lt;code&gt;BatchLoadService&lt;/code&gt; and &lt;code&gt;BatchLoadModule&lt;/code&gt;. This allows us to manage injection and initialization of domain batch loaders from a centralized location.&lt;/p&gt;

&lt;p&gt;To achieve this with &lt;code&gt;dataloader&lt;/code&gt; we rely on two tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;load&lt;/code&gt;&lt;/strong&gt;: this method, resolves a single key to a single result (or a list, but keyed by one id) via batching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;loadMany&lt;/code&gt;&lt;/strong&gt;: resolve multiple keys at once (e.g., a list of ids) via batching.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Scenarios and how to apply the tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Parent entity with one nested entity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a) If parent exposes nested entity id: use &lt;code&gt;load&lt;/code&gt; with the nested id.&lt;/li&gt;
&lt;li&gt;Example: get Members by member ids. and the return type will be array of members.&lt;/li&gt;
&lt;li&gt;b) If parent does NOT expose nested entity id: use &lt;code&gt;load&lt;/code&gt; with parent id (the batch function fetches by parent ids).&lt;/li&gt;
&lt;li&gt;Example service: get Members by place ids. and the return type again will be array of members.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Parent entity with list of nested entities&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a) If parent exposes nested ids list: use &lt;code&gt;loadMany&lt;/code&gt; with nested ids.&lt;/li&gt;
&lt;li&gt;Example: get ancestors by &lt;code&gt;ancestor_ids&lt;/code&gt;. and the return type will be array of ancestor ids.&lt;/li&gt;
&lt;li&gt;b) If parent does NOT expose nested ids list: use &lt;code&gt;load&lt;/code&gt; with parent id; the batch function returns an array per parent.&lt;/li&gt;
&lt;li&gt;Example service: get Members by place ids and return an array per place and the return type will be array of array of members.&lt;/li&gt;
&lt;li&gt;Note: the batch function must return results in the exact order of requested keys (array of arrays aligned to parent order).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Directory design (per-domain batch layer)
&lt;/h3&gt;

&lt;p&gt;Keep a reusable batching class per domain to consolidate all batchable methods for that domain, and export typed loaders for use in resolvers across domains.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
  infrastructure/
    common/
      batch-load/
        batch-load.module.ts        # Centralized batch loader management
        batch-load.service.ts       # Service to initialize all loaders
        batch-load.interface.ts     # Common interfaces and types
  application/
    place/
      place.batch-load.ts        # Place-specific batching (e.g., by place ids)
    service/
      service.batch-load.ts      # Example for service domain (pattern mirrors place)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Class diagram (PUML)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="//www.plantuml.com/plantuml/png/dPJFRjD04CRlVefHJermyW1MPA8MWP1I2QNY13rixMcwOl-uE-FGgl3ko6F7RgOHeDooEx_vlcztDbuJcyIjTrbMEKC4NvofaBP8qUqnmKi6y0uQap3mHTFLvaNFIC642ebZeh6siSGh0KeXFFexrFLhsIoptmVNIyFLmoQQ-ZhMhSE3ROfxMsEY0hwDn0sclQqmXrvatC_xMEFwLYIiRsBYJa2glNmENmdJgusUnQS3gyrkTTAf954Nq1nv5ogB8FuuRw4FqESDbhj5TsNpOdAxYe7nYSSSoW1UJdLliOgfNX4d6tOvV31i-cefWPmbldmMeS-rD4p4wcPealBxQLGgzNdE0Ocntlo7_z5xaHGsCAPxKo6Scs7LXXyX_Wmv33-qiBvrqPS9_-Da_OZ_0ecH-eijb-LRnm9so0JtANfO3viNixiEs-9ZYyIpUVRcBIuMfNuO1NWJp0xfDAsdEU5AfuDdqq5ccUxKW1POBd-LyDh8-iQXny2ajHIRgQ-3MEdJP5VH-nWqCNMTklQZdW8-8HDKmmJawdV9D0-FRY2-1p2X1ii4BU57Kt5CpuU6R8sXRhtx0m00" class="article-body-image-wrapper"&gt;&lt;img src="//www.plantuml.com/plantuml/png/dPJFRjD04CRlVefHJermyW1MPA8MWP1I2QNY13rixMcwOl-uE-FGgl3ko6F7RgOHeDooEx_vlcztDbuJcyIjTrbMEKC4NvofaBP8qUqnmKi6y0uQap3mHTFLvaNFIC642ebZeh6siSGh0KeXFFexrFLhsIoptmVNIyFLmoQQ-ZhMhSE3ROfxMsEY0hwDn0sclQqmXrvatC_xMEFwLYIiRsBYJa2glNmENmdJgusUnQS3gyrkTTAf954Nq1nv5ogB8FuuRw4FqESDbhj5TsNpOdAxYe7nYSSSoW1UJdLliOgfNX4d6tOvV31i-cefWPmbldmMeS-rD4p4wcPealBxQLGgzNdE0Ocntlo7_z5xaHGsCAPxKo6Scs7LXXyX_Wmv33-qiBvrqPS9_-Da_OZ_0ecH-eijb-LRnm9so0JtANfO3viNixiEs-9ZYyIpUVRcBIuMfNuO1NWJp0xfDAsdEU5AfuDdqq5ccUxKW1POBd-LyDh8-iQXny2ajHIRgQ-3MEdJP5VH-nWqCNMTklQZdW8-8HDKmmJawdV9D0-FRY2-1p2X1ii4BU57Kt5CpuU6R8sXRhtx0m00" alt="Pumlt Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation guideline
&lt;/h2&gt;

&lt;h3&gt;
  
  
  GraphQL Module Configuration
&lt;/h3&gt;

&lt;p&gt;The GraphQL module is configured with &lt;code&gt;forRootAsync&lt;/code&gt; to initialize DataLoaders per request from context property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;GraphQLModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forRootAsync&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ApolloDriverConfig&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApolloDriver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BatchLoadModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;batchLoadService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BatchLoadService&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="na"&gt;autoSchemaFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dist/schema.gql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;graphiql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;context&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;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;batchRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;batchLoadService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initLoaders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;buildSchemaOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;numberScalarMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;integer&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="nf"&gt;formatError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedError&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="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;formatGraphQLError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedError&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="na"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BatchLoadService&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;As you see for each request from context we initialize our dataloader.&lt;/p&gt;

&lt;h3&gt;
  
  
  BatchLoad Module Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BatchLoadService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MemberBatchLoad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PlaceBatchLoad&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;PlaceModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserPlaceModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BatchLoadService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BatchLoadModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding New Domain Batch Loaders
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create the batch loader class&lt;/strong&gt; implementing the &lt;code&gt;BatchLoad&lt;/code&gt; interface:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewDomainBatchLoad&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;BatchLoad&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;newDomainService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NewDomainService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;initLoaders&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;loadByIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadByIds&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="c1"&gt;// ... other loaders&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;loadByIds&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&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;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kr"&gt;string&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;NewDomain&lt;/span&gt;&lt;span class="p"&gt;[]&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newDomainService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&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;resultMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeFrequencyCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resultMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&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="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;makeFrequencyCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NewDomain&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="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&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="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&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="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NewDomain&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;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add to BatchLoadService&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BatchLoadService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;placeBatchLoad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PlaceBatchLoad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;memberBatchLoad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MemberBatchLoad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;newDomainBatchLoad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NewDomainBatchLoad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Add this&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;initLoaders&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;place&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;placeBatchLoad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initLoaders&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;member&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memberBatchLoad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initLoaders&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;newDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newDomainBatchLoad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initLoaders&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// Add this&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;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add to BatchLoadModule&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;BatchLoadService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;MemberBatchLoad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;PlaceBatchLoad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;NewDomainBatchLoad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Add this&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;PlaceModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserPlaceModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NewDomainModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Add required modules&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BatchLoadService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BatchLoadModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Resolver Usage
&lt;/h3&gt;

&lt;p&gt;Resolvers now get batch loaders from the GraphQL context instead of dependency injection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ResolveField&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;PlaceObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Place&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;batchRequest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;batchRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BatchLoadContext&lt;/span&gt;&lt;span class="p"&gt;)&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;batchRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listByPlaceIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent_id&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="nd"&gt;ResolveField&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="nx"&gt;PlaceObject&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;itemsAndList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;ancestors&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Place&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;batchRequest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;batchRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BatchLoadContext&lt;/span&gt;&lt;span class="p"&gt;)&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ancestor_ids&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;batchRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listByPlaceIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ancestor_ids&lt;/span&gt; &lt;span class="o"&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="nd"&gt;ResolveFieldSafe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;PaginatedMemberObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;itemsAndList&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;members&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="nf"&gt;members&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Place&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;batchRequest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;batchRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BatchLoadContext&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="nx"&gt;batchRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nestedListByPlaceIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;place&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&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;h3&gt;
  
  
  Key Implementation Details
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Members batch loader&lt;/strong&gt; (parent id → list of nested entities)

&lt;ul&gt;
&lt;li&gt;Tool used: &lt;code&gt;load&lt;/code&gt; with parent place id, returning an array per key, keeping order.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;nestedListByPlaceIds&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&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;placeIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kr"&gt;string&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;Member&lt;/span&gt;&lt;span class="p"&gt;[][]&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;membersService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMembersByPlaceIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;placeIds&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&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;resultFrequencyCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeMembersFrequencyCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;placeIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resultFrequencyCounter&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Place batch loader&lt;/strong&gt; (single id for parent, list of ids for ancestors):

&lt;ul&gt;
&lt;li&gt;Tool used for single id: &lt;code&gt;load&lt;/code&gt; by place id (e.g., parent), and for lists: &lt;code&gt;loadMany&lt;/code&gt; for ancestors.&lt;/li&gt;
&lt;li&gt;The reducer builds a frequency counter to ensure O(N) combine and preserve order.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;listByPlaceIds&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataLoader&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;placeIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kr"&gt;string&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Place&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)[]&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;places&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;placesService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;placeIds&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&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;resultFrequencyCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makePlacesFrequencyCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;places&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;placeIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resultFrequencyCounter&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;The key principle with DataLoader&lt;/strong&gt;: the batch function must return results in the same order as the keys. To avoid O(N²) lookup, we build a frequency counter map keyed by id and then map results back in order of input keys. This gives O(N) combine time for each batch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results and complexity comparison
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Legacy approach&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calls ≈ 1 (places) + P (members by place) + P·M (users by member) = O(P·M)&lt;/li&gt;
&lt;li&gt;Example with 50 places and 10 members per place → 551 calls&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Batched approach with DataLoader&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calls ≈ 1 (places) + 1 (all members for all places) + 1 (all users for all members) = O(1) per nested layer&lt;/li&gt;
&lt;li&gt;Example with 50 places and 10 members per place → 3 calls&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Impact&lt;/strong&gt;: Dramatically fewer DB/broker requests, lower latency, and significantly reduced load on downstream services, with clear, reusable batching primitives per domain.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Last consideration: Handling Large Lists
&lt;/h2&gt;

&lt;p&gt;We now gather all nested entity IDs efficiently. However, a new consideration arises: if a nested entity is a long list associated with a single parent, fetching the entire list could result in a heavy, inefficient query.&lt;/p&gt;

&lt;p&gt;To solve this potential problem, we addressed it through API design. For use cases where a nested entity could return a very long list, our GraphQL API returns only the list of IDs. We then provide a separate, dedicated API endpoint to fetch the contents of that list, complete with pagination features.&lt;/p&gt;

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

&lt;p&gt;This challenge pushed us to delve deeper into GraphQL's execution logic and ultimately implement a sophisticated solution using DataLoader patterns. The results were undeniable: we achieved a significant reduction in database load and dramatically improved response times for our large-scale admin panel, ensuring it could scale effectively.&lt;/p&gt;

&lt;p&gt;However, no solution is perfect. As we noted, addressing one bottleneck often reveals another, leading us to design a hybrid approach for large datasets using paginated endpoints.&lt;/p&gt;

&lt;p&gt;The key lesson? Powerful abstractions are invaluable, but they must be implemented with a clear understanding of their performance implications. Proactive monitoring and a willingness to iterate on your design are crucial for long-term success.&lt;/p&gt;

&lt;p&gt;We’re eager to hear from other developers and architects. Share your experiences and thoughts in the comments below!&lt;/p&gt;

&lt;p&gt;Help us reach more developers by your reaction and sharing this case study with your network.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>backend</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Case Study: How we Cut Blocking Time by 92% in React admin panel - A Story Starring useTransition</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Fri, 16 May 2025 21:10:28 +0000</pubDate>
      <link>https://dev.to/behnamrhp/we-cut-blocking-time-by-92-a-story-starring-usetransition-6ni</link>
      <guid>https://dev.to/behnamrhp/we-cut-blocking-time-by-92-a-story-starring-usetransition-6ni</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In this article, I want to tell you the story of the fixing a massive performance problem I faced in one of the projects which had a laggy, frustrating UI with slow renders and excessive API calls by changing the way of thinking in the coding architecture. The app had a complex tree-table structure, where every interaction felt sluggish, with blocking times up to 850ms and an explosion of network requests.&lt;/p&gt;

&lt;p&gt;It happens just by rethinking how data should flows and stores and how components should be rendered, we slashed blocking time to 70ms (92% faster), cut API calls by 60% and 2x faster rerendering in performance of user interactions with any components in the page.&lt;/p&gt;

&lt;p&gt;How? I found when the issue is in the process of data flow or the way of storing the data throwing more &lt;code&gt;useMemo&lt;/code&gt; at the problem or some minor changes in the caches doesn't help at all. Instead, we had to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Break a heavy dependency between the huge components, and URL state which caused massive heavy rerendering and high blocking time.&lt;/li&gt;
&lt;li&gt;Stop recursive fetching that caused O(n) API calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this experience I learnt some good lessions and I reached to some ideas and I decided to share it with you.&lt;/p&gt;

&lt;p&gt;Let’s dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem realizing
&lt;/h2&gt;

&lt;p&gt;In this project, there is a large, tree-shaped main component, structured like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifq4fg8z8tkqqaz3er1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifq4fg8z8tkqqaz3er1f.png" alt="Tree component ui" width="264" height="746"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, a table dynamically updates its content based on the selected node in this tree with many features like drag and drop of columns, many filtering, switching columns, export, etc:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hl1o3iuq0am1ucz29a1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hl1o3iuq0am1ucz29a1.png" alt="Table component ui" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To persist results between page reloads and enable shareable links, we store the selected tree branch and related data in the URL. This is achieved using dynamic path segments and query parameters, structured like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgzyjrba4pmci6gf8f5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgzyjrba4pmci6gf8f5g.png" alt="Url selected branch in the tree" width="800" height="22"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You see all ids after place path is the selected branch ids in the tree.&lt;/p&gt;

&lt;p&gt;There were several problems:&lt;/p&gt;

&lt;h3&gt;
  
  
  Excessive API Requests on Page Load
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Based on selected branch ids in the url, for every tree node, we checked if its ID existed in the selected branch IDs string or not. If so, we fetched its children.&lt;/li&gt;
&lt;li&gt;This resulted in O(n) server requests (where &lt;em&gt;n&lt;/em&gt; = depth of selected branches), significantly slowing down initial load.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Slow Re-rendering on User Interaction
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clicking a tree or table item triggered a &amp;gt;160ms delay due to:&lt;/li&gt;
&lt;li&gt;Heavy components depended on any url changes and as the result getting rerendered.&lt;/li&gt;
&lt;li&gt;Full dependency on URL state for all components even for some changes which shouldn't be depend on. For example tree shouldn't be depend or changes of selected table item in the url.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can see on click of each item in table we rendered the tree, despite we had memoization the ui part of the each tree node but as their ui logics accessed to parameters in the url they had rerendered in the ui logic part of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wp6wsry3r74oc7sh4qo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wp6wsry3r74oc7sh4qo.png" alt="heavy render time for click on one item in the table was 142ms" width="146" height="337"&gt;&lt;/a&gt;&lt;br&gt;
You can see after clicking on each item in the table in average we have 150ms of rerender.&lt;/p&gt;

&lt;p&gt;Also after clicking on each node in the tree we had in average 160ms of rerender time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lm7ohbqws31cto5af1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lm7ohbqws31cto5af1o.png" alt="heavy render time for click on one node in the tree was 158ms" width="146" height="337"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Excessive API Requests on Page Load
&lt;/h3&gt;

&lt;p&gt;The implementation suffered from two compounding issues. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, each node independently accessed URL parameters to determine whether it needed to fetch its children.&lt;/li&gt;
&lt;li&gt;Second, the recursive nature of these checks meant that for a deeply nested selection in the tree - say five levels deep - the page would make five consecutive API requests before fully rendering, with each request waiting for its parent's response like links in a chain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This waterfall fetching pattern not only slowed initial page loads but also caused unnecessary network traffic, as intermediate nodes would re-fetch data their parents already possessed.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.plantuml.com%2Fplantuml%2Fdpng%2FPL4zJqCn3DppAqxT-oJM2g4Yn0OCV6nTtCHzYKZpgjXjuTyJfu8dr2MAxyuNdxCMfQh7VNQ1bBOaZ3Uk9wuWWRX38vDF1ogAnNjbnYdnUM6ajkgQVva2usOOXj95fPN_H1-lJuOVQtQk7LZUTSqA3v7Ao04Q6SAPlLfXBsuh5H-HYd6ziyEtIxkly4p663AoQYeZAbSkWMl0zjiqyDrRe9DfPdjxUN1ssXoomYFvsD7PgplnjLNaWgJOb0Qn_nIa7VonDhrU7580VoLHmMP_6VulIc_UiRREIs3RFQW4I9pEa6dFo1E5De2JUKNteqNzJTSpTmQPJvnjmdvnwyQt7_q1" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.plantuml.com%2Fplantuml%2Fdpng%2FPL4zJqCn3DppAqxT-oJM2g4Yn0OCV6nTtCHzYKZpgjXjuTyJfu8dr2MAxyuNdxCMfQh7VNQ1bBOaZ3Uk9wuWWRX38vDF1ogAnNjbnYdnUM6ajkgQVva2usOOXj95fPN_H1-lJuOVQtQk7LZUTSqA3v7Ao04Q6SAPlLfXBsuh5H-HYd6ziyEtIxkly4p663AoQYeZAbSkWMl0zjiqyDrRe9DfPdjxUN1ssXoomYFvsD7PgplnjLNaWgJOb0Qn_nIa7VonDhrU7580VoLHmMP_6VulIc_UiRREIs3RFQW4I9pEa6dFo1E5De2JUKNteqNzJTSpTmQPJvnjmdvnwyQt7_q1" alt="first-diagram" width="1013" height="296"&gt;&lt;/a&gt;&lt;br&gt;
In this diagram, tree node is each node item in the tree and tree view as the main component of the tree which contains all the tree nodes.&lt;/p&gt;

&lt;p&gt;So we changed our idea of getting the data from the node to just fetching the whole branch at once from highest parent tree component and render it just once not level by level from the node aspect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.plantuml.com%2Fplantuml%2Fdpng%2FROyzJiH038NxdE9fUpZ05cWbMYf-EXfdnYI39fx8zYpYzcHSW92silm-Vq_onPpK-rQd_PWbbPt4SNfLPbmBVvr01Z-sV_pj-MdaNUkKoMacOxomNbb7RDDq4DmzX7Z6uqgoS8Ql3EFAoJa7mgmaQKMHOADknCEqCwXMn2ejis5f3bgacPS4cblt8I15kU5TG9BnsSsnKP5GGr9MbipA6VFtBMTXzo8BnYluqBR1uXT4MPF4uz9Nbj-ky7OgydTpFrqErBVw0m00" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.plantuml.com%2Fplantuml%2Fdpng%2FROyzJiH038NxdE9fUpZ05cWbMYf-EXfdnYI39fx8zYpYzcHSW92silm-Vq_onPpK-rQd_PWbbPt4SNfLPbmBVvr01Z-sV_pj-MdaNUkKoMacOxomNbb7RDDq4DmzX7Z6uqgoS8Ql3EFAoJa7mgmaQKMHOADknCEqCwXMn2ejis5f3bgacPS4cblt8I15kU5TG9BnsSsnKP5GGr9MbipA6VFtBMTXzo8BnYluqBR1uXT4MPF4uz9Nbj-ky7OgydTpFrqErBVw0m00" alt="second-diagram" width="446" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To address these performance issues, we fundamentally redesigned the data fetching approach. Rather than having each individual node check the URL and recursively fetch its children - which created the O(n) request problem - we implemented a centralized solution. The highest-level parent component now retrieves the complete branch data in a single request when the URL parameters change. This atomic fetch gathers all necessary nodes from the root to the deepest selected leaf in one network call, eliminating the previous chain of sequential requests.&lt;/p&gt;

&lt;p&gt;With this optimized architecture, the tree renders just once with the complete dataset instead of progressively updating level by level. The nodes no longer need to independently manage data fetching or parse URL parameters, which prevents the cascading re-renders that previously bypassed memoization. By shifting from a distributed to a consolidated data model, we reduced both network overhead and computational waste, as the system now handles branch selection as a unified operation rather than a series of fragmented node-level interactions.&lt;/p&gt;

&lt;p&gt;And in the result, with the same level of selected tree node form 3.6s of LCP 2.25s pf FCP and 850ms of blocking time,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgapzm8zu6jgw4xb301jp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgapzm8zu6jgw4xb301jp.png" alt="Bad result of light house before changes as description" width="600" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We reached to 2.1 of LCP, 0.7s of FCP and 70ms of blocking time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvptuc96xmd3h7sblt6kj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvptuc96xmd3h7sblt6kj.png" alt="Good result of light house after changes as description" width="574" height="185"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Laggy UI on url changes
&lt;/h3&gt;

&lt;p&gt;As we mentioned, based on the selected branch IDs in the URL, we re-rendered the tree and the table together since both of them depended on URL changes.&lt;/p&gt;

&lt;p&gt;We used a technique in the tree to make it agnostic about URL changes. Because we had many heavy re-renders based on URL changes, we separated the tree and table re-renders into two smaller, independent re-renders. To achieve this, we followed these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Defined an internal store for the tree state. which has two parameters.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TreeState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * With nodeToFetch, we decide which node required to fetch its sub tree and show loading.
   */&lt;/span&gt;
  &lt;span class="nx"&gt;nodeToFetch&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * With selectedBranchIds we ask tree view to change selected one and ask url to update the path
   * */&lt;/span&gt;
  &lt;span class="nl"&gt;selectedBranchIds&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&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;ol&gt;
&lt;li&gt;To make the tree, agnostic about the url changes we removed its dependency to the url changes and just initialize the selected branch ids when component mounted for the first time, just for one time.
&lt;/li&gt;
&lt;/ol&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;urlBranchIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commonUrlHanlder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSelectedTreeBranchIds&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;useEffect&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;treeStateStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;setTreeState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;selectedBranchIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;urlBranchIds&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;To update the URL based on internal state changes and separate the re-renders of the tree and table into two isolated smaller re-renders, we used transitions for URL updates.
&lt;/li&gt;
&lt;/ol&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;updateParamsNavigator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commonUrlHanlder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNavigatorToUpdateParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startTransition&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTransition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commonUrlHanlder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNavigatorToUpdateBranchIds&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;useEffect&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isInitialized&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="nf"&gt;startTransition&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="nf"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectedBranchIds&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;updateParamsNavigator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&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="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedBranchIds&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As a result, all internal state changes of our computationally intensive tree component now occur in isolation, while table updates synchronize via URL changes using useTransition — allowing React to prioritize these updates as non-blocking background work.&lt;/p&gt;

&lt;p&gt;And in the result on click of each node in the tree from in average 190ms of heavy task in the performance:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi844g7ltzdkkhcp1ifq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi844g7ltzdkkhcp1ifq4.png" alt="performance statistics which was 193ms" width="287" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We reached to all small rerenders all less than 50ms:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6aws9g3xskdi9ng3g5r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6aws9g3xskdi9ng3g5r.png" alt="performance statistics which was all render time less than 50ms" width="441" height="719"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Last shot to remove all unnecessary rerenders in the tree
&lt;/h3&gt;

&lt;p&gt;As the last action, the commonUrlHandler which was the boundary between the main codes and update url methods, behind the scene was using react router hooks and with any unrelated changes in the url parameters, the tree got rerendered. But based on our strategy the tree component shouldn't care about the url changes at all. So we used window object methods to get url data staticaly and just for hydration and update url chantes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="cm"&gt;/**
   * Update route without rerendering and react hooks
   * @param route - route to update
   */&lt;/span&gt;
  &lt;span class="nf"&gt;staticUpdateRouteWithoutRerendering&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushState&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sanitizeURL&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="nx"&gt;route&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PopStateEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;popstate&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the method in commonUrlHandler class which cause updating the url without rerendering the tree and this dispatch event helps react router find url changed then rerender the components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nf"&gt;startTransition&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;getRouteWithoutParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commonUrlHanlder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;staticRouteWithoutParams&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;getRouteWithoutParams&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commonUrlHanlder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;staticUpdateRouteWithoutRerendering&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="nx"&gt;getRouteWithoutParams&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;route&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="nx"&gt;selectedBranchIds&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&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="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;As you see instead of using react router hooks we just used window object in the tree and just on the first load we hydrate the component by the url informations.&lt;/p&gt;

&lt;p&gt;As the result, now the tree doesn't rerender, on the changes of the url.&lt;/p&gt;

&lt;h4&gt;
  
  
  Performance result
&lt;/h4&gt;

&lt;p&gt;The performance result calculated by user interaction with other components of the app except the tree, like advanced table component which was another huge component with many fitlers, features and drag and drop of columns in the same page as the tree. &lt;/p&gt;

&lt;p&gt;All re-renders in the app caused by URL changes have now been optimized, improving performance by 2x.&lt;/p&gt;

&lt;p&gt;For example, in the comparison screenshot, we clicked the same item in a table across two versions of the app. Previously, this would trigger a URL update (storing the selected item in the URL), leading to unnecessary re-renders in the tree component. Now, the performance impact is significantly reduced—here’s as we opt-out tree rerenders by url changes and the result:&lt;/p&gt;

&lt;p&gt;From average 75 reached to less than 50ms in all tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flr378pbhlepyoizg317w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flr378pbhlepyoizg317w.png" alt="browser dev tools of performance in legacy codes" width="572" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2quaxc5p4tekzs7joy1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2quaxc5p4tekzs7joy1.png" alt="browser dev tools of performance after changes" width="393" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And after all these changes the final score of light house from in average 65 increased to 90 in average.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fds3qdgce9rjutxcr8byj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fds3qdgce9rjutxcr8byj.png" alt="Final light house score with 92 score" width="594" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Performance optimizations often look like small tweaks, but the some times biggest wins come from architectural shifts, not just micro-optimizations. Here’s what we learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;One State Shouldn’t Control Everything&lt;br&gt;
Storing all state in the URL created cascading massive re-renders. By isolating the main standalone app component's state, we can keep it fast while still allowing shareable links.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Batch request, Don’t Cascade&lt;br&gt;
Batch request and cut network overhead and removed render waterfalls.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Break your heavy renders&lt;br&gt;
With useTransition, we made the table’s URL updates non-blocking, so the tree stayed responsive even during large state changes in two lighter rerenders.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final Thought:&lt;/strong&gt;&lt;br&gt;
If your app feels slow and you tried all tools and techniques from the framework and youre language, but still no changes happened, look from more abstraction viewport, maybe the solution resides in the higher level of architecting the codes not just small tweaks.&lt;/p&gt;

&lt;p&gt;If you found this article helpful, I’d be truly grateful if you could:&lt;br&gt;
⭐ Add some reaction to the article&lt;br&gt;
💬 Share your thoughts in the comments&lt;/p&gt;

&lt;p&gt;Every interaction helps these best practices reach more developers!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Next.js Localization Best Practices for Enterprise Apps</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Sat, 03 May 2025 14:49:41 +0000</pubDate>
      <link>https://dev.to/behnamrhp/nextjs-localization-best-practices-for-enterprise-apps-3m9k</link>
      <guid>https://dev.to/behnamrhp/nextjs-localization-best-practices-for-enterprise-apps-3m9k</guid>
      <description>&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of contents&lt;/li&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Guideline for adding new key&lt;/li&gt;
&lt;li&gt;Folder acrhitecture&lt;/li&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;li&gt;Provider and client support&lt;/li&gt;
&lt;li&gt;Combination with failure handling and params validation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This article based on Next.js boilerplate repository.&lt;br&gt;
To explore all concepts in depth and see a production-ready boilerplate and following these best practices, visit:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/behnamrhp" rel="noopener noreferrer"&gt;
        behnamrhp
      &lt;/a&gt; / &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;
        Next-clean-boilerplate
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Full featured nextjs boilerplate, based on clean architecture, mvvm and functional programming paradigm.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Nextjs clean architecture boilerplate&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of content&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#nextjs-clean-architecture-boilerplate" rel="noopener noreferrer"&gt;Nextjs clean architecture boilerplate&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#table-of-content" rel="noopener noreferrer"&gt;Table of content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#overview" rel="noopener noreferrer"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#motivation" rel="noopener noreferrer"&gt;Motivation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#technologies" rel="noopener noreferrer"&gt;Technologies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#architecture" rel="noopener noreferrer"&gt;Architecture&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#folder-structure" rel="noopener noreferrer"&gt;Folder structure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#requirements" rel="noopener noreferrer"&gt;Requirements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#local" rel="noopener noreferrer"&gt;Local&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#naming-convetions" rel="noopener noreferrer"&gt;naming convetions:&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This project is a starting point for your medium to large scale projects with Nextjs, to make sure having a structured, maintainable and scalable foundation for your Next.js project based on best practices in clean architecture, DDD approach for business logics, MVVM for the frontend part, storybook and vitest for testing and, localization and also functional programming with error handling for business logics.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Motivation&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Next.js and many other modern SSR frameworks provide powerful tools and a fresh approach to frontend development. However, since these tools are relatively new, the focus has largely been on features rather than software engineering best practices.&lt;/p&gt;
&lt;p&gt;As a result, many teams use Next.js for its capabilities but neglect maintainability, architecture, and scalability—especially in medium to large-scale applications…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Also to know more about higher level architecture in Next.js applications. please take a look at &lt;a href="https://dev.to/behnamrhp/stop-spaghetti-code-how-clean-architecture-saves-nextjs-projects-4l18"&gt;clean architecture in Next.js&lt;/a&gt; article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;A major issue with most localization libraries is their reliance on hardcoded string keys throughout the codebase. This forces developers to manually track and update every language dictionary file whenever keys change - a tedious and error-prone process with no type safety or automation.&lt;/p&gt;

&lt;p&gt;This document provides guidelines for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File organization: Structure localization files so changes to any key trigger errors across all language resources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Key management: Replace hardcoded strings with typed objects to eliminate manual refactoring when keys change&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Possible to work with error handling&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Guideline for adding new key
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Define namespace&lt;/strong&gt;: at first based on the domains which we talked about it in &lt;a href="///catalog/docs/clean-architecture/clean-architecture.md"&gt;this article&lt;/a&gt; we make a folder.&lt;br&gt;
Main folder: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/tree/develop/src/bootstrap/i18n/dictionaries" rel="noopener noreferrer"&gt;common&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add langkey and namespace name for the domain&lt;/strong&gt;: langKey is the single source for the languages in that domain which all dictionaries will use it. As we use this source in failure and params message in domain layer we put them in feature layer but dictionaries will remain in bootstrap.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * main language keys which will be used for translation to avoid using strings directly and be
 *  a single source of truth in all changes between all languages dictionaries.
 * All languages dictionaries should have the same keys by having this object type.
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commonLangKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;global.home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;global.dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;global.loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;global.required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;passwordMinLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;global.passwordMinLength&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="na"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failure.network&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;param&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failure.param&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="na"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;createButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dashboard.invoice.createButton&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commonLangNs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;common&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;commonLangKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Main file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/common/lang-keys/common.lang-key.ts" rel="noopener noreferrer"&gt;common-lang-key.ts&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;note: Make sure the namespace name is the same as folder name in dictionaries to be able import them by this namespace name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dictionaries&lt;/strong&gt;: Add related dictionaries for the domain in bootstrap layer.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// en.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;commonLangKey&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/feature/common/lang-keys/common.lang-key&lt;/span&gt;&lt;span class="dl"&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;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;commonLangKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{{field}} is Required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;passwordMinLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password length should be at least 8 characters!&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="na"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;createButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create random Invoice&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="na"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Something went wrong please try again layer!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;param&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide correct information&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Main file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/bootstrap/i18n/dictionaries/common/en.ts" rel="noopener noreferrer"&gt;en.ts&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;note: as we use language enum to define languages make sure the langs are the same as language enum in &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/bootstrap/i18n/i18n.ts" rel="noopener noreferrer"&gt;this file&lt;/a&gt; to be able import the dictionaries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Add your new key to langKey.ts to proper domain.&lt;/li&gt;
&lt;li&gt;Add translation for new key in en.ts and ru.ts in the same domain as in the langKey.ts. en.ts and ru.ts objects are connected to source object, so typescript will show an error if new key added or old key was changed. Also this connections helps us to avoid code become brittle since typescript will show an error in places of usage of translation keys if they were changed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;langKey.ts&lt;br&gt;
&lt;/p&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;langKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;keyWithoutSpecificDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;global.keyWithoutSpecificDomain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;sampleKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sampleDomain.sampleKey&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;langKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ru.ts&lt;br&gt;
&lt;/p&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;ru&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;langKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;keyWithoutSpecificDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Строка без домена&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;sampleKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Строка с доменом&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;en.ts&lt;br&gt;
&lt;/p&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;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;langKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;keyWithoutSpecificDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;String without domain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;sampleKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;String with domain&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Folder acrhitecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└── src/
    ├── bootstrap/
    │   └── i18n/
    │       ├── i18n.ts
    │       └── dictionaries/
    │           └── common/
    │               ├── en.ts
    │               └── ru.ts
    └── feature/
        └── common/
            └── lang-keys/
                ├── common.lang-keys.ts
                └── user.lang-key.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;i18n.ts&lt;/strong&gt; configuring i18next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;langKey.ts&lt;/strong&gt; contains object of all possible keys for translation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ru.ts&lt;/strong&gt; object of russian keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;en.ts&lt;/strong&gt; object of english keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Before usage i18next's translation hook, it must be configured with languages it should support.&lt;/p&gt;

&lt;p&gt;example of initialization&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;i18nInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;LANGS&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;EN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;RU&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ru&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nameSpaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;commonLangNs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userLangNs&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getI18n&lt;/span&gt; &lt;span class="o"&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;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LANGS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&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;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&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="nx"&gt;i18nInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isInitialized&lt;/span&gt;&lt;span class="p"&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;i18nInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changeLanguage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lng&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;i18n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i18nInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i18nInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourceStore&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="na"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nameSpaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;defaultNs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;commonLangNs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i18nInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;t&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;i18nInstance&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initReactI18next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;resourcesToBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LANGS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./dictionaries/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;namespace&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="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.ts`&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="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;getOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nameSpaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;defaultNS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;commonLangNs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resources&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="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&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;i18nInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&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;i18n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i18nInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i18nInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourceStore&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="na"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i18nInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;t&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getServerTranslation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LANGS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;keyPrefix&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;i18n&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getI18n&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// console.log(i18n);&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;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFixedT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&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="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;keyPrefix&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i18nInstance&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;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/bootstrap/i18n/i18n.ts" rel="noopener noreferrer"&gt;Main file&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Provider and client support
&lt;/h2&gt;

&lt;p&gt;To support client side translations with once loaded data in the server we can use provider component like &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/bootstrap/i18n/i18n-provider.tsx" rel="noopener noreferrer"&gt;this file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then we can use this provider in one main layout of the app like &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/app/%5Blang%5D/layout.tsx" rel="noopener noreferrer"&gt;this file&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combination with failure handling and params validation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For combination with failure hanlding, please check &lt;a href="https://dev.to/behnamrhp/how-to-automate-failure-handling-and-messages-with-this-powerful-architecture-i4j"&gt;this article&lt;/a&gt; or &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/catalog/docs/failure-error-handling/failure-error-handling.md" rel="noopener noreferrer"&gt;this document&lt;/a&gt; in the boilerplate&lt;/li&gt;
&lt;li&gt;Also for combination with params please check the &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/catalog/docs/feature-layer/feature-layer-architecture.md" rel="noopener noreferrer"&gt;feature layer architecture&lt;/a&gt; article.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;By implementing this structured approach to localization, we've solved three critical pain points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type-safe translations - No more hardcoded string keys&lt;/li&gt;
&lt;li&gt;Automated synchronization - Changes propagate across all language files&lt;/li&gt;
&lt;li&gt;Scalable architecture - Clean separation between domains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This system integrates seamlessly with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Failure handling&lt;/li&gt;
&lt;li&gt;Param validation&lt;/li&gt;
&lt;li&gt;Next.js server/client components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you found this article helpful, I’d be truly grateful if you could:&lt;br&gt;
⭐ Star the repository to support its visibility&lt;br&gt;
💬 Share your thoughts in the comments—your feedback helps improve content reach&lt;/p&gt;

&lt;p&gt;Every interaction helps these best practices reach more developers!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>nextjs</category>
      <category>react</category>
      <category>localization</category>
    </item>
    <item>
      <title>Why Next.js Apps Struggle at Scale (And How Feature Layers Solve It)</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Sat, 03 May 2025 09:50:11 +0000</pubDate>
      <link>https://dev.to/behnamrhp/why-nextjs-apps-struggle-at-scale-and-how-feature-layers-solve-it-3d9c</link>
      <guid>https://dev.to/behnamrhp/why-nextjs-apps-struggle-at-scale-and-how-feature-layers-solve-it-3d9c</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Architecture overview of the feature layer&lt;/li&gt;
&lt;li&gt;Class diagram of feature layer&lt;/li&gt;
&lt;li&gt;
Layers responsibilities

&lt;ul&gt;
&lt;li&gt;
Data layer

&lt;ul&gt;
&lt;li&gt;
Data transfer object (DTO) / Mapper
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Repository&lt;/li&gt;

&lt;li&gt;

Domain layer

&lt;ul&gt;
&lt;li&gt;Entity&lt;/li&gt;
&lt;li&gt;Enums&lt;/li&gt;
&lt;li&gt;IRepo&lt;/li&gt;
&lt;li&gt;Usecase&lt;/li&gt;
&lt;li&gt;Params&lt;/li&gt;
&lt;li&gt;BaseFailure&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;The usage of functional programming in feature layer&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture overview of the feature layer
&lt;/h2&gt;

&lt;p&gt;Business logic in applications is one of the most critical components, playing a major role in a project's maintainability and scalability. Unfortunately, in many large-scale frontend applications, mixing this logic with UI-related code severely compromises maintainability. In this article, we introduce a dedicated feature layer,a separate architectural tier for isolating business logic in medium-to-large projects. We’ll also explore how to organize this layer’s components (data/domain) and its folder structure using basic DDD principles.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article based on Next.js boilerplate repository.&lt;br&gt;
To explore all concepts in depth and see a production-ready boilerplate and following these best practices, visit:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/behnamrhp" rel="noopener noreferrer"&gt;
        behnamrhp
      &lt;/a&gt; / &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;
        Next-clean-boilerplate
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Full featured nextjs boilerplate, based on clean architecture, mvvm and functional programming paradigm.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Nextjs clean architecture boilerplate&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of content&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#nextjs-clean-architecture-boilerplate" rel="noopener noreferrer"&gt;Nextjs clean architecture boilerplate&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#table-of-content" rel="noopener noreferrer"&gt;Table of content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#overview" rel="noopener noreferrer"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#motivation" rel="noopener noreferrer"&gt;Motivation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#technologies" rel="noopener noreferrer"&gt;Technologies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#architecture" rel="noopener noreferrer"&gt;Architecture&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#folder-structure" rel="noopener noreferrer"&gt;Folder structure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#requirements" rel="noopener noreferrer"&gt;Requirements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#local" rel="noopener noreferrer"&gt;Local&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#naming-convetions" rel="noopener noreferrer"&gt;naming convetions:&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This project is a starting point for your medium to large scale projects with Nextjs, to make sure having a structured, maintainable and scalable foundation for your Next.js project based on best practices in clean architecture, DDD approach for business logics, MVVM for the frontend part, storybook and vitest for testing and, localization and also functional programming with error handling for business logics.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Motivation&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Next.js and many other modern SSR frameworks provide powerful tools and a fresh approach to frontend development. However, since these tools are relatively new, the focus has largely been on features rather than software engineering best practices.&lt;/p&gt;
&lt;p&gt;As a result, many teams use Next.js for its capabilities but neglect maintainability, architecture, and scalability—especially in medium to large-scale applications…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Also to know more about higher level architecture in Next.js applications. please take a look at &lt;a href="https://dev.to/behnamrhp/stop-spaghetti-code-how-clean-architecture-saves-nextjs-projects-4l18"&gt;clean architecture in Next.js&lt;/a&gt; article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As we mentioned this layer handles the business logic of each feature. For each feature this layer is divided into two parts, &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;domain&lt;/code&gt;. where the data is divided into:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;mapper&lt;/li&gt;
&lt;li&gt;repository&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;while the domain is divided into;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;usecase&lt;/li&gt;
&lt;li&gt;entity&lt;/li&gt;
&lt;li&gt;failure&lt;/li&gt;
&lt;li&gt;repository interface&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Class diagram of feature layer
&lt;/h2&gt;

&lt;p&gt;The following class diagram shows the relation between domain and data inside feature layer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.plantuml.com%2Fplantuml%2Fdpng%2FXL31QiCm33t7N-5ZEsW-88pG7NBOOPUIVO18jSqqiOsbm4ANVvzhrQtffO48-prBJ-_jYI7mF8tAmt22RzH7Du6n-gogZCo40n16ICTl6858VvPOfV4NXvbnisrq3tJg3FzWUEqi6rxQrBQgon-BiOpX2mRKxmVqZWoxiCXe_MIEJ55v1u7F5mpjFEak5afNuJq44E2sxcxNNjiYA8U4fTQ7TzAkujBtR82X22QWTsKeTpywgYQYWEhwhoqz9puBljJr8xcgAjQRD2Sf4Ve0xp7aqC2RtecPJK3opX8sX-kXNpnk6s5poEdIkpTldhvVLU51iXBnnArgZ_OuJ-C_" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.plantuml.com%2Fplantuml%2Fdpng%2FXL31QiCm33t7N-5ZEsW-88pG7NBOOPUIVO18jSqqiOsbm4ANVvzhrQtffO48-prBJ-_jYI7mF8tAmt22RzH7Du6n-gogZCo40n16ICTl6858VvPOfV4NXvbnisrq3tJg3FzWUEqi6rxQrBQgon-BiOpX2mRKxmVqZWoxiCXe_MIEJ55v1u7F5mpjFEak5afNuJq44E2sxcxNNjiYA8U4fTQ7TzAkujBtR82X22QWTsKeTpywgYQYWEhwhoqz9puBljJr8xcgAjQRD2Sf4Ve0xp7aqC2RtecPJK3opX8sX-kXNpnk6s5poEdIkpTldhvVLU51iXBnnArgZ_OuJ-C_" alt="Feature layer class diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Layers responsibilities
&lt;/h2&gt;

&lt;p&gt;feature layer responsibilities can be divided into data layer responsibilities and domain layer responsibilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data layer
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Data transfer object (DTO) / Mapper:
&lt;/h4&gt;

&lt;p&gt;Data transfer object (DTO) are commonly used within the repository layer to convert data fetched from external sources (such as databases or APIs) into domain entities, and vice versa. This conversion ensures that the domain entities remain agnostic of the underlying data storage mechanisms and allows for easier integration with different data sources without affecting the core business logic. for example let's imagine that the response type when a book will be created is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserMapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;toApiRole&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApiRole&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="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;ApiRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;ApiRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DESIGNER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MANAGER&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;ApiRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MANAGER&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="k"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;toEntityRole&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ApiRole&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Role&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="nx"&gt;ApiRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ApiRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DESIGNER&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ApiRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MANAGER&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MANAGER&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="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;mapToEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WithPaginationResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserResponse&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="nx"&gt;WithPagination&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserParams&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;try&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;usersEntity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toEntityRole&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display_name&lt;/span&gt; &lt;span class="o"&gt;??&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="nf"&gt;toPlainObject&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WithPagination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usersEntity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toPlainObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;mapToCreateParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateUserParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;CreateUserApiParams&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;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;display_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toApiRole&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&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="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;mapToUpdateParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UpdateUserParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;UpdateUserApiParams&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;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;display_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toApiRole&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Main file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/data/repository/user.mapper.ts" rel="noopener noreferrer"&gt;user.mapper.ts&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Repository:
&lt;/h4&gt;

&lt;p&gt;The repository acts as an adapter pattern between the use case and external resource. Also transforms entity-shaped data into a format compatible with the data source. This bidirectional transformation is achieved through the use of data transfer objects (DTOs), which we call mapper in this boilerplate. Additionally, repositories can interact with each other within the same layer, enabling seamless communication between sibling repositories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepositoryImpl&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BackendEndpoint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;fetchHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FetchHandler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&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;di&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;serverDi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userModuleKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;di&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FetchHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;di&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BackendEndpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UpdateUserParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ApiTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kc"&gt;true&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="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FetchOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UpdateUserApiParams&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="na"&gt;endpoint&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&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="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserMapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mapToUpdateParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchWithAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;ApiTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kc"&gt;true&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="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FetchOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&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="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DELETE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;Content-Type&lt;/span&gt;&lt;span class="dl"&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;application/json&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ids&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="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchWithAuth&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&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="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Main file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/data/repository/user.repository.ts" rel="noopener noreferrer"&gt;user repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see in this repository class we imported backend endpoints and fetch handler boundary, and the repository knows how to work with these dependencies to connect to the third party apis.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To know more about endpoint architecture and di in Next.js applications use these two other articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/behnamrhp/how-we-fixed-nextjs-at-scale-di-clean-architecture-secrets-from-production-gnj"&gt;How We Fixed Next.js at Scale: DI &amp;amp; Clean Architecture Secrets From Production&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/behnamrhp/a-simple-way-to-organize-api-endpoints-like-a-senior-cho"&gt;A Simple Way to Organize API Endpoints Like a Senior&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Domain layer
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Entity:
&lt;/h4&gt;

&lt;p&gt;Entity is the business object that contains business rules. For example a Book entity can be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;autherName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BookStatus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autherName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;autherName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;autherName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&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;blockquote&gt;
&lt;p&gt;note: BookStatus is an enum implemented in the same layer with entity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Enums:
&lt;/h4&gt;

&lt;p&gt;Enums are used to define a fixed set of values for specific properties within an entity. They ensure type safety and provide clarity on the possible states or options for those properties. for example if there is prop inside &lt;code&gt;Book&lt;/code&gt; entity that shows the status of the book, if the book is &lt;code&gt;published&lt;/code&gt;, &lt;code&gt;sent&lt;/code&gt;, &lt;code&gt;draft&lt;/code&gt;, an enum can be added as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;BookStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;PUBLISHED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;published&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;SENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DRAFT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;draft&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;BookStatus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  IRepo:
&lt;/h4&gt;

&lt;p&gt;The repository interface defines a contract for data access operations, abstracting the interaction between the application's domain logic and the underlying data storage mechanisms. for example an IRepo to get a book can be implmented as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IGetBookRepository&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;TaskEither&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Usecase:
&lt;/h4&gt;

&lt;p&gt;Usecase represents the main business logic of the application and orchestrates the overall process flow. It interacts with entities and communicates with external systems through repositories (IRepository). Usecase can connect to its siblings in the same layer, other usecases for example, the following diagram illustrates how usecase and repositories can connect to other ones in the same layer:&lt;/p&gt;

&lt;h4&gt;
  
  
  Params:
&lt;/h4&gt;

&lt;p&gt;Params serve as structured schemas containing input parameters crucial for use case execution. These schemas encompass business rules and validations that necessitate careful handling during processing. Their primary function is to facilitate modularization and decoupling within the application architecture, offering a standardized interface for seamless communication between different layers. For example if it is required to validate the params of the book, the &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;authorName&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt; should be validated, the team is formulating the validation terms, for example the team agreed on the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;title: it should not be undefined&lt;/li&gt;
&lt;li&gt;authername: it can be undefined&lt;/li&gt;
&lt;li&gt;status: it should be defined
so the parms class will have two methods, the first one to validate each param separately, the second one to validate all the params at once:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;bookCreationParamsValidator&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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;autherName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;required&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="cm"&gt;/**
   * Schema for all parameters.
   * You can use it to validate or cast or use its type
   */&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;schema&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="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bookCreationParamsValidator&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;&lt;code&gt;required&lt;/code&gt; word means that the param should be defined, &lt;code&gt;bookCreationParamsValidator&lt;/code&gt; can be used to validate a param while &lt;code&gt;schema&lt;/code&gt; is used to validate all params.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;note: In the boilerplate we used &lt;a href="https://zod.dev/" rel="noopener noreferrer"&gt;zod&lt;/a&gt; but you can use any other similare libraries for this part.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  BaseFailure:
&lt;/h4&gt;

&lt;p&gt;BaseFailure mechanisms in the application serve as critical pointer of errors or exceptional conditions encountered during execution. By sticking to functional programming principles, errors are consistently represented on the left side, ensuring clear and predictable error handling pathways. The base failure, represented by the Base Failure class, plays an important role in this process. By extending the Error class and providing a standardized failure message, it serves as a foundational element for creating custom failure classes made specifically to specific error scenarios. These custom failure classes allow for precise and reliable error signaling, enabling straight error handling workflows. Additionally, the use of failure keys facilitates automated error handling processes, enhancing efficiency and robustness. Overall, the integration of failure mechanisms aligns straightforward with the application's functional programming paradigm, ensuring comprehensive error management and enhancing code reliability.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;note: during the implementation, BaseFailure is the left side of the reusable type &lt;code&gt;ApiTask&lt;/code&gt; which is based on &lt;code&gt;TaskEither&lt;/code&gt; from &lt;code&gt;fp-ts&lt;/code&gt; library. for example to make a failure for a problem in the network, it can be implemented as follows:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NetworkFailure&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseFailure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;network&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;to get more familiar with failure, take a look at this article &lt;a href="https://dev.to/behnamrhp/how-to-automate-failure-handling-and-messages-with-this-powerful-architecture-i4j"&gt;How to Automate Failure Handling and Messages with This Design&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The usage of functional programming in feature layer
&lt;/h2&gt;

&lt;p&gt;Functional programming plays a crucial role in our feature layer, providing a structured and efficient approach to managing connections between different layers of our system. Beyond its traditional benefits, functional programming enables robust error handling and manipulation throughout the application. By following to functional principles, we ensure that each process within our system is handled independently and comprehensively, enhancing clarity and maintainability.&lt;br&gt;
Through functional programming, we can easily propagate return types across layers, enabling clear communication and transparent data flow. This approach not only enhances code organization and readability but also promote modularization and decoupling, leading to a more maintainable and scalable architecture.&lt;br&gt;
We leverage the &lt;a href="https://gcanti.github.io/fp-ts/" rel="noopener noreferrer"&gt;fp-ts&lt;/a&gt; library for functional programming in TypeScript, which provides a rich set of abstractions and utilities for working with functional concepts. For more detailed information on functional programming concepts, see &lt;a href="https://gcanti.github.io/fp-ts/modules/" rel="noopener noreferrer"&gt;Functional programming documentation&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The Feature Layer is the powerhouse of your Next.js app, cleanly separating business logic (domain) from data handling while leveraging functional programming for robust, scalable code. By structuring entities, use cases, and repositories with clear boundaries, you ensure:&lt;/p&gt;

&lt;p&gt;✅ Maintainability – Business rules stay pure and testable.&lt;br&gt;
✅ Flexibility – Swap data sources without breaking logic.&lt;br&gt;
✅ Error Resilience – Functional programming (via fp-ts) streamlines error handling.&lt;/p&gt;

&lt;p&gt;If you found this article helpful, I’d be truly grateful if you could:&lt;br&gt;
⭐ Star the repository to support its visibility&lt;br&gt;
💬 Share your thoughts in the comments—your feedback helps improve content reach&lt;/p&gt;

&lt;p&gt;Every interaction helps these best practices reach more developers!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Production-Proven Clean Architecture in Next.js: A Practical Guide</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Fri, 02 May 2025 15:42:46 +0000</pubDate>
      <link>https://dev.to/behnamrhp/stop-spaghetti-code-how-clean-architecture-saves-nextjs-projects-4l18</link>
      <guid>https://dev.to/behnamrhp/stop-spaghetti-code-how-clean-architecture-saves-nextjs-projects-4l18</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Clean Architecture Overview&lt;/li&gt;
&lt;li&gt;Why did We choose Clean Architecture?&lt;/li&gt;
&lt;li&gt;Project clean architecture diagram&lt;/li&gt;
&lt;li&gt;
Layers breaking down

&lt;ul&gt;
&lt;li&gt;Application Layer&lt;/li&gt;
&lt;li&gt;
Feature Layer

&lt;ul&gt;
&lt;li&gt;Domain&lt;/li&gt;
&lt;li&gt;Data&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Bootstrap layer&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Layers Connection&lt;/li&gt;

&lt;li&gt;

Folder structuring

&lt;ul&gt;
&lt;li&gt;DDD aproach in folder structuring&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Conclusion
&lt;h2&gt;
  
  
  Clean Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Clean Architecture is a software design philosophy that promotes structuring of large scale systems with a strong emphasis on separation of concerns and dependency inversion. The main goal is to create systems that are highly maintainable, scalable, and testable, while ensuring business logic remains isolated and independent of external frameworks and tools.&lt;/p&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flu83l3grmorszz82jfbw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flu83l3grmorszz82jfbw.jpg" alt="cleanarchitecture.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: for reading more information about clean architecture visit this &lt;a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why did We choose Clean Architecture?
&lt;/h2&gt;

&lt;p&gt;For large-scale projects that contains multiple domains characterized by tight coupling between them. Furthermore, these domains can be extended with new features without any predicted limitations. &lt;/p&gt;

&lt;p&gt;Therefore, we require an architecture that can support, scalability, single responsibility, maintainability, testability and quality assurance. Also we need a shared language for our big system which is understandable among the team members.&lt;/p&gt;

&lt;p&gt;Implementing a clean architecture, customized to accommodate frontend-specific features, was the optimal solution for this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project clean architecture diagram
&lt;/h2&gt;

&lt;p&gt;As we discussed we used customized version of clean architecture based on nextjs-specific features.&lt;/p&gt;

&lt;p&gt;&lt;a href="//www.plantuml.com/plantuml/png/ZL1Ton915BwVNt6SNYY91UiY_11HB4b8aABkJpDdRN1spZ0xYqdutvlPZDNC5s-MtNs-pxoE669JML7lZVCOi89527puIAYNR3c0OqT2l8IYA63LE1KDkneQhp6IXiXGiQRWGBNi-kWY_inaqIlFR7X2xwrHc3zMbW-ldh0lUe5YatHoBE5regI7dC1q-c8q_3xGRsOpANhaz5yJSUcdkKBpW5KvLANIprjXn0Sw3OX6WrkZyTUQmaG8Zo6Qrd3HJOLV0cwtootkIFR_2tqpTBW7RUu0ItOniBKK4k5qMML5cDPGMwFx-pxVxaytkBgaJ5iKrhdR3VJ19VNnZ8pNnMVoN9l8uNXrvmIVn7KQHZndTQUMdLps_yKsw7tUahtodfB3NgJGnNRTPj5R_Ow6RPQcYEf7om3SeN6PzuaLMlWIEU2ExYZ8QSWg8URfjQdiRm00" class="article-body-image-wrapper"&gt;&lt;img src="//www.plantuml.com/plantuml/png/ZL1Ton915BwVNt6SNYY91UiY_11HB4b8aABkJpDdRN1spZ0xYqdutvlPZDNC5s-MtNs-pxoE669JML7lZVCOi89527puIAYNR3c0OqT2l8IYA63LE1KDkneQhp6IXiXGiQRWGBNi-kWY_inaqIlFR7X2xwrHc3zMbW-ldh0lUe5YatHoBE5regI7dC1q-c8q_3xGRsOpANhaz5yJSUcdkKBpW5KvLANIprjXn0Sw3OX6WrkZyTUQmaG8Zo6Qrd3HJOLV0cwtootkIFR_2tqpTBW7RUu0ItOniBKK4k5qMML5cDPGMwFx-pxVxaytkBgaJ5iKrhdR3VJ19VNnZ8pNnMVoN9l8uNXrvmIVn7KQHZndTQUMdLps_yKsw7tUahtodfB3NgJGnNRTPj5R_Ow6RPQcYEf7om3SeN6PzuaLMlWIEU2ExYZ8QSWg8URfjQdiRm00" alt="Project architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article based on Next.js boilerplate repository.&lt;br&gt;
To explore all concepts in depth and see a production-ready boilerplate and following these best practices, visit:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/behnamrhp" rel="noopener noreferrer"&gt;
        behnamrhp
      &lt;/a&gt; / &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;
        Next-clean-boilerplate
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Full featured nextjs boilerplate, based on clean architecture, mvvm and functional programming paradigm.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Nextjs clean architecture boilerplate&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of content&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#nextjs-clean-architecture-boilerplate" rel="noopener noreferrer"&gt;Nextjs clean architecture boilerplate&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#table-of-content" rel="noopener noreferrer"&gt;Table of content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#overview" rel="noopener noreferrer"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#motivation" rel="noopener noreferrer"&gt;Motivation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#technologies" rel="noopener noreferrer"&gt;Technologies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#architecture" rel="noopener noreferrer"&gt;Architecture&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#folder-structure" rel="noopener noreferrer"&gt;Folder structure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#requirements" rel="noopener noreferrer"&gt;Requirements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#local" rel="noopener noreferrer"&gt;Local&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#naming-convetions" rel="noopener noreferrer"&gt;naming convetions:&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This project is a starting point for your medium to large scale projects with Nextjs, to make sure having a structured, maintainable and scalable foundation for your Next.js project based on best practices in clean architecture, DDD approach for business logics, MVVM for the frontend part, storybook and vitest for testing and, localization and also functional programming with error handling for business logics.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Motivation&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Next.js and many other modern SSR frameworks provide powerful tools and a fresh approach to frontend development. However, since these tools are relatively new, the focus has largely been on features rather than software engineering best practices.&lt;/p&gt;
&lt;p&gt;As a result, many teams use Next.js for its capabilities but neglect maintainability, architecture, and scalability—especially in medium to large-scale applications…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Layers breaking down
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Application Layer
&lt;/h3&gt;

&lt;p&gt;This layer is responsible for handling framework and tools, also it'll connect directly to the user. &lt;/p&gt;

&lt;p&gt;This Layer is based on &lt;a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel" rel="noopener noreferrer"&gt;MVVM (Model-ViewModel-View)&lt;/a&gt; Architecture.&lt;/p&gt;

&lt;p&gt;This architecture Helps us separate the business logics (controller), UI logics (ViewModel) and UI (View). &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To decrease the size of the article we separated application layer document to a separated document.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For application layer concepts visit this doc and also article which we mentioned inside of it to get master at MVVM in react based projects:&lt;br&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/catalog/docs/mvvm/application-layer-architecture.md" rel="noopener noreferrer"&gt;application layer architecture&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature Layer
&lt;/h3&gt;

&lt;p&gt;This layer responsibles for just business logics.&lt;/p&gt;

&lt;h4&gt;
  
  
  Domain
&lt;/h4&gt;

&lt;p&gt;This layer is a part of Feature layer which is the heart of our app and it just care about the main business logics of the app.&lt;br&gt;
So This layer is independent of any specific technology or framework and represent the fundamental concepts and rules of the business domain.&lt;br&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/tree/develop/src/feature/core/user/domain" rel="noopener noreferrer"&gt;User domain layer in the project&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This layer contains three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Entity: Is our business object and contains our business rules.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Example: `User` entity represents:
    - `id`
    - `firstName`
    - `lastname`
    - Also some business object logics like `getFullName`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example Code in the project: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/domain/entity/user.entity.ts" rel="noopener noreferrer"&gt;user.entity.ts&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Usecase: The Use Case represents the main business logic of the application and orchestrates the overall process flow. It interacts with entities and communicates with external systems through repositories (IRepository).&lt;br&gt;
In this scenario, the primary benefit is the separation of core business logic from external frameworks and third-party dependencies, ensuring encapsulation and maintainability of the application.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Example:
In an e-commerce application:
`PlaceOrderUseCase`:
Manages the process of placing an order, including validation, inventory management, and order creation.
Utilizes IProductRepository to retrieve and update product information without directly coupling to the database or external APIs.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example code in the porject: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/domain/usecase/create-user.usecase.ts" rel="noopener noreferrer"&gt;create-user.usecase.ts&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I-Repository: It's an interface to define a contract for data access operations within the application. It abstracts away the details of specific data storage implementations (such as databases or external services) from the usecase.&lt;br&gt;
Example code in the porject: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/domain/i-repo/user.repository.interface.ts" rel="noopener noreferrer"&gt;user.repository.interface.ts&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So our usecase knows how to communicate with outside of the app through I-Repository.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is exactly the adapter pattern which our repository will work as our adapter in our project. To learn more about adapter pattern check this &lt;a href="https://refactoring.guru/design-patterns/adapter" rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Data
&lt;/h4&gt;

&lt;p&gt;This layer is responsible for interfacing with external systems and services, adapting their data formats and logic into business entities that the application can use.&lt;br&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/tree/develop/src/feature/core/user/data" rel="noopener noreferrer"&gt;user data layer in the project&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Repository: It implements the &lt;code&gt;IRepository&lt;/code&gt; interface defined in the domain layer. It serves as a bridge for managing data format adaptation between the application's business entities and external data sources. In essence, the Repository acts as an adapter between use cases and external services.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example code in the project: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/data/repository/user.repository.ts" rel="noopener noreferrer"&gt;user.repository.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This repository uses &lt;code&gt;Mapper&lt;/code&gt; to turn data format between application business entities and datasources. &lt;br&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/data/repository/user.mapper.ts" rel="noopener noreferrer"&gt;user.mapper.ts&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: For more details about feature layer, class diagram, code example, testing of this layers, you can visit this document:&lt;br&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/catalog/docs/feature-layer/feature-layer-architecture.md" rel="noopener noreferrer"&gt;feature layer architecture&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Bootstrap layer
&lt;/h3&gt;

&lt;p&gt;Bootstrap layer represents configs and initial configuration of application. It includes DI configuration, localization configuration, configuration for graphql, httpHandled and local storage, base classes for context, view and vm.&lt;/p&gt;

&lt;p&gt;It has these folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;boundary - to implement library boundary.&lt;/li&gt;
&lt;li&gt;config - configuration for application.&lt;/li&gt;
&lt;li&gt;di - DI configuration.&lt;/li&gt;
&lt;li&gt;global-types - typescript types to be used in any place of project.&lt;/li&gt;
&lt;li&gt;helper - place for extract frequently encoutered functions, classes and types.&lt;/li&gt;
&lt;li&gt;i18n - localization configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Layers Connection
&lt;/h2&gt;

&lt;p&gt;For connecting layers we uses DI(dependency-injection) by &lt;a href="https://github.com/microsoft/tsyringe" rel="noopener noreferrer"&gt;tsyringe&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Fore more details about DI, Structures and usage examples, please visit DI documentation from &lt;a href="https://dev.to/behnamrhp/how-we-fixed-nextjs-at-scale-di-clean-architecture-secrets-from-production-gnj"&gt;Clean Architecture Layering in Next.js with DI&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Folder structuring
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;└── src/
    ├── app/
    │   └── users/
    │       ├── controllers
    │       ├── view/
    │       │   ├── server
    │       │   └── client/
    │       │       └── some-component/
    │       │           ├── some-component.view.tsx
    │       │           ├── stories
    │       │           └── style/
    │       │               └── i-vm
    │       ├── page
    │       └── vm
    ├── feature/
    │   ├── core/
    │   │   └── users/
    │   │       ├── data/
    │   │       │   ├── repository
    │   │       │   └── mapper
    │   │       └── domain/
    │   │           ├── failure
    │   │           ├── i-repository
    │   │           ├── entity
    │   │           └── usecase
    │   ├── support
    │   └── generic
    ├── bootstrap/
    │   ├── boundary
    │   ├── config/
    │   │   ├── server
    │   │   └── client
    │   ├── di
    │   ├── endpoint
    │   ├── helper
    │   └── i18n
    └── test/
        ├── common
        ├── unit
        └── e2e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DDD aproach in folder structuring
&lt;/h3&gt;

&lt;p&gt;For folder structuting we can following Domain Driven Design approach. A domain is a subject area that describes a set of business problems and goals. We have 3 domains in our project&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;core - all the features that are related to the main purpose of the project.&lt;/li&gt;
&lt;li&gt;generic - reusable logic to use in any place from project.&lt;/li&gt;
&lt;li&gt;support - logic that helps to perform logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each one of them has subdomains for separate feature.&lt;/p&gt;

&lt;p&gt;For example for buisness layer of User feature we will go inside of &lt;code&gt;src/features/core/user&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Clean Architecture in Next.js ensures a scalable, maintainable, and testable codebase by separating concerns into distinct layers: Application (UI), Feature (business logic), and Bootstrap (configurations).&lt;/p&gt;

&lt;p&gt;Why It Works:&lt;br&gt;
Decoupled Logic – Business rules stay independent of frameworks.&lt;/p&gt;

&lt;p&gt;Easy Testing – Isolated layers simplify unit and integration tests.&lt;/p&gt;

&lt;p&gt;Flexible Growth – Features can expand without tight coupling.&lt;/p&gt;

&lt;p&gt;By using Dependency Injection and Domain-Driven Design, teams can collaborate efficiently while keeping the codebase clean and adaptable.&lt;/p&gt;

&lt;p&gt;If you found this article helpful, I’d be truly grateful if you could:&lt;br&gt;
⭐ Star the repository to support its visibility&lt;br&gt;
💬 Share your thoughts in the comments—your feedback helps improve content reach&lt;/p&gt;

&lt;p&gt;Every interaction helps these best practices reach more developers!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Clean Architecture Layering in Next.js with DI</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Fri, 02 May 2025 14:42:17 +0000</pubDate>
      <link>https://dev.to/behnamrhp/how-we-fixed-nextjs-at-scale-di-clean-architecture-secrets-from-production-gnj</link>
      <guid>https://dev.to/behnamrhp/how-we-fixed-nextjs-at-scale-di-clean-architecture-secrets-from-production-gnj</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Di Architecture

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Requirements&lt;/li&gt;
&lt;li&gt;
Usecase

&lt;ul&gt;
&lt;li&gt;Where we Use DI&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Feature layer

&lt;ul&gt;
&lt;li&gt;DI Key: Interface-Based Binding&lt;/li&gt;
&lt;li&gt;Module&lt;/li&gt;
&lt;li&gt;Features di&lt;/li&gt;
&lt;li&gt;Usage&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Application layer

&lt;ul&gt;
&lt;li&gt;vm key&lt;/li&gt;
&lt;li&gt;App module&lt;/li&gt;
&lt;li&gt;Provider&lt;/li&gt;
&lt;li&gt;Usage&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Dependency Injection (DI) is one of the most debated design patterns in software development. While opinions vary on whether it’s universally beneficial, one thing is certain: DI can be extremely useful if not treated as a "golden hammer." Overusing it, such as blindly injecting every dependency, can lead to unnecessary complexity rather than cleaner code.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how DI can be effectively applied in Next.js, striking the right balance between scalability and simplicity and also tree shaking friendly to avoid messing the bundle size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;This article builds on the architecture outlined in the Next.js Boilerplate repository. To fully understand the implementation, I recommend reviewing these two foundational documents first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/catalog/docs/clean-architecture/clean-architecture.md" rel="noopener noreferrer"&gt;Clean Architecture in Next.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/catalog/docs/mvvm/application-layer-architecture.md" rel="noopener noreferrer"&gt;MVVM Architecture in Next.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We've included direct file references to key implementations in the main repository for each example.&lt;/p&gt;

&lt;p&gt;To explore all concepts in depth and see a production-ready boilerplate and following these best practices, visit:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/behnamrhp" rel="noopener noreferrer"&gt;
        behnamrhp
      &lt;/a&gt; / &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;
        Next-clean-boilerplate
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Full featured nextjs boilerplate, based on clean architecture, mvvm and functional programming paradigm.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Nextjs clean architecture boilerplate&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of content&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#nextjs-clean-architecture-boilerplate" rel="noopener noreferrer"&gt;Nextjs clean architecture boilerplate&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#table-of-content" rel="noopener noreferrer"&gt;Table of content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#overview" rel="noopener noreferrer"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#motivation" rel="noopener noreferrer"&gt;Motivation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#technologies" rel="noopener noreferrer"&gt;Technologies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#architecture" rel="noopener noreferrer"&gt;Architecture&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#folder-structure" rel="noopener noreferrer"&gt;Folder structure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#requirements" rel="noopener noreferrer"&gt;Requirements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#local" rel="noopener noreferrer"&gt;Local&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#naming-convetions" rel="noopener noreferrer"&gt;naming convetions:&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This project is a starting point for your medium to large scale projects with Nextjs, to make sure having a structured, maintainable and scalable foundation for your Next.js project based on best practices in clean architecture, DDD approach for business logics, MVVM for the frontend part, storybook and vitest for testing and, localization and also functional programming with error handling for business logics.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Motivation&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Next.js and many other modern SSR frameworks provide powerful tools and a fresh approach to frontend development. However, since these tools are relatively new, the focus has largely been on features rather than software engineering best practices.&lt;/p&gt;
&lt;p&gt;As a result, many teams use Next.js for its capabilities but neglect maintainability, architecture, and scalability—especially in medium to large-scale applications…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Usecase
&lt;/h2&gt;

&lt;p&gt;In Next.js applications, we organize the project into two primary layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature Layer – Contains all business logic.&lt;/li&gt;
&lt;li&gt;Application Layer – Handles communication with Next.js and React APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where we Use DI
&lt;/h3&gt;

&lt;p&gt;We apply DI only between components that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require minimal knowledge of each other (loose coupling).&lt;/li&gt;
&lt;li&gt;Can work independently but integrate seamlessly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We apply dependency Injection (DI) in different ways in each layer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Feature Layer

&lt;ul&gt;
&lt;li&gt;Usage: DI connects &lt;code&gt;UseCase&lt;/code&gt; and &lt;code&gt;Repository&lt;/code&gt; via interfaces, following the &lt;code&gt;Adapter Pattern&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Purpose: Ensures business logic remains decoupled from data sources (e.g., databases, APIs).&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Application Layer

&lt;ul&gt;
&lt;li&gt;Usage: DI bridges &lt;code&gt;ViewModel (VM)&lt;/code&gt; and &lt;code&gt;View&lt;/code&gt;, adhering to the &lt;code&gt;Bridge Pattern&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Special Case: When passing a VM from a &lt;code&gt;Server Component&lt;/code&gt; to a &lt;code&gt;Client Component&lt;/code&gt;, we use a unique &lt;code&gt;VM key&lt;/code&gt; for serialization.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Also global and other dependencies.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Feature layer
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, we use Dependency Injection (DI) in the Feature Layer to connect the &lt;code&gt;UseCase&lt;/code&gt; and &lt;code&gt;Repository&lt;/code&gt; layers via interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  DI Key: Interface-Based Binding
&lt;/h3&gt;

&lt;p&gt;To link a UseCase with its corresponding Repository, we use a unique DI key tied to the repository interface. This ensures type-safe dependency resolution.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateUserParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ApiTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UpdateUserParams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ApiTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kc"&gt;true&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;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;ApiTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kc"&gt;true&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userRepoKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRepoKey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/domain/i-repo/user.repository.interface.ts" rel="noopener noreferrer"&gt;user.repository.interface.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you see in this file we have a &lt;code&gt;userRepositoryKey&lt;/code&gt; Which we use to register a repository and also use it in usecase to use this repository inside of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Module
&lt;/h3&gt;

&lt;p&gt;In Domain-Driven Design (DDD), a Module serves as a high-level organizational unit that groups related domain concepts. For example, all user-related domain logic (entities, repositories, services) would be organized within a User Module, where we also centralize its dependency injection (DI) registrations.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;userModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;di&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DependencyContainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;di&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userRepoKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserRepositoryImpl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;di&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;file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/data/module/user-module.ts" rel="noopener noreferrer"&gt;User module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's examine the key aspects of this file:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Architectural Layer Responsibility&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;This component has visibility into both domain repository interfaces (i-repo) and concrete repository implementations. According to Clean Architecture principles, this dual knowledge properly places it in the &lt;code&gt;Data Layer&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dependency Injection Setup&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;We get a child DI container that registers all domain repositories and dependencies implementation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Features di
&lt;/h3&gt;

&lt;p&gt;To effectively organize, manage, and register all domain modules, we require a centralized component to handle these responsibilities:&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * On adding new domain module, just add it to this list
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moduleKeyToDi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;di&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DependencyContainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;DependencyContainer&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;authModuleKey&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;authModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userModuleKey&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;userModule&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;memoizedDis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DependencyContainer&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;featuresDi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;DependencyContainer&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="nx"&gt;memoizedDis&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;memoizedDis&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;module&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;moduleDiHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;moduleKeyToDi&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;module&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;moduleDiHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server Di didn't found for module: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;module&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moduleDi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;moduleDiHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;di&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createChildContainer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nf"&gt;globalModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;moduleDi&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;memoizedDis&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;moduleDi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;moduleDi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;diResolve&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InjectionToken&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;featuresDi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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;file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/common/features.di.ts" rel="noopener noreferrer"&gt;Feature di&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This implementation includes a method for retrieving domain-specific dependency injection (DI) configurations using &lt;code&gt;unique module keys&lt;/code&gt;. Each domain is assigned a distinct identifier to ensure proper isolation and organization.&lt;br&gt;
For reference, see the user domain domain module key in the &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/data/user-module-key.ts" rel="noopener noreferrer"&gt;user-module-key&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;Also in feature di file by adding new module for new domains we can add new domain module to the list of &lt;code&gt;moduleKeyToDi&lt;/code&gt; object.&lt;/p&gt;
&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;At the end, we can get the repository in usecase layer like this:&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createUserUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateUserParams&lt;/span&gt;&lt;span class="p"&gt;,&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;ApiEither&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;&amp;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;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;diResolve&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userModuleKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userRepoKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&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;file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/domain/usecase/create-user.usecase.ts" rel="noopener noreferrer"&gt;createUserUseCase&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Application layer
&lt;/h2&gt;

&lt;p&gt;Unlike the feature layer, the application layer follows a different architectural approach. Here we implement the MVVM (Model-View-ViewModel) pattern, where Dependency Injection plays a crucial role in passing ViewModels from server components to client-side views.&lt;/p&gt;

&lt;h3&gt;
  
  
  vm key
&lt;/h3&gt;

&lt;p&gt;For a vm we can have a unique key to register the vm by a single unique string&lt;/p&gt;

&lt;h3&gt;
  
  
  App module
&lt;/h3&gt;

&lt;p&gt;In each page or route we can define a module to register all vms and any other parts to our di like this:&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Each page can have its own di to connect all vms, usecases or controllers
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;dashboardAppModule&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;dashboardDi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;di&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createChildContainer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;dashboardDi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;createRandomInvoiceButtonVMKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;CreateRandomInvoiceButtonVM&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="nx"&gt;dashboardDi&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;file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/app/%5Blang%5D/dashboard/module/dashboard.app-module.ts" rel="noopener noreferrer"&gt;Dashboard app module&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider
&lt;/h3&gt;

&lt;p&gt;We can distribute our dependency injection container throughout the component tree by implementing a provider pattern. This enables any component to access registered ViewModels while maintaining proper dependency isolation.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&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;ReactNode&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;di&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dashboardAppModule&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ReactVVMDiProvider&lt;/span&gt; &lt;span class="nx"&gt;diContainer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;di&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex h-screen flex-col md:flex-row md:overflow-hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;w-full flex-none md:w-64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SideNav&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex-grow p-6 md:overflow-y-auto md:p-12&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ReactVVMDiProvider&lt;/span&gt;&lt;span class="err"&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;file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/app/%5Blang%5D/dashboard/layout.tsx" rel="noopener noreferrer"&gt;Dashboard layout&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example as we're using &lt;a href="https://github.com/behnamrhp/React-VVM" rel="noopener noreferrer"&gt;ReactVVM&lt;/a&gt; package, we passed the di to this library's provider so it can connect View to our vm, automatically based on passing Vmkey to the view component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;For a concrete implementation example, let's examine how to pass a ViewModel for random invoice generation to a button component.&lt;/p&gt;

&lt;p&gt;Example:&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LatestInvoices&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;latestInvoices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;latestInvoicesController&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="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex w-full flex-col md:col-span-4"&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;Button&lt;/span&gt; &lt;span class="na"&gt;vmKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createRandomInvoiceButtonVMKey&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="nt"&gt;div&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;file: &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/app/%5Blang%5D/dashboard/components/server/latest-invoices.tsx" rel="noopener noreferrer"&gt;Latest Invoice&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you see we passed the vm key to the view by vmKey prop to the Button component.&lt;/p&gt;

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

&lt;p&gt;In this article, we explored a &lt;code&gt;Clean Architecture&lt;/code&gt; approach using &lt;code&gt;Dependency Injection (DI)&lt;/code&gt; to decouple business logic, applying &lt;code&gt;DDD&lt;/code&gt; principles to connect &lt;code&gt;UseCase&lt;/code&gt; and &lt;code&gt;Repository&lt;/code&gt; layers via interfaces for maintainability and testability.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;Application Layer&lt;/code&gt;, we implemented an &lt;code&gt;MVVM pattern&lt;/code&gt;, using unique VM keys to bridge server and client components while ensuring reusability and single responsibility via the &lt;code&gt;Bridge Pattern&lt;/code&gt;. This keeps UI logic clean and scalable across &lt;code&gt;Next.js&lt;/code&gt; applications.&lt;/p&gt;

&lt;p&gt;By structuring DI registration around domain modules and centralized providers, we achieve a flexible, maintainable architecture that works seamlessly in both server and client contexts. &lt;/p&gt;

&lt;p&gt;If you found this article helpful, I’d be truly grateful if you could:&lt;br&gt;
⭐ Star the repository to support its visibility&lt;br&gt;
💬 Share your thoughts in the comments—your feedback helps improve content reach&lt;/p&gt;

&lt;p&gt;Every interaction helps these best practices reach more developers!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>react</category>
    </item>
    <item>
      <title>A Simple Way to Organize API Endpoints Like a Senior</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Thu, 01 May 2025 17:33:11 +0000</pubDate>
      <link>https://dev.to/behnamrhp/a-simple-way-to-organize-api-endpoints-like-a-senior-cho</link>
      <guid>https://dev.to/behnamrhp/a-simple-way-to-organize-api-endpoints-like-a-senior-cho</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Endpoints architecture

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Overview&lt;/li&gt;
&lt;li&gt;Endpoints architecture&lt;/li&gt;
&lt;li&gt;
Endpoints explanations

&lt;ul&gt;
&lt;li&gt;BaseEndpoint&lt;/li&gt;
&lt;li&gt;SpecificEndpoint&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;EndpointsProvider&lt;/li&gt;

&lt;li&gt;Usage example&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;API URLs, versions, and endpoints are among the most volatile parts of any application. Since they depend on external services, frequent changes can quickly lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spaghetti code (scattered URL strings)&lt;/li&gt;
&lt;li&gt;Brittle integrations (tight coupling with third-party APIs)&lt;/li&gt;
&lt;li&gt;Maintenance nightmares (manual updates across files)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture solves those problems by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralizing endpoint logic in dedicated classes.&lt;/li&gt;
&lt;li&gt;Abstracting URL construction behind reusable methods.&lt;/li&gt;
&lt;li&gt;Isolating versioning/base URLs for easy updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: Your core code stays clean, even when APIs change.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is a part of set of articles about clean architecture in Next.js, to explore all concepts in depth and see a production-ready boilerplate and following these best practices, visit:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/behnamrhp" rel="noopener noreferrer"&gt;
        behnamrhp
      &lt;/a&gt; / &lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;
        Next-clean-boilerplate
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Full featured nextjs boilerplate, based on clean architecture, mvvm and functional programming paradigm.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Nextjs clean architecture boilerplate&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of content&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#nextjs-clean-architecture-boilerplate" rel="noopener noreferrer"&gt;Nextjs clean architecture boilerplate&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#table-of-content" rel="noopener noreferrer"&gt;Table of content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#overview" rel="noopener noreferrer"&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#motivation" rel="noopener noreferrer"&gt;Motivation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#technologies" rel="noopener noreferrer"&gt;Technologies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#architecture" rel="noopener noreferrer"&gt;Architecture&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#folder-structure" rel="noopener noreferrer"&gt;Folder structure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#requirements" rel="noopener noreferrer"&gt;Requirements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#local" rel="noopener noreferrer"&gt;Local&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate#naming-convetions" rel="noopener noreferrer"&gt;naming convetions:&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This project is a starting point for your medium to large scale projects with Nextjs, to make sure having a structured, maintainable and scalable foundation for your Next.js project based on best practices in clean architecture, DDD approach for business logics, MVVM for the frontend part, storybook and vitest for testing and, localization and also functional programming with error handling for business logics.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Motivation&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Next.js and many other modern SSR frameworks provide powerful tools and a fresh approach to frontend development. However, since these tools are relatively new, the focus has largely been on features rather than software engineering best practices.&lt;/p&gt;
&lt;p&gt;As a result, many teams use Next.js for its capabilities but neglect maintainability, architecture, and scalability—especially in medium to large-scale applications…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/behnamrhp/Next-clean-boilerplate" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Endpoints architecture
&lt;/h2&gt;

&lt;p&gt;The following class diagram shows the architecture of endpoints:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.plantuml.com%2Fplantuml%2Fdpng%2FSoWkIImgAStDKU1ApaaiBbPmIYnETSrBASZFp2kfhkM2YoPdf-Qbm2GKW38G2S-K0an1nxpyaepK8iU2J6GvBdH3T7Lhx53iumAQXaSMOImUHGv06u3U0G00" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.plantuml.com%2Fplantuml%2Fdpng%2FSoWkIImgAStDKU1ApaaiBbPmIYnETSrBASZFp2kfhkM2YoPdf-Qbm2GKW38G2S-K0an1nxpyaepK8iU2J6GvBdH3T7Lhx53iumAQXaSMOImUHGv06u3U0G00" alt="EndpointArchitecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Endpoints explanations
&lt;/h2&gt;
&lt;h3&gt;
  
  
  BaseEndpoint:
&lt;/h3&gt;

&lt;p&gt;The Endpoint class is implemented for manipulating API endpoints within an application.&lt;br&gt;
the Endpoint class requires two parameters: &lt;code&gt;baseURL&lt;/code&gt; and &lt;code&gt;apiVersion&lt;/code&gt;, which represent the base URL of the API and the version of the API being used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;apiVersion&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="nl"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uris&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="nx"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sanitizeURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uris&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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;protected&lt;/span&gt; &lt;span class="nf"&gt;buildEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="nx"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sanitizeURL&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseURL&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiVersion&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="nx"&gt;endpoint&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="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;sanitizeURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(?&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;!:&lt;/span&gt;&lt;span class="se"&gt;)\/\/&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Endpoint&lt;/code&gt; class has several methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;compose Method (Static)&lt;/code&gt; This static method, takes an array of strings representing different parts of the URL and returns a sanitized URL by joining these parts together. It ensures that the URL is properly formatted and free of any unnecessary double slashes or incorrect separators.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;buildEndpoint Method&lt;/code&gt; This instance method, buildEndpoint, is responsible for constructing a complete endpoint URL by appending a specific endpoint to the base URL and API version. It returns a sanitized URL string ready for use in API requests.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sanitizeURL Method (Static)&lt;/code&gt; You can use this method to ensure that the constructed URLs are correctly formatted. It replaces any occurrences of consecutive slashes with a single slash, ensuring that the URL is valid and compliant with URL standards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, The &lt;code&gt;Endpoint&lt;/code&gt; class offers a convenient and reliable way to construct and manage endpoint URLs within an application. It enables team members to use a shared language when working with third-party libraries, reducing the risk of bloating the main application code due to API or UI changes in external dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  SpecificEndpoint
&lt;/h3&gt;

&lt;p&gt;Every specific endpoint class extends the functionality of the &lt;code&gt;Endpoint&lt;/code&gt; class and introduces additional features specific to this  specific endpoint. This inheritance includes methods for composing and building endpoint URLs.&lt;br&gt;
The specific endpoint encapsulates private properties, these properties are URLs for a specific endpoint.&lt;br&gt;
 The following is an example about making a specific endpoint and how it extends the base endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookEndpoint&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;addBookEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;booksEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;addBook&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addBookEndpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;books&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;booksEndpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;addBookEndpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;booksEndpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;apiVersion&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="nl"&gt;addBookEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;booksEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addBookEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addBookEndpoint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;booksEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;booksEndpoint&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;As you see the BookEndpoint class extends &lt;code&gt;Endpoint&lt;/code&gt; to provide a structured way of managing book-related API endpoints. It encapsulates endpoint construction logic, ensuring consistency and reusability across the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  EndpointsProvider
&lt;/h2&gt;

&lt;p&gt;The EndpointsProvider class offers centralized static methods for retrieving various endpoints used in the application. Each method returns an instance of a specific endpoint class, preconfigured with the necessary URLs and settings.&lt;/p&gt;

&lt;p&gt;By encapsulating the logic for creating and configuring endpoints, this class simplifies access and usage across the application. Below is an example of how the EndpointsProvider works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;BookEndpoint&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/bootstrap/helper/endpoint/endpoints/book-endpoints&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;appConfigs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/bootstrap/config/app-configs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Provides static methods to retrieve different types of endpoints.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EndpointsProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Book api
   */&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;book&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BookEndpoint&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api/v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;addBookEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;book/add&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;booksEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;book&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appConfigs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseApis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;book&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: The EndpointProvider step is optional to make the code agnostic about the changes or replacing of BookEndpoint with any other one. Instead, you can statically define all required endpoint configurations directly in the endpoint class's constructor. It's your choice based on the flexibility that your projects need.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Usage Example
&lt;/h2&gt;

&lt;p&gt;You can see implementation examples in this Next.js boilerplate:&lt;/p&gt;

&lt;h3&gt;
  
  
  Endpoint Files
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/tree/develop/src/bootstrap/endpoint/endpoints" rel="noopener noreferrer"&gt;Endpoint implementations&lt;/a&gt;
Contains two endpoint types:

&lt;ol&gt;
&lt;li&gt;HTTP backend API connections&lt;/li&gt;
&lt;li&gt;Identity provider API connections&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/behnamrhp/Next-clean-boilerplate/blob/develop/src/feature/core/user/data/repository/user.repository.ts" rel="noopener noreferrer"&gt;Repository usage&lt;/a&gt;
Demonstrates how backend endpoints are consumed throughout the application&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Managing API endpoints effectively is crucial for building maintainable applications in our API-driven world. The architecture we've explored:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Isolates volatile API configurations&lt;/strong&gt; from business logic&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Standardizes endpoint management&lt;/strong&gt; across your codebase&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Dramatically reduces tech debt&lt;/strong&gt; when APIs inevitably change  &lt;/p&gt;

&lt;p&gt;By implementing this pattern, you'll spend less time on finding and fixing broken endpoints and more time building features by separating api management responsibility in an isolated layer. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Remember: Good architecture isn't about preventing change - it's about making change manageable.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you found this article helpful, I’d be truly grateful if you could:&lt;br&gt;
⭐ Star the repository to support its visibility&lt;br&gt;
💬 Share your thoughts in the comments—your feedback helps improve content reach&lt;/p&gt;

&lt;p&gt;Every interaction helps these best practices reach more developers!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>api</category>
    </item>
    <item>
      <title>How to Automate Failure Handling and Messages with This Design!</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Thu, 13 Mar 2025 21:05:46 +0000</pubDate>
      <link>https://dev.to/behnamrhp/how-to-automate-failure-handling-and-messages-with-this-powerful-architecture-i4j</link>
      <guid>https://dev.to/behnamrhp/how-to-automate-failure-handling-and-messages-with-this-powerful-architecture-i4j</guid>
      <description>&lt;h2&gt;
  
  
  What is a failure
&lt;/h2&gt;

&lt;p&gt;In each application and in each logic, there can be failures in the process, and based on their complexity, there can be a few or many possible scenarios for these failures.&lt;/p&gt;

&lt;p&gt;In software development, we always try to have more control over these failures to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid possible bugs&lt;/li&gt;
&lt;li&gt;Help users understand the state of the application with proper messages&lt;/li&gt;
&lt;li&gt;Control processes for side effects&lt;/li&gt;
&lt;li&gt;Monitor the behavior of the application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, having a specific way of handling errors to achieve all these requirements in our app helps us build a more robust, reliable, and maintainable application.&lt;/p&gt;

&lt;p&gt;Many frameworks provide their own ways to handle these failures, which they may call exceptions, failures, or other terms. But we shouldn't always depend on the behavior of frameworks for our logic and apps. Besides, many frameworks do not provide error handling tools, so we need to design a reliable architecture for the error handling in our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure handling with base failure
&lt;/h2&gt;

&lt;p&gt;To have granular control over failures and define a specific type for all errors, we can use the power of inheritance and abstraction in OOP.&lt;/p&gt;

&lt;p&gt;We can define an abstract class as our base failure, which serves as the specific type for all failures in the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;META_DATA&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;META_DATA&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;META_DATA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;As you can see, it's just a simple abstract class that takes some metadata about the error details in any form. But wait, this is just the beginning of the story, we can explore many ideas with this failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to write a simple failure
&lt;/h2&gt;

&lt;p&gt;So, to create a simple failure, we can define our failure in any domain for any scenario we need, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateUserFailure&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&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;So in our logics for creating user we can return specific type of failure for creating user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combination with Functional programming
&lt;/h2&gt;

&lt;p&gt;Functional programming is a deep topic that we cannot fully cover here. For more details, you can check out various courses and books available online.&lt;/p&gt;

&lt;p&gt;However, for this article, we focus on one of the most useful functors in functional programming and how failure fits perfectly with it. This concept is the Either type.&lt;/p&gt;

&lt;p&gt;Either is an algebraic data type (ADT) that represents computations that can return either a success or a failure. It consists of two possible values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Right(value): Represents a successful result.&lt;/li&gt;
&lt;li&gt;Left(error): Represents a failure or unexpected result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You're guessing right, our base failure will serve as the Left value in Either, allowing us to handle errors in a structured and functional way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Either&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;BaseFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ResponseType&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So as we always have specific type for handling unexpected results, so we can define a new type for either in our app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ApiEither&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ResponseType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Either&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;BaseFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ResponseType&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, for any API calls, we can use the Either type to handle both success and failure cases.&lt;/p&gt;

&lt;p&gt;Additionally, for asynchronous processes, we use TaskEither, which works similarly to Either but is designed for handling asynchronous operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ApiTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ResponseType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TaskEither&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BaseFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ResponseType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, when creating a customer repository to handle all API calls for customers, we can use this type to manage success and failure cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CustomerRepo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fetchList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ApiTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Customer&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the repo we can have this pipe to get customer data:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: In functional programming, a pipe is a composition of functions where the output of one function is passed as the input to the next, allowing for a clean, readable flow of data transformations.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;tryCatch&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="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="c1"&gt;// calling api and returning result&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;failureOr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NetworkFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customersDto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ApiTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Customer&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, in the try-catch block, which is the constructor of ApiEither, we define the right response in the first callback and the failure in the second callback argument.&lt;/p&gt;

&lt;p&gt;failureOr is just a helper function that takes an error and converts it into a specific failure type, NetworkFailure in this example.&lt;/p&gt;

&lt;p&gt;This ensures that during the process of fetching customer data, we always know the unexpected result will be of a specific type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;failureOr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BaseFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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="nx"&gt;BaseFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;BaseFailure&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="nx"&gt;reason&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="nx"&gt;failure&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;So in the process of fetching customer we know the unexpected result, always will be a speicfic type. &lt;br&gt;
So in any layer we can get the failure do some logics on left response based on its metadata and turn the failure shape to any other failure shape and use it for different purposes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Usecases of this idea
&lt;/h2&gt;

&lt;p&gt;There are many situations where, if an important process encounters problems, we want to have control over it. We need to know when and why these issues happened and store that information in one of the monitoring tools.&lt;/p&gt;

&lt;p&gt;For example, when a CreateUserFailure occurs in the repository layer, we can send a log with the specific time and relevant parameter data to any logging or monitoring tool.&lt;/p&gt;
&lt;h3&gt;
  
  
  Monitoring on bugs with dev failures
&lt;/h3&gt;

&lt;p&gt;There are many situations, especially in frontend applications, where unexpected behavior occurs due to development mistakes or bugs. For example, when bugs or data changes in APIs happen, it's possible to face unexpected behaviors. In such cases, we want to show a specific message or redirect the user to an error page with a clear message.&lt;/p&gt;

&lt;p&gt;Additionally, in frontend applications, logs may not be directly available in these situations, as the issue occurs on the user's system. To handle this, we can send metadata as a log to an API when encountering development failures.&lt;/p&gt;

&lt;p&gt;To achieve this, we can simply define another abstract failure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseDevFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;META_DATA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;BaseFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;META_DATA&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it’s just another failure that extends from the base failure.&lt;/p&gt;

&lt;p&gt;For example, in some parts of the application, when sending dynamic arguments into the domain layer, there's a possibility of sending unexpected data. In such situations, we can define a specific development failure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArgumentsFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;META_DATA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;BaseDevFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;META_DATA&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;META_DATA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&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;So we can consider this scenario in our logics and facing with this failure we can make a log request to our log api even from frontend applications, so on facing with this situation they can show a descent message to user to contact to support team at the same time they store the bug log to have full controll on these situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manage translations and error messages with failure
&lt;/h3&gt;

&lt;p&gt;With this approach, we can go a step further than just error handling and even manage translations and display related messages in frontend applications automatically.&lt;/p&gt;

&lt;p&gt;For each process and scenario, we should define a specific failure. At the same time, for each failure, we should display a corresponding message in the selected language based on the user's preference.&lt;/p&gt;

&lt;p&gt;We can use this idea to automate both the error handling and message translation process.&lt;/p&gt;

&lt;p&gt;To achieve this, we can pass a unique string key from the constructor based on the failure scenario. Our base failure will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseFailure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;META_DATA&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;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;BASE_FAILURE_MESSAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Use this message as key lang for failure messages
   */&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BASE_FAILURE_MESSAGE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;META_DATA&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;META_DATA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeFailureMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="cm"&gt;/**
 * Gets Message key and it'll add it to the failure message key hierarchy
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeFailureMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&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="nx"&gt;key&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="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we have a message property, which contains &lt;code&gt;BASE_FAILURE_MESSAGE&lt;/code&gt;, the base key for all failure messages. It also accepts a key from the constructor, and with the makeFailureMessage function, it concatenates the new key with the message, shaping a unique message for each failure.&lt;/p&gt;

&lt;p&gt;Each failure can have its own key passed from its constructor.&lt;/p&gt;

&lt;p&gt;In the end, we can have a chained message key that we can use as the message key for each failure.&lt;/p&gt;

&lt;p&gt;For example, for a failure like &lt;code&gt;UserAlreadyExistsFailure&lt;/code&gt;, we can have a parent failure for all user domain failures, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserFailure&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseFailure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;makeFailureMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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;and now we can define our failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserAlreadyExistsFailure&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;UserFailure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alreadyExists&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so the result of message for &lt;code&gt;UserAlreadyExistsFailure&lt;/code&gt;, will be &lt;code&gt;failure.user.alreadyExists&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At the same time, in another part of our project, we're using a langKey object to specify the translation key. This object, like the failure structure, follows the domain and folder structure to specify the language key.&lt;br&gt;
&lt;/p&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;langKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="na"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;alreadyExists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failure.user.alreadyExists&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we can use our failure message key to retrieve the language key. By passing it to the translation method, we can get the translated failure message and automate the process of displaying the error message based on the failure we encounter.&lt;br&gt;
&lt;/p&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;usecaseResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUsersUsecase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&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;Either&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BaseFailure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isLeft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usecaseResponse&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&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;usecaseResponse&lt;/span&gt; &lt;span class="nx"&gt;instanceOf&lt;/span&gt; &lt;span class="nx"&gt;BaseFailure&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;translatedFailureMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usecaseResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the final version, class diagram for our failur architecture:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lfgihrzhitlc4uu8x7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lfgihrzhitlc4uu8x7q.png" alt="Failure class diagram" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, we've explored how to handle failures effectively in software applications by combining error handling with functional programming concepts like the Either type. &lt;/p&gt;

&lt;p&gt;Furthermore, by integrating these failure handling mechanisms with automated processes for translating and displaying error messages, we create a more seamless experience for users, no matter the scenario. This approach not only improves the user experience by offering clear and context-specific messages, but it also provides valuable insights through monitoring and logging, enabling teams to quickly address issues.&lt;/p&gt;

&lt;p&gt;Ultimately, this architecture supports building more robust, maintainable, and user-friendly applications, which I have used in many of my own projects, especially in frontend ones.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Cracking the Code: How the MVVM with Bridge Pattern Saves a Messy Frontend UI (Part 2)</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Sun, 01 Dec 2024 13:49:53 +0000</pubDate>
      <link>https://dev.to/behnamrhp/cracking-the-code-how-the-mvvm-with-bridge-pattern-saves-a-messy-frontend-ui-part-2-22oc</link>
      <guid>https://dev.to/behnamrhp/cracking-the-code-how-the-mvvm-with-bridge-pattern-saves-a-messy-frontend-ui-part-2-22oc</guid>
      <description>&lt;p&gt;&lt;em&gt;Note: This is the second part of our series on the MVVM architecture in the frontend. If you haven’t read the first part yet, we highly recommend &lt;a href="https://dev.to/behnamrhp/cracking-the-code-how-the-mvvm-with-bridge-pattern-saves-a-messy-frontend-ui-part-1-3h4"&gt;starting here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bridge Pattern Coding Example
&lt;/h2&gt;

&lt;p&gt;In the first part of this series, we discussed how the Bridge Pattern can enhance the reusability and maintainability of UI components in a large-scale frontend application. While we focused primarily on the high-level design with uml class diagrams, the real challenge lies in its implementation. After all, even the best designs are merely theoretical without effective implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: A Reusable Button Component
&lt;/h2&gt;

&lt;p&gt;Let’s walk through a simple example: creating a &lt;strong&gt;button component&lt;/strong&gt; that is reusable across different contexts and logic in our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bridge Pattern in Action
&lt;/h2&gt;

&lt;p&gt;To achieve this, we use an interface to establish a convention between the UI (view layer) and the UI logic (view model layer). This interface acts as a bridge, enabling the separation of concerns and improving flexibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonUiLogic&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When building reusable components, it's important to distinguish between props (state-driven UI data) and events (interactions or behaviors triggered by the user).&lt;/p&gt;

&lt;p&gt;In our example, the props define the visual and state attributes of the button, such as title and disabled. Meanwhile, the events, such as onClick, are passed separately to handle interactions outside the UI logic. This convention can help you to have a clear separation and also can help you in some memoization down the road.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;uiLogic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonUiLogic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;// rest of props comes from the parent&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Abstraction layer (UI)
&lt;/h2&gt;

&lt;p&gt;Now its time to handle our abstraction layer of bridge pattern which is a button component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AppButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;uiLogic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;uiLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;uiLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClick&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="nx"&gt;uiLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&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;In this code you can see that we got our uiLogic from our props and binding events and props of ui logic to the UI part which is nothing but a react component. It’s responsible just to decide how to show the data and how to connect behaviors to the UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation layer (UI logic)
&lt;/h2&gt;

&lt;p&gt;The implementation layer in the Bridge Pattern defines the behavior and state logic of the component. For our button example, we’ll implement a counting button that increments a counter every time it’s clicked.&lt;/p&gt;

&lt;p&gt;This example demonstrates how the UI logic handles the component's state and provides props for the UI, ensuring a clean separation of concerns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterUILogic&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IBaseUiLogic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonUiLogic&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;useUiLogic&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ButtonUiLogic&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Current counter is: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&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="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;onClick&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;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see we used a class which implements IBaseUiLogc by passing correspond ButtonUiLogic as generic to it which is nothing but.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IBaseUiLogic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UI_LOGIC&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;useUiLogic&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;UI_LOGIC&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;You can see that this interface is just a convention for having one custom hook with a specific name to handle ui logic inside of it.&lt;/p&gt;

&lt;p&gt;So you can see instead of having many implementation of one ui component for each logic, we can have thousands of UI logic for one UI for any purpose, like submit a form, open a modal, etc. Most of the hooks, connection to business logic to fetch the data, manipulating the state of the component, will happen in this layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connection
&lt;/h2&gt;

&lt;p&gt;Let’s review the design together one more time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik3ph75dfc1gz9qeqgdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik3ph75dfc1gz9qeqgdr.png" alt="Ui logic and ui connected together through one parent component" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The connection between the UI and UiLogic layers is critical for ensuring seamless interaction while maintaining isolation. This connection must ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encapsulation&lt;/strong&gt;: The logic and state changes of one component don’t affect others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactivity&lt;/strong&gt;: The UI updates dynamically when the state in the UiLogic changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: A single component can serve as a reusable environment for various UiLogic implementations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To achieve this, we’ll create a reusable LogicProvider component. It will encapsulate the UiLogic and provide the necessary bridge to the UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IUiLogicProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UI_LOGIC&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;Ui&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="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;uiLogic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UI_LOGIC&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="nl"&gt;UiLogic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IBaseUiLogic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UI_LOGIC&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;restProps&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UiLogicProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UI_LOGIC&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IUiLogicProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UI_LOGIC&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UiLogic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;restProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&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;uiLogic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UiLogic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useUiLogic&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;allProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;restProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;uiLogic&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Ui&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;allProps&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="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Ui&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this basic component we’re getting UiLogic instance and based on the interface convention for our UI logic we call our ui logic to run and pass the result to the Ui.&lt;br&gt;
&lt;strong&gt;Notes and Optimization Tips&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encapsulation&lt;/strong&gt;: The UiLogicProvider encapsulates all the logic-specific state updates and re-renders, ensuring other components remain unaffected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Memoization&lt;/strong&gt;: By wrapping this provider with memo, you avoid the need to explicitly use React.memo or similar techniques for each child component as you know your UI component should just be rerendered by changes of the Uilogic not rerendering of the parents. This ensures optimal performance without extra effort in an automatic way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UiLogicProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currProps&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;prevProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restProps&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;currProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restProps&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;prevProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;currProps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&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;ul&gt;
&lt;li&gt;This component is the most basic one. You can get the base concept of it and use it with other ideas and techniques as well. The main concept is one layer will handle calling the Ui logic to avoid other components getting affected by this component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Last part&lt;/strong&gt;&lt;br&gt;
The last part will be the easiest one. Just from required parent component connect Ui and Ui logic with our provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UiLogicProvider&lt;/span&gt; &lt;span class="nx"&gt;Ui&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ButtonComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;UiLogic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CounterUILogic&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UiLogicProvider&lt;/span&gt; &lt;span class="nx"&gt;Ui&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ButtonComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;Uilogic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CancelButtonLogic&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&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;h2&gt;
  
  
  Transitioning to MVVM Architecture in Bridge Pattern
&lt;/h2&gt;

&lt;p&gt;In the MVVM (Model-ViewModel-View) architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model: Handles the business logic and manipulates stored data.&lt;/li&gt;
&lt;li&gt;ViewModel (VM): Focuses on UI logic and connects the Model to the View.&lt;/li&gt;
&lt;li&gt;View: Manages the UI rendering and binds to the ViewModel via an interface, typically called IVM, which acts as the Bridge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture builds on the Bridge Pattern we discussed earlier, but introduces a Model layer to separate business logic, ensuring a clean and scalable foundation for large-scale applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping Bridge Pattern to MVVM
&lt;/h2&gt;

&lt;p&gt;Here’s how the Bridge Pattern concepts translate into MVVM:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Bridge Pattern&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;MVVM&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Role&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Abstraction Layer&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;td&gt;Reusable UI components that bind to the ViewModel.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bridge&lt;/td&gt;
&lt;td&gt;IVM Interface&lt;/td&gt;
&lt;td&gt;Connects the View to the ViewModel.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation Layer&lt;/td&gt;
&lt;td&gt;ViewModel (VM)&lt;/td&gt;
&lt;td&gt;Handles UI logic and mediates between View and Model.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Business logic and data manipulation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Till now we had all our logic inside of the Implementation layer part if we separate business logic into separate classes or functions and we name it as a model. So the only remains in Implementation is just real UI logic and just with one connection with the model we can send all data to UI and handle UI logic as well.&lt;br&gt;
In this case we’ll have a clean foundation architecture based on MVVM for our large scale application that is reusable, maintainable and testable. &lt;/p&gt;

&lt;p&gt;Now in MVVM (Model-ViewModel-View) our Model is responsible for business logic, ViewModel is responsible for UI logic and connecting model to view and then we’ll have our reusable View part that connects to ViewModel with one interface as the IVM which plays our bridge.&lt;/p&gt;

&lt;p&gt;Now lets review the diagram but this time in MVVM architecture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk1gtokhxve7jej2fcsw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk1gtokhxve7jej2fcsw.png" alt="Mapped bridge pattern architecture to mvvm in class diagram" width="800" height="441"&gt;&lt;/a&gt;&lt;br&gt;
Now you can see in the above diagram that View is replaced with an abstraction layer, IVM replaced with Bridge and VM replaced with an implementation layer. Also we add Model as a layer which is responsible for business logics and manipulation of stored data.&lt;/p&gt;

&lt;p&gt;Now we can translate our code example to MVVM shape. For this reason we need to just replace the UI name with View and UiLogic with VM. That’s it now you have a base MVVM approach architecture in your react.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rewriting the example in MVVM
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;View (UI)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AppButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClick&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="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&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;&lt;strong&gt;IVM and IProps (Bridge)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonVM&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonVM&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;// rest of props comes from the parent&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;VM (UI Logic)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;CounterVM&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;IBaseVM&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonVM&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;useVM&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ButtonVM&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Current counter is: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&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="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;onClick&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;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;With this journey, we’ve wrapped up building a clean MVVM architecture in a React application, ensuring it aligns with the best practices of performance and software architecture.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separation of Concerns:&lt;/strong&gt; Each layer has a distinct responsibility with using Model, ViewModel (VM), View.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bridge Pattern Integration&lt;/strong&gt;: The IVM interface serves as the Bridge, providing a clean abstraction between the View and the ViewModel, making the architecture flexible and reusable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability and Testability&lt;/strong&gt;: Models, ViewModels, and Views are independently reusable and testable, ensuring long-term maintainability and reducing the cost of changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Optimization&lt;/strong&gt;: With the use of a UiLogicProvider, we’ve ensured that re-renders are isolated to only the relevant components.
By encapsulating the ViewModel logic, we’ve also enabled automatic memoization, avoiding unnecessary re-renders across the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: The modular design makes the architecture inherently scalable, suitable for large-scale applications with complex requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Congratulations on completing these two articles with me! I hope you found them helpful. If you did, please let me know in the comments and feel free to like and share to help more people discover and benefit from these ideas. I’d also love to hear your suggestions or ideas about coding architecture and approaches that have worked well for you!&lt;/p&gt;

</description>
      <category>mvvm</category>
      <category>designpatterns</category>
      <category>frontend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Cracking the Code: How the MVVM with Bridge Pattern Saves a Messy Frontend UI (Part 1)</title>
      <dc:creator>behnam rahimpour</dc:creator>
      <pubDate>Sun, 01 Dec 2024 12:50:31 +0000</pubDate>
      <link>https://dev.to/behnamrhp/cracking-the-code-how-the-mvvm-with-bridge-pattern-saves-a-messy-frontend-ui-part-1-3h4</link>
      <guid>https://dev.to/behnamrhp/cracking-the-code-how-the-mvvm-with-bridge-pattern-saves-a-messy-frontend-ui-part-1-3h4</guid>
      <description>&lt;p&gt;One of the most basic concerns in frontend applications is reusability of UI components. While there are several ideas on how to categorize them, one of the most underrated concepts is how to manage their logic to maintain reusability while also preserving the single responsibility principle for parent components.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does that mean?
&lt;/h2&gt;

&lt;p&gt;Let's explore this with an example. Imagine we have a card component whose responsibility is to display some detailed information. Naturally, we want to reuse this card component in various scenarios. But where do we handle the data fetching and transformation necessary for displaying in the component? If we place this logic inside the component itself, it will no longer be reusable. So, most of the time, we move these logics to the parent component.&lt;/p&gt;

&lt;p&gt;Now, imagine this parent component contains not just the card, but also other components like a SelectBox, input fields, and others. Each of these components, which are also reusable, requires some logic as well. Where do we put this logic? Once again, it ends up in the parent component. This is where the problem arises: we violate the single responsibility principle because the parent component now manages too many details it shouldn't need to know about.&lt;br&gt;
Now, imagine having to modify the logic for one of these components within this increasingly complex parent component. Not to mention the challenge of testing all these intertwined logics.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the solution?
&lt;/h2&gt;

&lt;p&gt;In such situations, it's clear that there is a problem with our coding architecture, which could have significant effects on both the development process and the maintainability of the application. We need to rethink how we structure our UI and UI logics to keep components reusable while ensuring their business logic remains specific to each component without cluttering the parent components. This is where the Bridge Pattern comes into play.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bridge Pattern
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Note: This article won't focus on the bridge pattern itself with extensive examples, but rather on the concept as it pertains to this discussion. For more details, you can refer to the &lt;a href="https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0201633612&amp;amp;linkCode=as2&amp;amp;tag=martinfowlerc-20" rel="noopener noreferrer"&gt;gang of four book&lt;/a&gt; or &lt;a href="https://refactoring.guru/design-patterns/bridge" rel="noopener noreferrer"&gt;refactoring guru&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Bridge Pattern helps when we have a large class or multiple tightly coupled classes by separating them into two layers: abstraction and implementation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Abstraction&lt;/strong&gt;: Don't confuse this with the "abstract" keyword in your programming language. The abstraction layer is the part of the code that handles high-level responsibilities and delegates functionality to the implementation layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation&lt;/strong&gt;: This lower-level module handles all the actual work and logic, returning the results to the abstraction layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bridge&lt;/strong&gt;: The bridge is the interface that connects the abstraction to the implementation layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each abstraction layer has one corresponding bridge, and based on this bridge, we can have multiple implementations for different purposes.&lt;/p&gt;

&lt;p&gt;Now, how does this work in a real-world frontend application?&lt;/p&gt;

&lt;h2&gt;
  
  
  Bridge Pattern in Frontend Applications
&lt;/h2&gt;

&lt;p&gt;In frontend applications, we often have UI components that need to be reused in multiple places. These components, which are responsible only for displaying data and not for handling logic, represent the abstraction layer.&lt;/p&gt;

&lt;p&gt;We mentioned earlier that we need an interface for this abstraction layer, which will act as the bridge.&lt;/p&gt;

&lt;p&gt;So, at this point, we have the abstraction and the bridge. The only thing left is the UI logic, which will act as our implementation layer.&lt;/p&gt;

&lt;p&gt;Imagine we have a button component. In this case, we create a UI component that serves as the abstraction layer with an interface acting as the bridge. On the other hand, we can have multiple UI logics for this button for different purposes, and they all follow the bridge interface.&lt;/p&gt;

&lt;p&gt;You can visualize this relationship in the following class diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46fwjmywmkppygamfcih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46fwjmywmkppygamfcih.png" alt="Many ui logics with an interface as bridge connected to a component" width="800" height="509"&gt;&lt;/a&gt;&lt;br&gt;
In the above example, you can see how we can have multiple UI logics for a reusable UI component through one interface that serves as the bridge.&lt;/p&gt;

&lt;p&gt;Now, you might wonder how we connect one UI logic to its respective UI component. This is where the parent component comes in - it knows which UI logic is associated with which UI component. In this setup, the parent component simply connects the child components to their corresponding UI logics without knowing any of the details. This is the parent component's only responsibility.&lt;/p&gt;

&lt;p&gt;This is the revised version of the previous diagram with considering the connection between UI and UI logics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifxu00sxnbw898q8ssa9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifxu00sxnbw898q8ssa9.png" alt="Ui logic passed to the component through a parent component" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits
&lt;/h2&gt;

&lt;p&gt;With this approach, your code becomes much more maintainable. Any changes in one implementation layer won't affect the parent component or the UI itself. Additionally, the code is fully testable: you can write unit tests for your UI logics and test the UI thoroughly using appropriate tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Devil Is in the Details
&lt;/h2&gt;

&lt;p&gt;Of course, these concepts offer only a high-level overview. Implementing this architecture requires careful consideration of framework-related performance issues, like rerendering. In the next article, we'll dive deeper into implementing these ideas using the React framework.&lt;/p&gt;

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

&lt;p&gt;In conclusion, the Bridge Pattern offers a clean solution to the common challenge of maintaining reusable UI components while preventing parent components from becoming cluttered with business logic. By separating concerns between the abstraction and implementation layers, and introducing a bridge to connect them, we ensure that each component remains modular, maintainable, and easy to test. Adopting this architectural approach can significantly improve both the quality and scalability of frontend applications.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>frontend</category>
      <category>react</category>
      <category>designpatterns</category>
    </item>
  </channel>
</rss>
