Skill

TypeORM এর জন্য Best Practices এবং Design Patterns

টাইপওআরএম (TypeORM) - Web Development

301

TypeORM এর জন্য Best Practices

TypeORM হল একটি জনপ্রিয় Object-Relational Mapping (ORM) লাইব্রেরি যা JavaScript এবং TypeScript ডেভেলপমেন্টে ডেটাবেস পরিচালনার জন্য ব্যবহৃত হয়। TypeORM ব্যবহার করার সময় কিছু Best Practices অনুসরণ করলে কোড ক্লিন, দক্ষ এবং স্কেলেবল হয়। নিচে TypeORM ব্যবহারের জন্য কিছু গুরুত্বপূর্ণ Best Practices আলোচনা করা হলো।

১. ডেটাবেস কনফিগারেশন আলাদা রাখা

ডেটাবেস কনফিগারেশন এবং কানেকশন তথ্য সরাসরি কোডে না রেখে আলাদা ফাইল বা পরিবেশ ভেরিয়েবল (environment variables) হিসেবে সংরক্ষণ করুন। এটি নিরাপত্তা এবং পরিবেশ পরিবর্তন সহজ করবে।

উদাহরণ (ormconfig.json):

{
  "type": "postgres",
  "host": "localhost",
  "port": 5432,
  "username": "user",
  "password": "password",
  "database": "mydb",
  "entities": ["src/entity/**/*.ts"],
  "synchronize": true
}

২. synchronize: true শুধুমাত্র ডেভেলপমেন্টে ব্যবহার করা

TypeORM এর synchronize: true কনফিগারেশন শুধুমাত্র ডেভেলপমেন্টে ব্যবহার করা উচিত। এটি টেবিলের স্কিমা স্বয়ংক্রিয়ভাবে সিঙ্ক্রোনাইজ করে, যা প্রোডাকশন পরিবেশে বিপদজনক হতে পারে। প্রোডাকশনে এটি synchronize: false করা উচিত।

৩. Entity এবং Column গুলোতে ডেকোরেটর ব্যবহার

TypeORM এ প্রতিটি Entity এবং Column কে ডেকোরেটর দিয়ে মার্ক করুন। এটি TypeScript এর সাথে কাজ করার জন্য অত্যন্ত গুরুত্বপূর্ণ। সব কন্ডিশন অনুযায়ী @Entity(), @PrimaryGeneratedColumn(), এবং @Column() ডেকোরেটর ব্যবহার করুন।

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}

৪. Transactions ব্যবহার করা

এমন কোনো অপারেশন যেখানে একাধিক টেবিল পরিবর্তন হবে, সেগুলোর জন্য Transactions ব্যবহার করা উচিত। এটা নিশ্চিত করে যে, সকল অপারেশন একযোগভাবে সফল হলে প্রক্রিয়া সম্পন্ন হবে, না হলে আগের অবস্থায় ফিরে যাবে।

import { getManager } from "typeorm";
import { User } from "./entity/User";

await getManager().transaction(async transactionalEntityManager => {
  const user = new User();
  user.name = "Jane";
  user.email = "jane.doe@example.com";
  await transactionalEntityManager.save(user);
});

৫. QueryBuilder ব্যবহার করার সময় প্রপার সিকিউরিটি নিশ্চিত করা

QueryBuilder ব্যবহারের সময় SQL ইনজেকশন থেকে বাঁচতে সঠিকভাবে প্যারামিটার ব্যবহার করুন। TypeORM এর QueryBuilder প্রি-ডিফাইন করা প্যারামিটার ব্যবহার করে SQL ইনজেকশন থেকে সুরক্ষা দেয়।

const users = await getRepository(User)
  .createQueryBuilder("user")
  .where("user.name = :name", { name: "Jane" })
  .getMany();

৬. Indexing এবং Optimizing Queries

প্রোডাকশন ডেটাবেসে কার্যকারিতা নিশ্চিত করতে Indexes ব্যবহার করুন। এটি বড় ডেটাবেসে অপারেশন গতি উন্নত করবে। বিশেষত, যেখানে বারবার find বা search অপারেশন হচ্ছে সেখানে ইনডেক্স ব্যবহার করুন।

৭. Migrations ব্যবহারের মাধ্যমে ডেটাবেস স্কিমা ম্যানেজ করা

ডেটাবেস স্কিমা পরিবর্তনের জন্য Migrations ব্যবহার করুন। TypeORM-এ migrations ডেটাবেস স্কিমা পরিবর্তন এবং সংস্করণ নিয়ন্ত্রণে সাহায্য করে।

typeorm migration:generate -n MigrationName

এটি ডেটাবেসের বর্তমান অবস্থা এবং পরিবর্তনসমূহকে ট্র্যাক করতে সহায়তা করে।


TypeORM Design Patterns

TypeORM এ কিছু জনপ্রিয় Design Patterns ব্যবহার করলে আপনার অ্যাপ্লিকেশনটির স্থিতিশীলতা, স্কেলেবিলিটি এবং রক্ষণাবেক্ষণ সহজ হয়ে ওঠে।

১. Repository Pattern

Repository Pattern একটি Design Pattern যা ডেটাবেসের সাথে যোগাযোগের জন্য একটি অবজেক্ট ব্যবহার করতে সহায়তা করে। এটি TypeORM এর একটি জনপ্রিয় প্যাটার্ন, যেখানে ডেটাবেস অপারেশনগুলো একটি Repository-র মাধ্যমে করা হয়। এটি ডেটা অ্যাক্সেস লেয়ারকে অ্যাপ্লিকেশন লজিক থেকে আলাদা রাখে এবং কোডের পুনঃব্যবহারযোগ্যতা বাড়ায়।

উদাহরণ:

import { EntityRepository, Repository } from "typeorm";
import { User } from "../entity/User";

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  findByName(name: string) {
    return this.createQueryBuilder("user")
      .where("user.name = :name", { name })
      .getMany();
  }
}

২. Singleton Pattern

Singleton Pattern ব্যবহৃত হয় যখন একটি ক্লাসের মাত্র একটিই ইনস্ট্যান্স থাকতে হবে। TypeORM-এ Singleton প্যাটার্ন ডেটাবেস কানেকশন ম্যানেজমেন্টে ব্যবহৃত হতে পারে। এটি নিশ্চিত করে যে, ডেটাবেস কানেকশন একবারই তৈরি হয় এবং সেই কানেকশনটি সারা অ্যাপ্লিকেশনজুড়ে ব্যবহৃত হয়।

import { createConnection, Connection } from "typeorm";

class DatabaseConnection {
  private static instance: Connection;

  private constructor() {}

  public static async getInstance(): Promise<Connection> {
    if (!DatabaseConnection.instance) {
      DatabaseConnection.instance = await createConnection();
    }
    return DatabaseConnection.instance;
  }
}

৩. Factory Pattern

Factory Pattern ব্যবহৃত হয় যখন আপনার অ্যাপ্লিকেশনকে বিভিন্ন ধরনের অবজেক্ট তৈরি করতে হয় এবং আপনি চান যে এটি একটি নির্দিষ্ট প্রক্রিয়ায় তৈরি হোক। TypeORM-এ, Factory Pattern ব্যবহৃত হতে পারে Entity তৈরি করার জন্য।

class UserFactory {
  public static createUser(name: string, email: string): User {
    const user = new User();
    user.name = name;
    user.email = email;
    return user;
  }
}

৪. Observer Pattern

Observer Pattern ব্যবহৃত হয় যখন একাধিক অবজেক্ট একটি অবজেক্টের পরিবর্তনকে পর্যবেক্ষণ করতে পারে এবং প্রতিটি পরিবর্তন সংশ্লিষ্ট অবজেক্টের উপর প্রভাব ফেলতে পারে। TypeORM-এ Event Subscribers এর মাধ্যমে Observer Pattern প্রয়োগ করা হয়, যা ডেটাবেসের পরিবর্তন সম্পর্কে অবহিত করে।

import { EntitySubscriberInterface, EventSubscriber, InsertEvent } from "typeorm";
import { User } from "./User";

@EventSubscriber()
export class UserSubscriber implements EntitySubscriberInterface<User> {
  listenTo() {
    return User;
  }

  afterInsert(event: InsertEvent<User>) {
    console.log("User has been inserted", event.entity);
  }
}

সারাংশ

TypeORM ব্যবহার করার সময় Best Practices এবং Design Patterns অনুসরণ করা কোডের মান, স্থিতিশীলতা এবং রক্ষণাবেক্ষণ সহজ করে তোলে। Repository Pattern, Singleton Pattern, Factory Pattern, এবং Observer Pattern এর মতো Design Patterns TypeORM-এ ডেটাবেস পরিচালনা সহজ করে এবং কোডের পুনঃব্যবহারযোগ্যতা এবং স্কেলেবিলিটি বাড়ায়। TypeORM এর মাইগ্রেশন, ক্যাসকেড অপারেশন এবং বিভিন্ন সম্পর্ক ডেকোরেটরের মতো ফিচারগুলোও ডেভেলপারদের ডেটাবেস পরিচালনা ও রক্ষণাবেক্ষণে সহায়তা করে।

Content added By

Clean Code Structure এবং Maintainability

Clean Code বা পরিষ্কার কোড হল কোড লেখার একটি পদ্ধতি যেখানে কোড সহজ, পরিষ্কার, এবং বুঝতে সহজ হয়, যাতে এটি দীর্ঘ সময় ধরে রক্ষণাবেক্ষণ করা এবং সংশোধন করা যায়। Maintainability (রক্ষণাবেক্ষণযোগ্যতা) হল এমন একটি গুণ, যা কোডের সহজ পরিবর্তন, বাগ ফিক্সিং এবং ফিচার এক্সটেনশন সহজ করে তোলে।

TypeORM ব্যবহার করার সময় কোডের পরিষ্কার এবং রক্ষণাবেক্ষণযোগ্য গঠন বজায় রাখা গুরুত্বপূর্ণ, কারণ এর সাথে অনেক ডেটাবেস অপারেশন, সম্পর্ক এবং মডেল যুক্ত থাকে, যা কোডের জটিলতা বাড়াতে পারে। TypeORM এবং অন্যান্য টুলস ব্যবহার করে কোডের মান বজায় রাখতে কিছু সেরা প্র্যাকটিস মেনে চলা উচিত।


১. ডিরেক্টরি স্ট্রাকচার

TypeORM প্রজেক্টে কোডের পরিষ্কার এবং রক্ষণাবেক্ষণযোগ্য স্ট্রাকচার নিশ্চিত করার জন্য একটি ভাল ডিরেক্টরি স্ট্রাকচার গুরুত্বপূর্ণ। এখানে একটি উদাহরণ দেওয়া হল, যা মডুলার স্টাইলে গঠন করা হয়েছে:

src/
│
├── entity/                  # ডেটাবেসের Entity গুলি (Model)
│   ├── User.ts
│   └── Post.ts
│
├── repository/              # Repository গুলি
│   ├── UserRepository.ts
│   └── PostRepository.ts
│
├── service/                 # ব্যবসায়িক লজিক
│   ├── UserService.ts
│   └── PostService.ts
│
├── controller/              # API Controller
│   ├── UserController.ts
│   └── PostController.ts
│
├── migration/               # মাইগ্রেশন ফাইলগুলি
│   ├── 1615348341001-CreateUser.ts
│   └── 1615348352001-CreatePost.ts
│
├── utils/                   # সহায়ক ইউটিলিটি ফাংশন
│   ├── Logger.ts
│   └── Validator.ts
│
└── app.ts                   # এপ্লিকেশন স্টার্টপয়েন্ট

এখানে:

  • entity/: আপনার ডেটাবেস টেবিলগুলির প্রতিনিধিত্বকারী ক্লাস (এন্টিটি) গুলি থাকবে।
  • repository/: ডেটাবেস থেকে ডেটা অ্যাক্সেস করতে ব্যবহৃত রেপোজিটরি গুলি।
  • service/: ব্যবসায়িক লজিক বা অ্যাপ্লিকেশনের ব্যাকএন্ড লজিক যেমন, ডেটা প্রসেসিং ইত্যাদি।
  • controller/: API রাউটগুলি যেখানে আপনার HTTP রিকোয়েস্ট হ্যান্ডলিং হয়।
  • migration/: ডেটাবেস মাইগ্রেশন স্ক্রিপ্টগুলি।
  • utils/: সহায়ক ইউটিলিটি ফাংশনগুলি যেমন লগিং, ভ্যালিডেশন ইত্যাদি।

এই গঠন অনুযায়ী, আপনার কোড ছোট ছোট অংশে ভাগ হয়ে যাবে, যা পরবর্তীতে কোডের উন্নতি এবং রক্ষণাবেক্ষণ সহজ করবে।


২. Naming Conventions (নামকরণের কনভেনশন)

নামকরণের কনভেনশন অনুসরণ করা কোড পরিষ্কার এবং সহজে বোঝা যায় এমন করতে সাহায্য করে। TypeORM এ কিছু সাধারণ নামকরণের নিয়ম মেনে চললে কোড আরও রক্ষণাবেক্ষণযোগ্য হবে।

  • Entity Naming: Entity নাম সাধারণত সংখ্যাতীত এবং কাল্পনিক শব্দ হিসেবে রাখা উচিত, যেমন User, Post, Order, ইত্যাদি।
  • Repository Naming: Repository গুলির নাম UserRepository, PostRepository ইত্যাদি হওয়া উচিত, যাতে সম্পর্কিত Entity এর নাম পরিষ্কারভাবে বোঝা যায়।
  • Service Naming: সার্ভিস ক্লাসের নাম যেমন UserService, PostService ইত্যাদি রাখা উচিত, যাতে পরিষ্কার হয় যে এটি কোন ব্যবসায়িক লজিক পরিচালনা করছে।
  • Controller Naming: কন্ট্রোলার ক্লাসের নাম যেমন UserController, PostController ইত্যাদি রাখা উচিত।

এছাড়া, নামের কনভেনশন যতটা সম্ভব বর্ণনামূলক হওয়া উচিত, যাতে কোড পড়া সহজ হয় এবং ডেভেলপারদের দ্রুত বুঝতে সাহায্য করে।


৩. Modularization (মডুলারিটি)

কোডকে ছোট ছোট অংশে ভাগ করা উচিত যাতে একে অপরের থেকে স্বাধীনভাবে কাজ করতে পারে। TypeORM ব্যবহার করার সময় Services, Repositories, এবং Controllers এর মধ্যে স্পষ্ট বিভাজন রাখতে হবে।

উদাহরণ:

// UserService.ts
import { getRepository } from "typeorm";
import { User } from "../entity/User";

export class UserService {
  private userRepository = getRepository(User);

  async getAllUsers() {
    return this.userRepository.find();
  }

  async getUserById(id: number) {
    return this.userRepository.findOne(id);
  }

  async createUser(name: string, email: string) {
    const user = new User();
    user.name = name;
    user.email = email;
    await this.userRepository.save(user);
    return user;
  }
}

এখানে UserService ক্লাসটি UserRepository এর সাথে ইন্টারঅ্যাক্ট করে এবং ইউজারের জন্য প্রয়োজনীয় লজিক আলাদা করে দেয়। একইভাবে, অন্যান্য সেবাগুলিকে তাদের নিজস্ব ডিরেক্টরিতে আলাদা রাখা উচিত।


৪. Error Handling (এরর হ্যান্ডলিং)

কোনও অ্যাপ্লিকেশনে ত্রুটি ব্যবস্থাপনা অত্যন্ত গুরুত্বপূর্ণ। TypeORM এ ত্রুটি ব্যবস্থাপনার জন্য আপনি try-catch blocks, custom error classes, এবং error logging ব্যবহার করতে পারেন। এটা কোডের রক্ষণাবেক্ষণযোগ্যতা এবং ডিবাগিং সহজ করে।

উদাহরণ:

import { User } from "../entity/User";
import { getRepository } from "typeorm";

export class UserService {
  private userRepository = getRepository(User);

  async createUser(name: string, email: string) {
    try {
      const user = new User();
      user.name = name;
      user.email = email;

      await this.userRepository.save(user);
      return user;
    } catch (error) {
      console.error("Error creating user: ", error);
      throw new Error("User creation failed");
    }
  }
}

এখানে, try-catch ব্লকের মাধ্যমে error handling করা হয়েছে, এবং ত্রুটির বিস্তারিত লগ করা হয়েছে।


৫. Code Reusability (কোড পুনঃব্যবহারযোগ্যতা)

কোডের পুনঃব্যবহারযোগ্যতা নিশ্চিত করতে, একই ধরনের কোড বারবার না লিখে তাকে ফাংশন বা ক্লাসে আড়াল করা উচিত। TypeORM এ, Services এবং Repositories ব্যবহার করে কোড পুনঃব্যবহারযোগ্য এবং কার্যকরী করা যায়।

উদাহরণ:

// BaseService.ts
import { Repository, EntityTarget, EntityManager } from "typeorm";

export class BaseService<T> {
  private repository: Repository<T>;

  constructor(entity: EntityTarget<T>) {
    this.repository = getRepository(entity);
  }

  async findAll() {
    return this.repository.find();
  }

  async findById(id: number) {
    return this.repository.findOne(id);
  }

  async save(entity: T) {
    return this.repository.save(entity);
  }
}

এখানে, BaseService ক্লাসটি পুনঃব্যবহারযোগ্য একটি সাধারণ সেবা, যেটি বিভিন্ন Entity এর সাথে ব্যবহার করা যেতে পারে। এর ফলে একাধিক Entity এর জন্য আলাদা আলাদা সার্ভিস লেখার প্রয়োজন নেই।


৬. Testability (টেস্টযোগ্যতা)

Testability (টেস্টযোগ্যতা) নিশ্চিত করতে কোডের মধ্যে একক (unit) এবং ইন্টিগ্রেশন টেস্টিং করার জন্য পরিষ্কারভাবে লজিক এবং নির্ভরতা (dependencies) আলাদা করে রাখা উচিত।

TypeORM ব্যবহারে, Repository এবং Service ক্লাসগুলো সহজে টেস্টযোগ্য হতে পারে যদি সেগুলোকে যথাযথভাবে মডুলার এবং নির্ভরশীলতার ওপর ভিত্তি করে সাজানো হয়।


সারাংশ

Clean Code Structure এবং Maintainability TypeORM প্রজেক্টের জন্য অত্যন্ত গুরুত্বপূর্ণ। পরিষ্কার কোড লিখতে, আপনাকে ডিরেক্টরি স্ট্রাকচার, নামকরণের কনভেনশন, মডুলারিটি, ত্রুটি ব্যবস্থাপনা, পুনঃব্যবহারযোগ্য কোড, এবং টেস্টযোগ্যতা নিশ্চিত করতে হবে। TypeORM-এ এই সেরা প্র্যাকটিসগুলির অনুসরণ করলে, কোড আরও স্কেলেবল, রক্ষণাবেক্ষণযোগ্য, এবং সহজে পরিবর্তনযোগ্য হবে, যা আপনার প্রজেক্টের দীর্ঘমেয়াদী সফলতা নিশ্চিত করবে।

Content added By

Repository এবং Service Layers এর ধারণা

Repository এবং Service Layers দুটি গুরুত্বপূর্ণ ডিজাইন প্যাটার্ন যা TypeORM সহ অনেক আধুনিক অ্যাপ্লিকেশন ডিজাইন ও আর্কিটেকচারতে ব্যবহৃত হয়। এই দুটি স্তরের ব্যবহার অ্যাপ্লিকেশনের কোডকে আরও শুদ্ধ, পুনঃব্যবহারযোগ্য এবং স্কেলযোগ্য করে তোলে।

  • Repository Layer: ডেটাবেস অপারেশন এবং ডেটা ম্যানিপুলেশন এর জন্য ব্যবহৃত হয়। এটি TypeORM-এ ডেটাবেস থেকে ডেটা সংগ্রহ, আপডেট, ডিলিট, এবং সেভ করা পরিচালনা করে। সাধারণভাবে, Repository layer ডেটাবেস সম্পর্কিত সমস্ত লজিক একত্রিত করে।
  • Service Layer: এটি অ্যাপ্লিকেশনের বিজনেস লজিক ধারণ করে। সার্ভিস লেয়ার ডেটাবেস অপারেশন পরিচালনা না করে, বরং Repository Layer থেকে ডেটা নিয়ে বিজনেস লজিক প্রয়োগ করে। এটি মূলত ক্লায়েন্টের রিকোয়েস্টের ওপর বিজনেস লজিক প্রয়োগ করতে সহায়তা করে।

এই দুটি স্তরের আলাদা করা অ্যাপ্লিকেশনকে ডি-কাপলড এবং আরও ম্যানেজেবল করে তোলে, যেহেতু Repository শুধুমাত্র ডেটাবেসের সাথে সম্পর্কিত কাজ করবে এবং Service Layer বিজনেস লজিক পরিচালনা করবে।


TypeORM এ Repository এবং Service Layers এর Separation

১. Repository Layer

Repository Layer TypeORM এর Entity সম্পর্কিত সমস্ত ডেটাবেস অপারেশন পরিচালনা করবে। TypeORM এর getRepository() ফাংশন ব্যবহার করে আপনি ডেটাবেস টেবিলের উপর CRUD (Create, Read, Update, Delete) অপারেশন পরিচালনা করতে পারবেন।

উদাহরণ: User Entity এর জন্য Repository তৈরি:

import { EntityRepository, Repository } from "typeorm";
import { User } from "./User";

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  // কাস্টম কুয়েরি এবং ফাংশন এখানে যোগ করা যেতে পারে
}

এখানে, UserRepository এর মধ্যে আপনি কাস্টম কুয়েরি বা অন্যান্য ডেটাবেস সম্পর্কিত অপারেশন যুক্ত করতে পারেন। এতে সার্ভিস লেয়ারের কাজ সহজ হয়ে যাবে কারণ সার্ভিস লেয়ার শুধু এই রেপোজিটরি ব্যবহার করবে।

২. Service Layer

Service Layer হলো এমন একটি লেয়ার যা অ্যাপ্লিকেশনের বিজনেস লজিক ধারণ করে। এখানে আমরা Repository এর মাধ্যমে ডেটা রিটার্ন করি, তার পর সেই ডেটার ওপর বিজনেস লজিক প্রয়োগ করি এবং ফলাফল রিটার্ন করি।

উদাহরণ: User Service এর জন্য একটি Service ক্লাস:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserRepository } from './user.repository';
import { User } from './user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UserRepository)
    private userRepository: UserRepository
  ) {}

  // ইউজার তৈরি করা
  async createUser(name: string, email: string): Promise<User> {
    const user = new User();
    user.name = name;
    user.email = email;
    
    return this.userRepository.save(user);
  }

  // ইউজার পাওয়া
  async getUserById(id: number): Promise<User | undefined> {
    return this.userRepository.findOne(id);
  }

  // সব ইউজার পাওয়া
  async getAllUsers(): Promise<User[]> {
    return this.userRepository.find();
  }
}

এখানে, UserService একটি সার্ভিস ক্লাস যা UserRepository এর মাধ্যমে ডেটাবেসের সাথে যোগাযোগ করছে। সার্ভিস লেয়ারকে বিজনেস লজিক পরিচালনা করতে বলা হয়েছে এবং Repository কে শুধুমাত্র ডেটা রিটার্ন বা ম্যানিপুলেট করার দায়িত্ব দেওয়া হয়েছে।

৩. Controller Layer (Optional)

অ্যাপ্লিকেশন অ্যাক্সেস করার জন্য একটি কন্ট্রোলার লেয়ার হতে পারে (যেমন, NestJS-এ)। কন্ট্রোলার রিকোয়েস্ট গ্রহণ করবে এবং সার্ভিস লেয়ারকে কল করবে।

উদাহরণ: UserController:

import { Controller, Get, Param, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post()
  create(@Body() createUserDto: { name: string; email: string }): Promise<User> {
    return this.userService.createUser(createUserDto.name, createUserDto.email);
  }

  @Get(':id')
  getUser(@Param('id') id: number): Promise<User | undefined> {
    return this.userService.getUserById(id);
  }

  @Get()
  getAllUsers(): Promise<User[]> {
    return this.userService.getAllUsers();
  }
}

এখানে, UserController সার্ভিস লেয়ারের ফাংশনগুলোকে কল করছে এবং রিকোয়েস্টের মাধ্যমে ডেটা রিটার্ন করছে।


Service Layer এবং Repository Layer এর মধ্যে পার্থক্য

ফিচারRepository LayerService Layer
কাজডেটাবেস অপারেশন পরিচালনা (CRUD)বিজনেস লজিক পরিচালনা
ফোকাসডেটাবেস এবং Entity এর উপরঅ্যাপ্লিকেশনের বিজনেস লজিক এবং কাজের প্রবাহ
রোলডেটা ম্যানিপুলেশন এবং ডেটাবেস সম্পর্কিত কাজRepository থেকে ডেটা নিয়ে বিজনেস লজিক প্রয়োগ করা
ডিপেন্ডেন্সিEntity এবং TypeORM অপারেশনগুলোর উপর নির্ভরশীলRepository এবং অন্যান্য সার্ভিসের উপর নির্ভরশীল
প্রধান উদ্দেশ্যডেটাবেসের সাথে সংযুক্ত থাকাব্যবসায়িক ফাংশনগুলো এবং কার্যকরী লজিক তৈরি করা

সারাংশ

Repository এবং Service Layers এর আলাদা করা অ্যাপ্লিকেশনের উন্নয়ন প্রক্রিয়া সহজ করে, কোড আরও পরিষ্কার এবং ম্যানেজযোগ্য করে তোলে। Repository Layer শুধুমাত্র ডেটাবেসের সাথে কাজ করে, এবং Service Layer বিজনেস লজিক পরিচালনা করে। TypeORM-এ এই দুটির ব্যবহার অ্যাপ্লিকেশন কোডের সংরক্ষণযোগ্যতা এবং স্কেলেবিলিটি বাড়ায়, কারণ এগুলো একে অপর থেকে স্বাধীন থাকে। Service Layer ডেটার সাথে সম্পর্কিত সমস্ত বিজনেস লজিক রাখবে এবং Repository Layer শুধুমাত্র ডেটা সংরক্ষণ, রিট্রিভ, আপডেট এবং ডিলিট করবে।

Content added By

zRY (Don’t Repeat Yourself) প্যাটার্ন কি?

DRY (Don’t Repeat Yourself) একটি সফটওয়্যার ডেভেলপমেন্ট প্যাটার্ন যা কোড রিইউসেবিলিটি এবং স্কেলেবিলিটি বাড়াতে সাহায্য করে। এর মূল ধারণা হল: কোডের পুনরাবৃত্তি এড়ানো, অর্থাৎ একই কোড বারবার না লিখে এক জায়গায় সঠিকভাবে কোড লেখা এবং রিইউজ করা।

DRY প্যাটার্নের উদ্দেশ্য হল:

  • কোড পুনঃব্যবহারযোগ্য এবং রক্ষণাবেক্ষণযোগ্য করা।
  • কোডে গড়বড় এবং বাগ কমিয়ে আনা।
  • কোড পড়তে এবং বুঝতে সহজ করা।

TypeORM-এ DRY প্যাটার্ন অনুসরণ করলে, আপনি ডেটাবেস সম্পর্কিত কাজগুলোকে আরও পরিষ্কার, সহজ এবং মডুলার করতে পারেন।


TypeORM এ DRY প্যাটার্ন মেনে কোড লেখা

TypeORM ব্যবহার করার সময় DRY প্যাটার্ন মেনে কোড লেখা অনেক সুবিধা দেয়। এর মাধ্যমে আপনি ডেটাবেস অপারেশনগুলিকে পুনঃব্যবহারযোগ্য এবং অ্যাবস্ট্রাক্ট (abstract) করে রাখতে পারেন, যাতে কোডের গঠন পরিষ্কার এবং কার্যকরী হয়। এখানে আমরা দেখব TypeORM এ DRY প্যাটার্ন কীভাবে প্রয়োগ করতে পারি।


১. Repository Pattern ব্যবহার করা

Repository Pattern হল একটি ডিজাইন প্যাটার্ন, যা TypeORM এ ডেটাবেস অপারেশনগুলিকে এক জায়গায় রাখে। এটি ডেটাবেস টেবিলের জন্য CRUD অপারেশনগুলির সাধারণ কোড পুনরাবৃত্তি এড়াতে সাহায্য করে।

উদাহরণ:

ধরা যাক, আমাদের দুটি Entity আছে: User এবং Post। প্রতিটি Entity এর জন্য Repository Pattern ব্যবহার করা উচিত, যাতে একই ধরনের ডেটাবেস অপারেশন কোডে পুনরাবৃত্তি না হয়।

import { EntityRepository, Repository } from "typeorm";
import { User } from "../entity/User";

@EntityRepository(User)
export class UserRepository extends Repository<User> {

  // Retrieve users by name
  async findByName(name: string) {
    return this.find({ where: { name } });
  }

  // Create and save a new user
  async createAndSaveUser(name: string, email: string) {
    const user = new User();
    user.name = name;
    user.email = email;
    return this.save(user);
  }

}

এখানে, UserRepository ক্লাসে সাধারণ ডেটাবেস অপারেশনগুলি (যেমন findByName এবং createAndSaveUser) রাখা হয়েছে। এর ফলে কোড পুনঃব্যবহারযোগ্য হয় এবং যেখানে দরকার সেখানে এটি সহজে ব্যবহার করা যায়।


২. Services ব্যবহার করা

Service Layer ব্যবহার করার মাধ্যমে ডেটাবেসের সাথে যোগাযোগের লজিক আলাদা করা হয়। এতে অ্যাপ্লিকেশনের মূল লজিক এবং ডেটাবেস অপারেশন আলাদা থাকে, এবং এই ধরনের কোড পুনরাবৃত্তি কমে যায়।

উদাহরণ:

import { getCustomRepository } from "typeorm";
import { UserRepository } from "../repositories/UserRepository";
import { User } from "../entity/User";

class UserService {

  // Get users by name
  async getUsersByName(name: string): Promise<User[]> {
    const userRepository = getCustomRepository(UserRepository);
    return userRepository.findByName(name);
  }

  // Create a new user
  async createUser(name: string, email: string): Promise<User> {
    const userRepository = getCustomRepository(UserRepository);
    return userRepository.createAndSaveUser(name, email);
  }
}

এখানে, UserService ক্লাসে ডেটাবেসের সাথে সম্পর্কিত লজিক রাখলে, ডেটাবেস অপারেশনগুলো এক জায়গায় থাকে এবং UserRepository ক্লাসটি কোড পুনরাবৃত্তি না করে ব্যবহার করা যেতে পারে।


৩. Common Functions ব্যবহার করা

Utility Functions বা Helper Functions ব্যবহার করে সাধারণ কাজগুলো এক জায়গায় রাখুন। উদাহরণস্বরূপ, আপনি যদি বারবার একই ধরনের ডেটা ভ্যালিডেশন বা ক্যালকুলেশন করেন, তাহলে সেই কাজগুলো একটি ফাংশনে রেখে ব্যবহার করুন।

উদাহরণ:

// utils/validation.ts
export const validateEmail = (email: string): boolean => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
};

// services/UserService.ts
import { validateEmail } from "../utils/validation";

class UserService {

  async createUser(name: string, email: string): Promise<User> {
    if (!validateEmail(email)) {
      throw new Error("Invalid email format");
    }
    const userRepository = getCustomRepository(UserRepository);
    return userRepository.createAndSaveUser(name, email);
  }
}

এখানে, validateEmail একটি সাধারণ ভ্যালিডেশন ফাংশন তৈরি করা হয়েছে, যা পুনঃব্যবহারযোগ্য এবং কোডের পুনরাবৃত্তি এড়ায়।


৪. Abstract Class এবং Inheritance ব্যবহার করা

কখনও কখনও আপনার প্রোজেক্টে বিভিন্ন ধরনের ডেটাবেস টেবিলের জন্য একই ধরনের অপারেশন দরকার হয়। আপনি একাধিক রিপোজিটরি তৈরি করার পরিবর্তে একটি Abstract Class ব্যবহার করে সাধারণ অপারেশনগুলো একটি জায়গায় রাখতে পারেন এবং অন্যান্য ক্লাসে ইনহেরিট করতে পারেন।

উদাহরণ:

import { EntityRepository, Repository } from "typeorm";

// Abstract Repository
export abstract class AbstractRepository<T> extends Repository<T> {

  async findById(id: number) {
    return this.findOne(id);
  }

  async findAll() {
    return this.find();
  }

  async saveEntity(entity: T) {
    return this.save(entity);
  }
}

// Specific Repository
@EntityRepository(User)
export class UserRepository extends AbstractRepository<User> {
  // Specific methods related to User can go here
}

এখানে, AbstractRepository ক্লাসে সাধারণ অপারেশনগুলো রাখা হয়েছে এবং UserRepository ক্লাসে সেই সাধারণ ফাংশনগুলো ইনহেরিট করা হয়েছে। এতে কোড পুনঃব্যবহারযোগ্য হয় এবং আপনার কোডে পুনরাবৃত্তি কমে যায়।


৫. Migration এবং Seeders ব্যবহারের মাধ্যমে ডেটাবেস মডেল আপডেট করা

TypeORM Migrations এবং Seeders এর মাধ্যমে ডেটাবেসের কাঠামো এবং ডেটা আপডেট করতে পারেন। এতে কোড পুনরাবৃত্তি কমে এবং ডেটাবেস স্কিমা নির্দিষ্টভাবে সংরক্ষিত থাকে।

Migration উদাহরণ:

typeorm migration:create -n UserMigration

এর পর, নতুন মাইগ্রেশন ফাইল তৈরি হলে সেখানে আপনি ডেটাবেসের আপডেট বা পরিবর্তন করতে পারবেন। উদাহরণস্বরূপ, নতুন টেবিল তৈরি বা কলাম যোগ করার জন্য:

import {MigrationInterface, QueryRunner} from "typeorm";

export class UserMigration implements MigrationInterface {

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255), email VARCHAR(255))`);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`DROP TABLE users`);
    }

}

এখানে, Migrations ব্যবহার করার মাধ্যমে আপনি কোড পুনঃব্যবহারযোগ্য এবং স্কেলেবল করতে পারেন, যেখানে ডেটাবেসের পরিবর্তনগুলো একটি নির্দিষ্ট স্কিমায় রাখা যায়।


সারাংশ

DRY (Don’t Repeat Yourself) প্যাটার্ন TypeORM এ কোডের পুনঃব্যবহারযোগ্যতা এবং কার্যকারিতা বাড়াতে সাহায্য করে। Repository Pattern, Services, Helper Functions, Abstract Classes, এবং Migrations ব্যবহার করে আপনি পুনরাবৃত্তি এড়িয়ে যাবেন এবং কোডটিকে আরও পরিষ্কার, রক্ষণাবেক্ষণযোগ্য এবং স্কেলেবল রাখতে পারবেন। TypeORM এর মাধ্যমে এসব প্যাটার্ন ব্যবহার করে আপনি আরও প্রফেশনাল, পড়তে সুবিধাজনক এবং সহজে রক্ষণাবেক্ষণযোগ্য কোড তৈরি করতে পারবেন।

Content added By

TypeORM এবং Design Patterns

TypeORM হল একটি শক্তিশালী ORM (Object-Relational Mapping) লাইব্রেরি যা TypeScript এবং JavaScript দিয়ে ডেটাবেস ম্যানেজমেন্ট এবং মডেলিং সহজ করে তোলে। যখন আপনি একটি Large Scale Application (বিশাল আকারের অ্যাপ্লিকেশন) তৈরি করেন, তখন কোডের স্কেল, পরিচালনাযোগ্যতা, এবং রক্ষণাবেক্ষণ অত্যন্ত গুরুত্বপূর্ণ হয়ে ওঠে। এই ধরনের অ্যাপ্লিকেশন তৈরি করার সময় Design Patterns ব্যবহার করলে আপনার কোড আরও পরিষ্কার, রিডেবল এবং টেকসই হয়। Design Patterns এমন পদ্ধতি বা রুলস যা কোডের পুনঃব্যবহারযোগ্যতা এবং প্রজেক্টের স্কেলেবলিটি বাড়ায়।

TypeORM এর সাথে Large Scale অ্যাপ্লিকেশন তৈরি করার সময় বিভিন্ন Design Patterns ব্যবহার করা হয়, যাতে কোড আরও মডুলার, রক্ষণাবেক্ষণযোগ্য এবং উন্নত হয়।


Design Patterns Types

TypeORM ব্যবহার করে Large Scale অ্যাপ্লিকেশন তৈরি করার সময় কিছু গুরুত্বপূর্ণ Design Patterns রয়েছে, যেমন:

  1. Repository Pattern
  2. Service Layer Pattern
  3. Singleton Pattern
  4. Factory Pattern
  5. Decorator Pattern
  6. Observer Pattern

এখানে প্রতিটি প্যাটার্ন কীভাবে কাজ করে এবং TypeORM অ্যাপ্লিকেশনে কিভাবে প্রয়োগ করা যায় তা আলোচনা করা হবে।


১. Repository Pattern

Repository Pattern ডেটাবেসে ডেটা অ্যাক্সেসের জন্য একটি বিশেষ অবজেক্ট বা ক্লাস তৈরি করার কৌশল। এটি ডেটাবেস অপারেশনগুলিকে অ্যাপ্লিকেশন লজিক থেকে আলাদা করে এবং কোডকে পরিষ্কার ও রক্ষণাবেক্ষণযোগ্য করে তোলে।

TypeORM-এ Repository Pattern ব্যবহারের মাধ্যমে আপনি ডেটাবেসের সাথে সম্পর্কিত সমস্ত অপারেশন একটি নির্দিষ্ট ক্লাসে রাখেন। এতে কোডে উন্নত বিচ্ছিন্নতা (separation of concerns) পাওয়া যায়।

উদাহরণ:

import { EntityRepository, Repository } from "typeorm";
import { User } from "./entity/User";

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  async findByEmail(email: string): Promise<User | undefined> {
    return this.findOne({ where: { email } });
  }
}

এখানে, UserRepository ক্লাস User Entity এর সাথে সম্পর্কিত সমস্ত ডেটাবেস অপারেশন পরিচালনা করে। এটি Repository Pattern ব্যবহার করে findByEmail ফাংশন তৈরি করেছে।


২. Service Layer Pattern

Service Layer Pattern হল একটি প্যাটার্ন যেখানে অ্যাপ্লিকেশনের ব্যবসায়িক লজিক আলাদা একটি service ক্লাসে রাখা হয়, যা ডেটা অ্যাক্সেস লেয়ারের (repository) সাথে কাজ করে। এটি অ্যাপ্লিকেশনের বিভিন্ন অংশের মধ্যে যোগাযোগের একটি মজবুত স্তর তৈরি করে, এবং কোডের রক্ষণাবেক্ষণ এবং এক্সটেনশন সহজ হয়।

উদাহরণ:

import { Injectable } from "@nestjs/common";
import { UserRepository } from "./repository/UserRepository";
import { User } from "./entity/User";

@Injectable()
export class UserService {
  constructor(private userRepository: UserRepository) {}

  async getUserByEmail(email: string): Promise<User | undefined> {
    return this.userRepository.findByEmail(email);
  }
}

এখানে, UserService ক্লাসটি UserRepository ব্যবহার করে ডেটা অ্যাক্সেস করার কাজটি করছে। এতে অ্যাপ্লিকেশনের ব্যবসায়িক লজিক আলাদা হয়ে যায় এবং অন্য অংশগুলো থেকে বিচ্ছিন্ন থাকে।


৩. Singleton Pattern

Singleton Pattern এমন একটি প্যাটার্ন যেখানে একটি ক্লাসের একটি মাত্র ইনস্ট্যান্স তৈরি হয় এবং তা একাধিক জায়গায় ব্যবহৃত হয়। TypeORM অ্যাপ্লিকেশনে এটি ব্যবহার করা যেতে পারে যেখানে একটি ডাটাবেস কানেকশন বা কনফিগারেশন একাধিক জায়গায় ব্যবহৃত হয়, কিন্তু আমরা চাই যে এটি একটি মাত্র ইনস্ট্যান্স হোক।

উদাহরণ:

import { createConnection } from "typeorm";

let connection;

export async function getDatabaseConnection() {
  if (!connection) {
    connection = await createConnection();
  }
  return connection;
}

এখানে, Singleton Pattern ব্যবহার করে ডাটাবেস কানেকশন নিশ্চিত করা হয়েছে যাতে একাধিক কানেকশন তৈরি না হয়।


৪. Factory Pattern

Factory Pattern এমন একটি প্যাটার্ন যেখানে অবজেক্ট তৈরির কাজ আলাদা একটি ফ্যাক্টরি ক্লাস বা ফাংশনের মাধ্যমে করা হয়। এটি অবজেক্ট তৈরি করার সময় এর কনফিগারেশন বা তৈরির পদ্ধতিকে অ্যাবস্ট্রাক্ট করে, যার ফলে কোডে এক্সটেনশন এবং রক্ষণাবেক্ষণ সহজ হয়।

উদাহরণ:

class UserFactory {
  static createUser(name: string, email: string): User {
    const user = new User();
    user.name = name;
    user.email = email;
    return user;
  }
}

এখানে, UserFactory একটি ফ্যাক্টরি ক্লাস যা User অবজেক্ট তৈরি করে।


৫. Decorator Pattern

Decorator Pattern হল একটি ডিজাইন প্যাটার্ন যা একটি অবজেক্টের আচরণে পরিবর্তন আনতে ব্যবহার করা হয়, কিন্তু তার প্রকৃত ক্লাসে পরিবর্তন না করে। TypeORM-এ Decorator Pattern ব্যবহৃত হয় যখন আমরা অ্যাসোসিয়েশন (Relations), ভ্যালিডেশন, বা কাস্টম লজিক যোগ করতে চাই।

উদাহরণ:

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}

এখানে, @Entity(), @PrimaryGeneratedColumn(), এবং @Column() ডেকোরেটরগুলি Decorator Pattern ব্যবহার করে Entity এবং Column তৈরি করছে।


৬. Observer Pattern

Observer Pattern হল একটি প্যাটার্ন যেখানে একটি অবজেক্ট তার পরিবর্তনের জন্য অন্যান্য অবজেক্টকে অবহিত করে। TypeORM-এ এটি সাধারণত Event Listeners বা Subscribers এর মাধ্যমে ব্যবহৃত হয়, যা ডেটাবেসের পরিবর্তন বা ট্রানজ্যাকশন সম্পর্কে অবহিত করে।

উদাহরণ:

import { EntitySubscriberInterface, EventSubscriber, InsertEvent } from "typeorm";
import { User } from "./User";

@EventSubscriber()
export class UserSubscriber implements EntitySubscriberInterface<User> {
  listenTo() {
    return User;
  }

  afterInsert(event: InsertEvent<User>) {
    console.log(`User inserted: ${event.entity.name}`);
  }
}

এখানে, UserSubscriber ব্যবহার করে আমরা Observer Pattern প্রয়োগ করেছি যাতে ইউজার ইনসার্ট হওয়ার পর অবহিত হওয়া যায়।


সারাংশ

Design Patterns ব্যবহার করে TypeORM-এ Large Scale Applications তৈরি করলে অ্যাপ্লিকেশনটির স্থিতিশীলতা, রক্ষণাবেক্ষণযোগ্যতা, এবং স্কেলেবিলিটি বৃদ্ধি পায়। Repository Pattern, Service Layer Pattern, Singleton Pattern, Factory Pattern, Decorator Pattern এবং Observer Pattern এগুলোর মধ্যে কিছু গুরুত্বপূর্ণ প্যাটার্ন যা TypeORM ব্যবহার করে Large Scale অ্যাপ্লিকেশন ডিজাইন এবং উন্নত করার জন্য কার্যকর। এই প্যাটার্নগুলো অ্যাপ্লিকেশনের কাঠামো পরিষ্কার করে এবং ভবিষ্যতে এক্সটেনশন এবং পরিবর্তন সহজ করে তোলে।

Content added By
Promotion

Are you sure to start over?

Loading...