top of page
  • Writer's pictureRevanth Reddy Tondapu

Part 29: Data Layer Separation: Enhancing Code Reusability and Cleanliness


Data Layer Separation: Enhancing Code Reusability and Cleanliness
Data Layer Separation: Enhancing Code Reusability and Cleanliness

In our previous post, we successfully loaded review data from a Markdown file using front matter to handle properties like the title, date, and image path. Now, let's take it a step further by separating the data loading logic from the component itself. This will make our code cleaner, more modular, and easier to maintain.


Why Separate the Data Layer?

Separating the data layer from the presentation layer has several benefits:

  1. Clean Code: Keeps the component focused only on displaying the data.

  2. Reusability: Makes the data-fetching logic reusable across different components.

  3. Flexibility: Allows you to change the data source (e.g., switch from local files to an API) without altering the component.


Step-by-Step Guide to Data Layer Separation


Step 1: Move Data Loading Logic into a Separate Function

First, let's write a new function called getReview that will handle loading and parsing the Markdown file. We'll define this function in the same file for now.

import { readFile } from 'node:fs/promises';
import matter from 'gray-matter';
import { marked } from 'marked';

async function getReview(slug) {
  const text = await readFile(`./content/reviews/${slug}.md`, 'utf8');
  const { content, data: { title, date, image } } = matter(text);
  const body = marked(content);
  return { title, date, image, body };
}

Step 2: Refactor the Component to Use the New Function

Now, let's update our component to use the getReview function. This way, the component will only be responsible for displaying the data.

import Heading from '@/components/Heading';
import { getReview } from '@/lib/reviews';

export default async function StardewValleyPage() {
  const review = await getReview('stardew-valley');
  return (
    <>
      <Heading>{review.title}</Heading>
      <p className="italic pb-2">{review.date}</p>
      <img src={review.image} alt={review.title} width="640" height="360" className="mb-2 rounded" />
      <article dangerouslySetInnerHTML={{ __html: review.body }} className="prose prose-slate max-w-screen-sm" />
    </>
  );
}

Step 3: Make the Function Reusable

To make getReview truly reusable, we should not hard-code the review identifier. Instead, we will pass it as an argument. This way, the function can be used to load any review.

async function getReview(slug) {
  const text = await readFile(`./content/reviews/${slug}.md`, 'utf8');
  const { content, data: { title, date, image } } = matter(text);
  const body = marked(content);
  return { title, date, image, body };
}

Step 4: Move the Function to a Separate File

To keep our project organized, we should move the getReview function into a separate file. A common convention is to create a lib folder for utility functions.

  1. Create a new file: lib/reviews.js.

  2. Move the getReview function into this file.

// lib/reviews.js
import { readFile } from 'node:fs/promises';
import matter from 'gray-matter';
import { marked } from 'marked';

export async function getReview(slug) {
  const text = await readFile(`./content/reviews/${slug}.md`, 'utf8');
  const { content, data: { title, date, image } } = matter(text);
  const body = marked(content);
  return { title, date, image, body };
}

Step 5: Update the Component to Use the New File

Finally, update your component to import the getReview function from the new file.

import Heading from '@/components/Heading';
import { getReview } from '@/lib/reviews';

export default async function StardewValleyPage() {
  const review = await getReview('stardew-valley');
  return (
    <>
      <Heading>{review.title}</Heading>
      <p className="italic pb-2">{review.date}</p>
      <img src={review.image} alt={review.title} width="640" height="360" className="mb-2 rounded" />
      <article dangerouslySetInnerHTML={{ __html: review.body }} className="prose prose-slate max-w-screen-sm" />
    </>
  );
}

Conclusion

By separating the data layer from the presentation layer, we have made our code cleaner, more modular, and easier to maintain. The getReview function encapsulates all the logic for loading and parsing review data, making it reusable across different components. This separation also makes it easier to switch data sources in the future without affecting the component code.

By following this approach, you can create more maintainable and scalable applications, setting a strong foundation for future development.

Happy coding!

0 views0 comments

Comments


bottom of page