GraphQL এবং TypeORM এর পরিচিতি
GraphQL একটি জিজ্ঞাসা ভাষা (query language) এবং API এর জন্য একটি রানটাইম, যা ক্লায়েন্টদেরকে তাদের প্রয়োজনীয় ডেটা সুনির্দিষ্টভাবে অনুরোধ করতে সক্ষম করে। এটি REST API এর বিকল্প হিসাবে ব্যবহৃত হয়, কারণ GraphQL এ ক্লায়েন্ট শুধুমাত্র প্রয়োজনীয় ডেটা পাঠায় এবং সার্ভার শুধুমাত্র সেই ডেটা রিটার্ন করে, ফলে কার্যকরী এবং কম্প্যাক্ট ডেটা ট্রান্সফার হয়।
TypeORM একটি জনপ্রিয় ORM (Object-Relational Mapping) লাইব্রেরি যা TypeScript এবং JavaScript এর জন্য ডেটাবেস পরিচালনা সহজ করে তোলে। TypeORM ব্যবহার করলে ডেটাবেসের টেবিলগুলোকে অবজেক্ট হিসেবে ম্যাপ করা যায় এবং জটিল SQL কুয়েরি লেখার প্রয়োজন হয় না।
GraphQL এবং TypeORM এর সংমিশ্রণ Web Development-এ শক্তিশালী ডেটা ম্যানিপুলেশন এবং API প্রদান করতে পারে। এই গাইডে, আমরা দেখব TypeORM এর সাথে GraphQL কিভাবে ইন্টিগ্রেট করা যায়।
TypeORM এবং GraphQL ইন্টিগ্রেশন: একটি সাধারণ প্রক্রিয়া
১. প্রকল্প সেটআপ
প্রথমে, আমাদের Node.js এবং TypeScript ভিত্তিক একটি প্রকল্প তৈরি করতে হবে। এরপর, আমরা TypeORM, GraphQL, এবং অন্যান্য প্রয়োজনীয় লাইব্রেরি ইনস্টল করব।
১.১ Node.js প্রকল্প তৈরি করা:
mkdir typeorm-graphql-project
cd typeorm-graphql-project
npm init -y
১.২ প্রয়োজনীয় প্যাকেজ ইনস্টল করা:
npm install typeorm graphql @nestjs/graphql typeorm-graphql apollo-server-express express reflect-metadata
npm install typescript ts-node @types/node --save-dev
- typeorm: TypeORM লাইব্রেরি ইনস্টল।
- graphql: GraphQL লাইব্রেরি।
- @nestjs/graphql: NestJS এর জন্য GraphQL।
- apollo-server-express: Apollo Server যা Express এ GraphQL চালাতে সাহায্য করে।
১.৩ TypeScript কনফিগারেশন:
npx tsc --init
এটি tsconfig.json ফাইল তৈরি করবে যেখানে TypeScript কম্পাইলেশন সেটিংস কনফিগার করা হয়।
২. TypeORM কনফিগারেশন
TypeORM সেটআপের জন্য একটি ডাটাবেস কনফিগারেশন ফাইল তৈরি করতে হবে।
২.১ ormconfig.json ফাইল তৈরি করা:
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "postgres",
"password": "your_password",
"database": "your_database",
"entities": ["src/entity/*.ts"],
"synchronize": true
}
এখানে, আমরা PostgreSQL ডাটাবেস ব্যবহার করেছি, তবে আপনি অন্য ডেটাবেস ড্রাইভার যেমন MySQL, SQLite ইত্যাদি ব্যবহার করতে পারেন।
৩. Entity তৈরি করা
এখন একটি Entity তৈরি করা যাক, যাতে আমাদের ডাটাবেসের টেবিলের গঠন থাকে। এখানে, আমরা User নামক একটি Entity তৈরি করছি।
৩.১ User Entity তৈরি করা:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
}
এখানে, User Entity একটি টেবিলের প্রতিনিধিত্ব করছে যেটিতে id, name, এবং email কলাম রয়েছে।
৪. GraphQL স্কিমা তৈরি করা
GraphQL স্কিমা তৈরি করার জন্য আমরা @nestjs/graphql লাইব্রেরির @ObjectType, @Field ডেকোরেটর ব্যবহার করব।
৪.১ User স্কিমা তৈরি করা:
import { ObjectType, Field, Int } from '@nestjs/graphql';
@ObjectType()
export class UserType {
@Field(type => Int)
id: number;
@Field()
name: string;
@Field()
email: string;
}
এখানে, আমরা UserType নামে একটি GraphQL স্কিমা তৈরি করেছি, যেটি User Entity এর মত দেখতে।
৫. Resolver তৈরি করা
Resolver হল GraphQL এ রিকোয়েস্ট হ্যান্ডল করার জায়গা, যেখানে কুয়েরি এবং মিউটেশনগুলি ব্যাখ্যা করা হয়।
৫.১ User Resolver তৈরি করা:
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { UserType } from './user.model';
import { User } from './user.entity';
import { getRepository } from 'typeorm';
@Resolver(of => UserType)
export class UserResolver {
@Query(returns => [UserType])
async users(): Promise<User[]> {
return await getRepository(User).find();
}
@Mutation(returns => UserType)
async createUser(
@Args('name') name: string,
@Args('email') email: string,
): Promise<User> {
const user = new User();
user.name = name;
user.email = email;
return await getRepository(User).save(user);
}
}
এখানে, users কুয়েরি ব্যবহার করে সব User রিটার্ন করা হয়েছে এবং createUser মিউটেশন ব্যবহার করে নতুন ব্যবহারকারী তৈরি করা হয়েছে।
৬. GraphQL Server তৈরি করা
এখন আমাদের Apollo Server ব্যবহার করে GraphQL API চালু করতে হবে।
৬.১ Apollo Server ইনস্টলেশন এবং কনফিগারেশন:
import { ApolloServer } from 'apollo-server-express';
import express from 'express';
import { buildSchema } from 'type-graphql';
import { UserResolver } from './user.resolver';
import { createConnection } from 'typeorm';
async function startServer() {
const app = express();
// TypeORM connection
await createConnection();
// GraphQL schema
const schema = await buildSchema({
resolvers: [UserResolver],
});
// Apollo Server setup
const server = new ApolloServer({
schema,
});
// Apply middleware to Express app
server.applyMiddleware({ app });
app.listen(4000, () =>
console.log('Server is running at http://localhost:4000/graphql')
);
}
startServer();
এখানে, আমরা ApolloServer দিয়ে GraphQL API তৈরি করছি এবং UserResolver-এ ডিফাইন করা কুয়েরি এবং মিউটেশনগুলি চালু করছি।
৭. GraphQL API টেস্ট করা
আপনার অ্যাপ্লিকেশন চালু করার পর, আপনি http://localhost:4000/graphql URL এ গিয়ে GraphQL API টেস্ট করতে পারবেন। নিচে কিছু কুয়েরি এবং মিউটেশন উদাহরণ দেওয়া হলো:
Query (সব User দেখা):
query {
users {
id
name
email
}
}
Mutation (নতুন User তৈরি করা):
mutation {
createUser(name: "John Doe", email: "john@example.com") {
id
name
email
}
}
সারাংশ
TypeORM এর সাথে GraphQL ইন্টিগ্রেশন Web Development-এ শক্তিশালী এবং কার্যকরী API তৈরি করতে সাহায্য করে। আপনি TypeORM ব্যবহার করে ডেটাবেসের সাথে সম্পর্কিত ডেটা পরিচালনা করতে পারেন এবং GraphQL ব্যবহার করে ক্লায়েন্টদের জন্য প্রয়োজনীয় ডেটা নির্দিষ্টভাবে এবং কার্যকরীভাবে প্রদান করতে পারেন। এই গাইডে TypeORM এবং GraphQL এর সাথে ইন্টিগ্রেশন এবং সেটআপ করার প্রক্রিয়া বিস্তারিতভাবে ব্যাখ্যা করা হয়েছে, যাতে আপনি একটি পূর্ণাঙ্গ GraphQL API তৈরি করতে পারেন।
Microservices এর পরিচিতি
Microservices হল একটি সফটওয়্যার আর্কিটেকচার স্টাইল যেখানে একটি অ্যাপ্লিকেশনকে একাধিক ছোট, স্বতন্ত্র, এবং স্বাধীনভাবে পরিচালনাযোগ্য সেবা বা সার্ভিসে ভাগ করা হয়। প্রতিটি সার্ভিস একটি নির্দিষ্ট কাজ সম্পাদন করে এবং একটি নির্দিষ্ট ব্যবসায়িক প্রক্রিয়া বা বৈশিষ্ট্য পূর্ণ করে। প্রতিটি মাইক্রোসার্ভিসের নিজস্ব ডেটাবেস, এপিআই, এবং অন্যান্য উপাদান থাকতে পারে যা অন্য সার্ভিসের সাথে নির্ভরশীল না। এই আর্কিটেকচারে, প্রতিটি সার্ভিস স্বতন্ত্রভাবে ডেভেলপ, ডিপ্লয় এবং স্কেল করা যায়।
Microservices এর বৈশিষ্ট্য
- স্বতন্ত্রতা: প্রতিটি মাইক্রোসার্ভিস স্বতন্ত্রভাবে কাজ করে এবং একটি নির্দিষ্ট কাজ সম্পাদন করে।
- ডিপ্লয়মেন্ট এবং স্কেলিং: মাইক্রোসার্ভিসগুলো একে অপর থেকে স্বাধীনভাবে ডিপ্লয় এবং স্কেল করা যায়।
- ডেটাবেস ভিন্নতা: প্রতিটি সার্ভিসের নিজস্ব ডেটাবেস থাকতে পারে, যা তাদের একে অপরের উপর নির্ভরশীল না করে কাজ করতে সক্ষম করে।
- সাধারণ ইন্টারফেস: মাইক্রোসার্ভিসের মধ্যে যোগাযোগের জন্য সাধারণ ইন্টারফেস বা এপিআই ব্যবহৃত হয়, যেমন HTTP, gRPC, অথবা AMQP।
- পুনঃব্যবহারযোগ্যতা এবং রক্ষণাবেক্ষণ: ছোট ছোট সিস্টেমের কারণে কোড রক্ষণাবেক্ষণ এবং পুনঃব্যবহারযোগ্যতা সহজ হয়।
Microservices কেন প্রয়োজন?
মাইক্রোসার্ভিস আর্কিটেকচার ব্যবহার করার অনেক সুবিধা রয়েছে। এখানে মাইক্রোসার্ভিস ব্যবহারের কিছু প্রধান কারণ আলোচনা করা হলো:
১. স্কেলেবিলিটি
মাইক্রোসার্ভিস আর্কিটেকচারে, প্রতিটি সার্ভিসের নিজস্ব স্কেলিং পলিসি থাকে। এর মানে হল যে, যেকোনো একটি সার্ভিসের লোড বেশি হলে, শুধুমাত্র সেই সার্ভিসটি আলাদাভাবে স্কেল করা যাবে, পুরো অ্যাপ্লিকেশন স্কেল করার প্রয়োজন নেই। উদাহরণস্বরূপ, যদি একটি সার্ভিস প্রচুর ট্র্যাফিক পায়, তবে সেই সার্ভিসটি স্কেল আপ করা যাবে, কিন্তু অন্যান্য সার্ভিসগুলো অপরিবর্তিত থাকবে।
২. স্বতন্ত্র ডিপ্লয়মেন্ট
মাইক্রোসার্ভিস আর্কিটেকচারে, প্রতিটি সার্ভিস স্বাধীনভাবে ডিপ্লয় করা যায়। এতে কোনো একটি সার্ভিসে পরিবর্তন করলে পুরো অ্যাপ্লিকেশন ডিপ্লয় করার প্রয়োজন হয় না। এর ফলে ডেভেলপমেন্ট, টেস্টিং এবং ডিপ্লয়মেন্ট প্রক্রিয়া অনেক সহজ এবং দ্রুত হয়।
৩. টেকনোলজি স্ট্যাকের বৈচিত্র্য
মাইক্রোসার্ভিস আর্কিটেকচারে আপনি বিভিন্ন সার্ভিসের জন্য আলাদা টেকনোলজি স্ট্যাক ব্যবহার করতে পারেন। উদাহরণস্বরূপ, একটি সার্ভিসে Node.js ব্যবহার করা হতে পারে, অন্যটিতে Java অথবা Python ব্যবহার করা হতে পারে। এই ধরনের লচিলিটি সিস্টেমের নমনীয়তা বৃদ্ধি করে।
৪. ডেভেলপমেন্ট টিমের স্বাধীনতা
মাইক্রোসার্ভিস ব্যবহারের মাধ্যমে বিভিন্ন টিম একাধিক সার্ভিসের উপর কাজ করতে পারে এবং প্রতিটি টিম তাদের নির্দিষ্ট সার্ভিসে স্বাধীনভাবে কাজ করতে পারে। ফলে, ডেভেলপমেন্ট প্রক্রিয়া দ্রুত হয় এবং টিমগুলোর মধ্যে সংঘর্ষ (conflict) কম হয়।
৫. রক্ষণাবেক্ষণ এবং আপডেট সহজ
কারণ মাইক্রোসার্ভিস একে অপর থেকে স্বাধীন থাকে, তাই একটি সার্ভিসে কোনো পরিবর্তন বা আপডেট করার পর তা সহজে বাস্তবায়ন করা যায়। এক সার্ভিসে সমস্যা হলে, তা অন্য সার্ভিসের ওপর প্রভাব ফেলবে না, যা রক্ষণাবেক্ষণ প্রক্রিয়াকে অনেক সহজ করে তোলে।
৬. বিশাল অ্যাপ্লিকেশনের পরিচালনা সহজ
যখন একটি অ্যাপ্লিকেশন বড় এবং জটিল হয়ে ওঠে, তখন এটি মেইনটেন করা কঠিন হয়ে যায়। মাইক্রোসার্ভিস আর্কিটেকচারে, আপনি বড় অ্যাপ্লিকেশনটি ছোট ছোট পরিষ্কারভাবে চিহ্নিত সেবা বা সার্ভিসে ভাগ করতে পারেন, যা পুরো সিস্টেমের পরিচালনা এবং রক্ষণাবেক্ষণকে সহজ করে তোলে।
Microservices আর্কিটেকচারে TypeORM ব্যবহার
TypeORM একটি জনপ্রিয় ORM (Object Relational Mapper) যা TypeScript এবং JavaScript ব্যবহার করে ডেটাবেস ম্যানিপুলেশন সহজ করে তোলে। মাইক্রোসার্ভিস আর্কিটেকচারে TypeORM এর ব্যবহার অত্যন্ত কার্যকরী হতে পারে, কারণ এতে আপনি প্রতিটি মাইক্রোসার্ভিসের জন্য আলাদা ডেটাবেস কানেকশন এবং পৃথক ডেটাবেস মডেল ব্যবহার করতে পারবেন। TypeORM আপনাকে ডেটাবেস সম্পর্ক এবং CRUD অপারেশন সহজভাবে পরিচালনা করতে সাহায্য করে।
TypeORM এবং Microservices ব্যবহার করার উদাহরণ:
ধরা যাক, আপনি একটি User এবং Order সার্ভিস তৈরি করতে চান। প্রতিটি সার্ভিসের আলাদা ডেটাবেস থাকবে, এবং TypeORM তাদের সাথে যোগাযোগ করতে সাহায্য করবে।
UserService:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
OrderService:
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm";
import { User } from "./User";
@Entity()
export class Order {
@PrimaryGeneratedColumn()
id: number;
@Column()
orderDate: Date;
@ManyToOne(() => User)
user: User;
}
এখানে, UserService এবং OrderService আলাদা সার্ভিস হিসেবে থাকবে, তবে OrderService ইউজারের তথ্য ব্যবহার করতে পারে, যেহেতু সেখানে User এর সাথে সম্পর্ক স্থাপন করা হয়েছে।
ডেটাবেস কানেকশন:
TypeORM এর মাধ্যমে প্রতিটি সার্ভিসের জন্য ডেটাবেস কানেকশন এবং মাইগ্রেশন পৃথকভাবে পরিচালনা করা যাবে।
import { createConnection } from "typeorm";
import { User } from "./entity/User";
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "password",
database: "user_database",
entities: [User],
synchronize: true,
}).then(() => {
console.log("User Service connected");
}).catch(error => console.log(error));
এভাবে, মাইক্রোসার্ভিস আর্কিটেকচারে TypeORM ব্যবহার করে প্রতিটি সার্ভিসের ডেটাবেস ম্যানেজমেন্ট আলাদাভাবে এবং কার্যকরভাবে পরিচালনা করা সম্ভব।
সারাংশ
Microservices হল একটি সফটওয়্যার আর্কিটেকচার স্টাইল যা একটি বৃহৎ অ্যাপ্লিকেশনকে ছোট ছোট, স্বাধীনভাবে পরিচালনাযোগ্য সার্ভিসে বিভক্ত করে। মাইক্রোসার্ভিস আর্কিটেকচার ব্যবহারের অনেক সুবিধা রয়েছে, যেমন স্কেলেবিলিটি, রক্ষণাবেক্ষণ সহজতা, স্বাধীন ডিপ্লয়মেন্ট, এবং টেকনোলজি স্ট্যাকের বৈচিত্র্য। TypeORM এই আর্কিটেকচারের মধ্যে ব্যবহৃত হলে, প্রতিটি সার্ভিসের ডেটাবেস ম্যানেজমেন্ট আলাদা এবং সহজভাবে পরিচালনা করা যায়। TypeORM এবং Microservices একত্রে ব্যবহার করার মাধ্যমে আপনি মডুলার, স্কেলেবল, এবং রক্ষণাবেক্ষণযোগ্য অ্যাপ্লিকেশন তৈরি করতে সক্ষম হবেন।
Microservices ডিজাইন: একটি সংক্ষিপ্ত পরিচিতি
Microservices Architecture হল একটি সফটওয়্যার আর্কিটেকচার স্টাইল যেখানে একটি বৃহৎ অ্যাপ্লিকেশনকে ছোট, স্বতন্ত্র এবং স্বাধীনভাবে পরিচালনাযোগ্য সার্ভিসে ভাগ করা হয়। প্রতিটি মাইক্রোসার্ভিস এককভাবে ডেপ্লয় করা যায়, এবং সাধারণত একটি নির্দিষ্ট কাজ বা ফিচার সংক্রান্ত ফাংশনালিটি পরিচালনা করে। মাইক্রোসার্ভিস অ্যাপ্লিকেশনের প্রতিটি সার্ভিসের নিজস্ব ডেটাবেস থাকতে পারে, যা সার্ভিসের স্বায়ত্তশাসন নিশ্চিত করে।
TypeORM এর সাথে Microservices ডিজাইন করা হলে, আপনি ডেটাবেস ম্যানেজমেন্ট এবং ডেটা অ্যাক্সেসের জন্য TypeORM ব্যবহার করতে পারবেন, যা ডেটাবেস অপারেশনকে আরও সহজ এবং কার্যকরী করে তোলে।
TypeORM মাইক্রোসার্ভিস আর্কিটেকচারে ব্যবহারের জন্য আদর্শ, কারণ এটি TypeScript এবং JavaScript এর সাথে খুব ভালোভাবে কাজ করে, এবং ডেটাবেসের সাথে যোগাযোগ করার জন্য সহজ API প্রদান করে।
TypeORM এবং Microservices: একটি একত্রিত ডিজাইন
TypeORM ব্যবহার করে Microservices ডিজাইন করতে হলে, কয়েকটি মূল পদ্ধতি এবং স্টেপ অনুসরণ করা যেতে পারে:
- প্রতিটি মাইক্রোসার্ভিসের নিজস্ব ডেটাবেস: প্রতিটি মাইক্রোসার্ভিসে পৃথক ডেটাবেস ব্যবহার করা উচিত। এর ফলে, প্রতিটি সার্ভিস স্বতন্ত্রভাবে কাজ করতে পারে এবং সার্ভিসের মধ্যে যেকোনো সমস্যা একে অপরকে প্রভাবিত করবে না।
- Service Communication: মাইক্রোসার্ভিসগুলো একে অপরের সাথে যোগাযোগের জন্য সাধারণত HTTP (REST), gRPC, অথবা Message Brokers (যেমন RabbitMQ বা Kafka) ব্যবহার করে।
- TypeORM Setup: TypeORM প্রতিটি মাইক্রোসার্ভিসে ডেটাবেস পরিচালনা করতে ব্যবহৃত হবে, যার মধ্যে Entity, Repository এবং QueryBuilder ব্যবহার করা হবে।
Step-by-Step Microservices ডিজাইন TypeORM সহ
১. Microservice সৃষ্টির জন্য প্রকল্প তৈরি
প্রথমে, আপনি একটি নতুন Node.js অ্যাপ্লিকেশন তৈরি করবেন যেটিতে TypeORM ইন্টিগ্রেট করা থাকবে। প্রতিটি মাইক্রোসার্ভিসের জন্য একটি আলাদা প্রকল্প তৈরি করা উচিত, এবং প্রতিটি প্রকল্পে আলাদা ডেটাবেস কনফিগারেশন থাকবে।
npm init দিয়ে একটি নতুন প্রকল্প তৈরি করুন:
mkdir microservice-1
cd microservice-1
npm init -y
npm install typeorm reflect-metadata mysql2 express
২. TypeORM কনফিগারেশন সেটআপ
প্রথমে TypeORM কনফিগারেশন তৈরি করুন যা ডেটাবেস সংযোগ স্থাপন করবে।
import { createConnection } from "typeorm";
import { User } from "./entities/User";
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "password",
database: "microservice_db",
entities: [User],
synchronize: true, // true হলে ডেটাবেসের schema স্বয়ংক্রিয়ভাবে সিঙ্ক হবে
}).then(() => {
console.log("Connected to the database.");
}).catch(error => console.log(error));
৩. Entities এবং Repository তৈরি
Microservices ডিজাইন করার সময়, প্রতিটি সার্ভিসের জন্য একটি নির্দিষ্ট ডেটাবেস Entity তৈরি করা হয়। User Entity তৈরি করা হবে যেটি Microservice এর জন্য কাজ করবে।
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
}
এটি একটি সাধারণ User Entity যা ডেটাবেসের User টেবিলের প্রতিনিধিত্ব করে।
৪. Express সার্ভিস তৈরি
এখন Express.js ব্যবহার করে একটি API তৈরি করুন যা TypeORM দিয়ে তৈরি করা Entity এর সাথে যোগাযোগ করবে।
import express from "express";
import { getRepository } from "typeorm";
import { User } from "./entities/User";
const app = express();
const port = 3000;
app.use(express.json());
// Create a new user
app.post("/users", async (req, res) => {
const userRepository = getRepository(User);
const user = new User();
user.name = req.body.name;
user.email = req.body.email;
await userRepository.save(user);
res.status(201).send(user);
});
// Get all users
app.get("/users", async (req, res) => {
const userRepository = getRepository(User);
const users = await userRepository.find();
res.status(200).json(users);
});
app.listen(port, () => {
console.log(`Microservice running at http://localhost:${port}`);
});
এটি একটি মাইক্রোসার্ভিস যা POST /users এবং GET /users এন্ডপয়েন্টের মাধ্যমে User Entity এর সাথে যোগাযোগ করে।
৫. Service Communication: Microservice গুলির মধ্যে যোগাযোগ
একাধিক মাইক্রোসার্ভিস একে অপরের সাথে যোগাযোগ করতে পারে। উদাহরণস্বরূপ, যদি একটি User Service এবং একটি Order Service থাকে, তবে Order Service কে User Service থেকে ডেটা নিতে হবে। আপনি HTTP (REST API) অথবা Message Broker (যেমন RabbitMQ বা Kafka) ব্যবহার করে একে অপরের সাথে যোগাযোগ করতে পারেন।
এখানে একটি HTTP রিকোয়েস্টের উদাহরণ:
import axios from 'axios';
// User Service থেকে ডেটা রিট্রিভ করা
const getUserDetails = async (userId: number) => {
const response = await axios.get(`http://localhost:3000/users/${userId}`);
return response.data;
};
এখানে, Order Service এর মাধ্যমে User Service কে HTTP রিকোয়েস্ট পাঠানো হচ্ছে এবং User এর ডেটা নেয়া হচ্ছে।
৬. API Gateway (Optional)
মাইক্রোসার্ভিসে API Gateway ব্যবহার করলে একাধিক মাইক্রোসার্ভিসের এন্ডপয়েন্টগুলো এক জায়গায় কেন্দ্রীভূত করা যায়। এটি রাউটিং এবং রিকোয়েস্ট হ্যান্ডলিং আরও সহজ করে।
import express from 'express';
import axios from 'axios';
const app = express();
const port = 4000;
// Forwarding request to the User Service
app.get("/users/:id", async (req, res) => {
const { id } = req.params;
const user = await axios.get(`http://localhost:3000/users/${id}`);
res.send(user.data);
});
app.listen(port, () => {
console.log(`API Gateway running at http://localhost:${port}`);
});
এখানে, API Gateway রিকোয়েস্টটি User Service তে ফরওয়ার্ড করছে।
Microservices এর মধ্যে TypeORM ব্যবহারের সুবিধা
- ডেটাবেস সম্পর্কের স্বতন্ত্রতা: প্রতিটি মাইক্রোসার্ভিসের নিজস্ব ডেটাবেস থাকতে পারে, যার মাধ্যমে ডেটা ম্যানিপুলেশন সহজ হয় এবং সিস্টেমের নির্ভরতা কমে যায়।
- ডিস্ট্রিবিউটেড ডেটাবেস: প্রতিটি মাইক্রোসার্ভিস তার নিজস্ব ডেটাবেস ব্যবহার করে, তাই ডেটা একে অপরের সাথে মিশে না যায় এবং স্বতন্ত্রভাবে পরিচালনা করা যায়।
- স্কেলেবিলিটি: মাইক্রোসার্ভিসগুলি একে অপর থেকে স্বাধীনভাবে স্কেল করা যেতে পারে এবং TypeORM এর মাধ্যমে ডেটাবেস পরিচালনা সহজ হয়।
- ডাটাবেস নির্ভরতা কমানো: ডেটাবেসের ব্যবস্থাপনা এবং কনফিগারেশন আলাদা করে রাখলে, মাইক্রোসার্ভিসের নির্ভরতা কমে যায় এবং এটি বেশি স্কেলযোগ্য হয়।
সারাংশ
TypeORM এবং Microservices Architecture একত্রে ব্যবহার করলে আপনি একটি স্কেলেবল এবং মডুলার অ্যাপ্লিকেশন ডিজাইন করতে পারবেন। TypeORM মাইক্রোসার্ভিসে ডেটাবেসের ম্যানেজমেন্ট সহজ করে তোলে, এবং REST API বা Message Broker ব্যবহার করে মাইক্রোসার্ভিসগুলো একে অপরের সাথে যোগাযোগ করতে পারে। TypeORM-এর সাথে মাইক্রোসার্ভিস ডিজাইন একটি কার্যকরী এবং শক্তিশালী আর্কিটেকচার তৈরি করার জন্য উপযুক্ত।
Data Consistency in TypeORM
Data Consistency (ডেটা কনসিস্টেন্সি) হল ডেটাবেসে ডেটার সঠিকতা এবং নির্ভুলতা বজায় রাখা। TypeORM ব্যবহার করার সময়, ডেটা কনসিস্টেন্সি নিশ্চিত করার জন্য বিভিন্ন কৌশল প্রয়োগ করা হয়। যখন ডেটাবেসের মধ্যে একাধিক ট্রানজেকশন পরিচালিত হয়, তখন নিশ্চিত করতে হয় যে একাধিক প্রসেস একই ডেটাতে প্রভাব ফেলছে না এবং ডেটা সঠিকভাবে সংরক্ষিত হচ্ছে।
TypeORM ডেটাবেসের ACID (Atomicity, Consistency, Isolation, Durability) বৈশিষ্ট্যগুলো মেনে চলে এবং আপনাকে ডেটার কনসিস্টেন্সি নিশ্চিত করার জন্য উপযুক্ত কার্যকরী টুল সরবরাহ করে।
1. ACID Properties in TypeORM
TypeORM-এ ACID বৈশিষ্ট্যগুলি রক্ষিত থাকে, যা ডেটাবেসে ট্রানজেকশন পরিচালনা করার সময় ডেটার সঠিকতা এবং নির্ভুলতা নিশ্চিত করে।
- Atomicity: একটি ট্রানজেকশন যদি সফলভাবে সম্পন্ন না হয়, তবে সমস্ত পরিবর্তন বাতিল হয়ে যাবে। TypeORM এ, Transactional বৈশিষ্ট্য ব্যবহার করে আপনি এটিকে সহজে বাস্তবায়ন করতে পারেন।
- Consistency: ডেটা একক বা কয়েকটি ট্রানজেকশনের মাধ্যমে সঠিক এবং নির্ভুল অবস্থায় থাকবে। TypeORM-এ ট্রানজেকশন ব্যবহারের মাধ্যমে আপনি নিশ্চিত করতে পারেন যে ডেটাবেসের মধ্যে সব পরিবর্তন সঠিকভাবে সম্পাদিত হচ্ছে।
- Isolation: একাধিক ট্রানজেকশন একে অপরের ডেটাতে প্রভাব ফেলতে পারে না। TypeORM আপনাকে isolation level কনফিগার করার সুযোগ দেয় যাতে একাধিক ট্রানজেকশন একে অপরকে প্রভাবিত না করে।
- Durability: ডেটা একবার রাইট হলে তা চিরকালই রক্ষা পাবে, যতই সিস্টেম বা সার্ভার বন্ধ হোক না কেন।
2. Transactions in TypeORM
TypeORM-এ transactions ব্যবহার করে আপনি ডেটাবেসে একাধিক অপারেশন একত্রে পরিচালনা করতে পারেন। এটি ডেটা কনসিস্টেন্সি নিশ্চিত করার একটি গুরুত্বপূর্ণ উপায়। TypeORM এ transaction শুরু করা এবং তার সাথে সম্পর্কিত কাজগুলি করতে নিচের কোডটি ব্যবহার করা হয়:
import { createConnection } from "typeorm";
import { User } from "./entity/User";
createConnection().then(async connection => {
const userRepository = connection.getRepository(User);
// Begin a transaction
const queryRunner = connection.createQueryRunner();
await queryRunner.startTransaction();
try {
const user = new User();
user.name = "John Doe";
user.email = "john.doe@example.com";
await queryRunner.manager.save(user);
// Commit the transaction
await queryRunner.commitTransaction();
} catch (error) {
// If something goes wrong, rollback the transaction
await queryRunner.rollbackTransaction();
} finally {
// Release the query runner to avoid memory leaks
await queryRunner.release();
}
}).catch(error => console.log(error));
এখানে, queryRunner ব্যবহার করে একটি ট্রানজেকশন শুরু করা হয়েছে, যা সফল হলে commitTransaction() এবং যদি কোনো সমস্যা হয় তবে rollbackTransaction() কার্যকর হয়।
Distributed Transactions
Distributed Transactions হল এমন একটি প্রক্রিয়া যেখানে একাধিক ডাটাবেস বা সিস্টেমের মধ্যে একযোগে ট্রানজেকশন পরিচালিত হয়। যখন আপনি একটি অ্যাপ্লিকেশন তৈরি করেন, যার মধ্যে একাধিক সার্ভার বা সিস্টেমের মধ্যে ডেটা ভাগ করা হয়, তখন distributed transactions ব্যবহার করা প্রয়োজন।
1. Why Distributed Transactions?
Distributed transactions প্রয়োজনীয় হয় যখন একাধিক মাইক্রোসার্ভিস বা সিস্টেমের মধ্যে ডেটা প্রসেসিং এবং পরিবর্তন করতে হয়, কিন্তু সেই পরিবর্তনগুলো একে অপরের উপর নির্ভরশীল থাকে। যেমন, যদি আপনি দুটি আলাদা সার্ভার বা ডেটাবেসে ডেটা ইনসার্ট বা আপডেট করতে চান, তবে আপনি নিশ্চিত করতে চান যে দুটি পরিবর্তনই সফলভাবে সম্পন্ন হয় অথবা যদি একটি ব্যর্থ হয় তবে অন্যটি বাতিল হয়ে যাবে।
2. Managing Distributed Transactions with TypeORM
TypeORM সরাসরি distributed transactions সমর্থন করে না, তবে আপনি Saga Pattern বা Two-Phase Commit পদ্ধতি ব্যবহার করে এই সমস্যা সমাধান করতে পারেন।
- Saga Pattern: এটি একটি লজিক্যাল প্যাটার্ন যা একাধিক সিস্টেমের মধ্যে অ্যাটমিক অপারেশন সম্পাদন করে। যদি একটি অপারেশন ব্যর্থ হয়, তবে অন্য অপারেশনগুলো ব্যাকআপ বা রোলব্যাক হয়।
- Two-Phase Commit (2PC): এটি একটি কৌশল যেখানে দুটি বা ততোধিক ডাটাবেসে ট্রানজেকশন পরিচালনা করার সময় প্রথমে প্রস্তুতি নেওয়া হয় এবং পরে নিশ্চিতকরণ (commit) বা বাতিল (rollback) করা হয়।
3. Handling Distributed Transactions Using TypeORM
TypeORM এর মাধ্যমে একাধিক ডাটাবেস বা সার্ভিসের মধ্যে ডিস্ট্রিবিউটেড ট্রানজেকশন পরিচালনা করার জন্য, আপনি ম্যানুয়ালি ডিস্ট্রিবিউটেড লজিক প্রয়োগ করতে পারেন। যদিও TypeORM সরাসরি two-phase commit বা distributed transactions সমর্থন করে না, আপনি queryRunner ব্যবহার করে ট্রানজেকশন পরিচালনা করতে পারেন এবং সার্ভিস লেয়ারে সেগুলিকে একত্রিত করতে পারেন।
Example: Two-phase Commit
import { createConnection } from "typeorm";
import { User } from "./entity/User";
createConnection().then(async connection => {
const userRepository = connection.getRepository(User);
// Step 1: Start transaction for DB1
const queryRunner1 = connection.createQueryRunner();
await queryRunner1.startTransaction();
try {
const user1 = new User();
user1.name = "John Doe";
user1.email = "john.doe@example.com";
await queryRunner1.manager.save(user1);
// Step 2: Start another transaction for DB2 (another system)
const queryRunner2 = connection.createQueryRunner();
await queryRunner2.startTransaction();
const user2 = new User();
user2.name = "Jane Doe";
user2.email = "jane.doe@example.com";
await queryRunner2.manager.save(user2);
// Step 3: Commit both transactions
await queryRunner1.commitTransaction();
await queryRunner2.commitTransaction();
} catch (error) {
// Step 4: Rollback both transactions if something goes wrong
await queryRunner1.rollbackTransaction();
await queryRunner2.rollbackTransaction();
} finally {
// Step 5: Release query runners
await queryRunner1.release();
await queryRunner2.release();
}
}).catch(error => console.log(error));
এখানে দুটি ডাটাবেসে ট্রানজেকশন সম্পন্ন করা হচ্ছে, এবং যদি কোনো একটি ব্যর্থ হয়, তবে অন্যটি রোলব্যাক করা হয়।
সারাংশ
Data Consistency নিশ্চিত করার জন্য TypeORM-এ transactions ব্যবহারের মাধ্যমে আপনি ACID বৈশিষ্ট্যগুলি কার্যকরভাবে প্রয়োগ করতে পারেন। Distributed Transactions পরিচালনা করা একটু জটিল হতে পারে, তবে Saga Pattern বা Two-Phase Commit প্যাটার্ন ব্যবহার করে আপনি একাধিক সিস্টেমের মধ্যে ডেটার সঠিকতা এবং নির্ভুলতা নিশ্চিত করতে পারেন। TypeORM-এ transactions এবং distributed transactions এর ব্যবহারের মাধ্যমে আপনি ডেটা কনসিস্টেন্সি বজায় রাখতে পারবেন এবং আপনার অ্যাপ্লিকেশনকে আরো স্কেলেবল ও নির্ভরযোগ্য করে তুলতে পারবেন।
Communication Patterns in TypeORM
In web development, particularly when dealing with databases and large-scale systems, it's crucial to choose the right communication patterns for inter-service communication and data handling. Event-Driven and Synchronous communication are two common patterns that help manage how data and events flow between services, modules, or layers in your application.
When using TypeORM in your application, the database interactions are typically a part of these communication patterns. TypeORM, being an ORM (Object-Relational Mapping) library, handles the communication with the database using its own mechanisms, but these patterns are more relevant to the architecture of the application itself and how different modules or services interact with each other.
1. Event-Driven Communication Pattern
An Event-Driven Architecture (EDA) is a communication pattern where services or components within a system communicate by emitting and listening for events. In an event-driven system, when something significant happens, an event is triggered, and other parts of the application may subscribe to this event and take actions based on it.
In TypeORM, the event-driven approach can be integrated with different tools and frameworks to handle events. This pattern can be particularly useful for microservices, message queues, and scenarios where you want to decouple the producer and consumer of data.
Benefits of Event-Driven Communication:
- Loose Coupling: Components do not need to know about each other, which reduces dependencies.
- Scalability: It helps in building highly scalable systems where components can operate asynchronously.
- Flexibility: You can trigger actions based on events from any part of the system.
Example: Using Event-Driven with TypeORM
Let's say you're building an application where a User registers, and this triggers an event to send a welcome email to the user.
- User Entity in TypeORM:
import { Entity, PrimaryGeneratedColumn, Column, BeforeInsert } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
@BeforeInsert()
sendWelcomeEmail() {
// Here we would normally trigger an email event (this is a simplified example)
console.log(`Sending welcome email to: ${this.email}`);
// You can use an event emitter or message queue here
}
}
- Event Listener/Handler: In a more advanced scenario, you might use an event emitter to listen for specific events. For example, after the user is inserted into the database, you could emit an event that sends the email.
You could use Node.js EventEmitter or integrate with tools like RabbitMQ or Kafka for inter-service communication.
import { EventEmitter } from "events";
const eventEmitter = new EventEmitter();
eventEmitter.on('userRegistered', (user) => {
// Triggering email logic
console.log(`Sending welcome email to ${user.email}`);
});
// Simulating user registration
const user = new User();
user.name = 'John Doe';
user.email = 'john@example.com';
eventEmitter.emit('userRegistered', user);
In a more production-ready system, you might push this event to a message queue or other event handling system to decouple different parts of your application.
2. Synchronous Communication Pattern
In Synchronous Communication, a request is sent from one service or component to another, and the sender waits for a response before continuing. This is the traditional form of communication where one service requests data from another and waits for the result before moving forward.
When using TypeORM, synchronous communication is common in HTTP APIs and database interactions. For instance, when you perform a database query using TypeORM, it is often synchronous, meaning the execution stops until the query result is returned.
Benefits of Synchronous Communication:
- Simpler Flow: It is easier to understand and follow since each operation is processed step by step.
- Guaranteed Response: The caller gets a response directly, ensuring the next action can only be taken when the previous one finishes.
- Easier to Debug: Since the flow is sequential, debugging becomes more straightforward.
Example: Using Synchronous Communication in TypeORM
In a typical scenario, if you are querying a database to fetch a User, TypeORM's query functions like find() or save() will be synchronous unless explicitly configured otherwise.
import { createConnection } from "typeorm";
import { User } from "./entity/User";
createConnection().then(async connection => {
const userRepository = connection.getRepository(User);
// Synchronously fetching a user by id
const user = await userRepository.findOne({ where: { id: 1 } });
console.log(user);
}).catch(error => console.log(error));
Here, the findOne() query is synchronous. The code waits for the result of the database query before continuing to the next line of code.
Synchronous Web API Example with TypeORM
In web applications, especially when using REST APIs, you may often have synchronous communication with the database.
For example, a User Controller in an Express.js app might look like this:
import express from 'express';
import { getRepository } from 'typeorm';
import { User } from './entity/User';
const app = express();
app.get('/users/:id', async (req, res) => {
const userRepository = getRepository(User);
const user = await userRepository.findOne({ where: { id: req.params.id } });
if (!user) {
return res.status(404).send('User not found');
}
res.json(user);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
In this case, when a request is made to the /users/:id endpoint, the system synchronously queries the database and waits for the result before sending a response.
Comparing Event-Driven and Synchronous Communication in TypeORM
| Aspect | Event-Driven | Synchronous |
|---|---|---|
| Use case | Suitable for microservices and loosely coupled systems | Suitable for traditional request-response models |
| Flow | Asynchronous, events can be triggered and handled independently | Sequential, each operation waits for the previous one to finish |
| Complexity | Can become complex with multiple services and event brokers | Simpler to implement but may cause bottlenecks |
| Performance | Can scale easily with independent components | May struggle with performance as more synchronous requests pile up |
| Error Handling | Errors are handled through event listeners or fallback mechanisms | Errors are handled immediately and block further execution |
| Example | Using event emitters, message queues, or pub/sub systems | Using direct API calls or database queries |
Conclusion
Both Event-Driven and Synchronous communication patterns are essential for different use cases in web development. TypeORM can be easily integrated with both patterns to handle database interactions.
- Event-Driven communication is ideal for decoupling services and scaling applications, especially in microservices architectures or systems that require asynchronous processing.
- Synchronous communication is suitable for simpler, linear workflows where each request needs to wait for the previous one to complete before continuing.
When using TypeORM, both patterns can coexist depending on the architecture of your system, and it's important to choose the right pattern based on the requirements of your application.
Read more