جس لمحے کوئی ٹیم میراثی درخواست کو منتقل کرنے کا فیصلہ کرتی ہے، عام طور پر کوڈ کو منتقل کرنا شروع کرنے کا دباؤ ہوتا ہے۔
اپنا ڈیٹا بیس، API، یا UI منتقل کریں۔ اپنی درخواست کو ایک نئے فریم ورک، رن ٹائم، کلاؤڈ فراہم کنندہ، یا فن تعمیر میں منتقل کریں۔
یہ قابل فہم لگتا ہے، لیکن ایک مسئلہ ہے۔
اگر آپ کا موجودہ نظام کاروباری اصولوں، استقامت، بنیادی ڈھانچے، اور ایک ہی ماڈیول میں بیرونی انضمام اور آرکیسٹریشن کو ملا دیتا ہے، تو ہجرت اس کی ضرورت سے کہیں زیادہ مشکل ہو جاتی ہے۔
یہ صرف سافٹ ویئر کو منتقل کرنے کے بارے میں نہیں ہے۔ ہم کئی ذمہ داریوں کو تبدیل کرنے کی کوشش کر رہے ہیں جو ترقی کے سالوں میں آپس میں جڑی ہوئی ہیں۔
یہی وجہ ہے کہ میں اکثر ری فیکٹرنگ کو ترجیح دیتا ہوں۔ پہلے ہجرت۔ ہم میراثی نظام کو خوبصورت بنانے یا ہر چیز کو دوبارہ ڈیزائن کرنے کی کوشش نہیں کر رہے ہیں۔ اور آپ کو تیاری کے مرحلے کو صرف ایک اور دوبارہ لکھنے کے مرحلے میں تبدیل نہیں کرنا چاہئے۔
مقصد بہت تنگ ہے۔ یہ ڈھانچے کو کافی تبدیل کرنے کے بارے میں ہے تاکہ اہم کارروائیوں کو آزادانہ طور پر منتقل کرنے کی اجازت دی جا سکے۔
اس ورک فلو کے پچھلے مراحل میں، ہم نے پہلے کوڈ بیس کو سمجھنے کی کوشش کی اور پھر اس طرز عمل کو محفوظ بنانے کے لیے کریکٹرائزیشن ٹیسٹ کا استعمال کیا جسے ہم تبدیل کرنا چاہتے تھے۔
اب آپ اپنے ڈھانچے کو تبدیل کرنے کا طریقہ سیکھیں گے۔
اس ٹیوٹوریل میں، میں آپ کو بتاؤں گا کہ ہجرت کے لیے میراثی درخواست کیسے تیار کی جاتی ہے:
-
نقل مکانی کی حدود کا انتخاب؛
-
بنیادی ڈھانچے سے الگ کاروباری اصول،
-
سیون کا تعارف،
-
الگ الگ ضمنی اثرات،
-
بیرونی نظاموں کے ارد گرد اڈاپٹر بنائیں،
-
انحصار سمت کے مسائل کو کم کریں،
-
مربوط درخواست کے رویے کو نکالنا؛
-
اپنی ریفیکٹرنگ کے دوران خصوصیت کے ٹیسٹ کا استعمال کریں،
-
سسٹم کو آنکھیں بند کرکے دوبارہ ڈیزائن کرنے کے بجائے، AI کا استعمال کریں۔
-
آپ کو معلوم ہو جائے گا کہ آپ کی درخواست منتقلی شروع کرنے کے لیے کب تیار ہے۔
مثالیں TypeScript کا استعمال کرتی ہیں، لیکن یہ عمل زیادہ تر زبانوں اور فن تعمیر پر لاگو ہوتا ہے۔
مقصد یہ نہیں ہے:
legacy application
↓
perfect architecture
اس کے بجائے:
legacy application
↓
migration-friendly structure
↓
incremental migration
یہ فرق آپ کو بہت سارے غیر ضروری کام سے بچا سکتا ہے۔
شرائط
آپ کو اس کے ساتھ آرام دہ ہونا چاہئے:
-
موجودہ کوڈ بیس پڑھیں
-
TypeScript یا اس سے ملتی جلتی زبان
-
یونٹ اور انضمام کی جانچ
-
انحصار انجکشن
-
انٹرفیس اور اڈاپٹر
-
بنیادی سافٹ ویئر فن تعمیر
-
انکریمنٹل ری فیکٹرنگ
جس فنکشن میں آپ ترمیم کرنا چاہتے ہیں اس کے لیے آپ کے پاس کچھ رویے سے متعلق تحفظ بھی ہونا چاہیے۔
ان میں شامل ہوسکتا ہے:
-
کریکٹرائزیشن ٹیسٹ
-
انضمام کی جانچ
-
معاہدہ ٹیسٹ
متبادل طور پر، یہ موجودہ رویے کو چیک کرنے کا ایک اور قابل اعتماد طریقہ ہے۔
اس تحفظ کے بغیر ریفیکٹرنگ ممکن ہے۔ لیکن ساختی بہتری اور حادثاتی رویے کی تبدیلیوں کے درمیان فرق کرنا بھی زیادہ مشکل ہے۔
انڈیکس
کیوں ہجرت کے مسائل اکثر ہجرت سے پہلے شروع ہوتے ہیں۔
فرض کریں کہ آپ کو اپنی آرڈر پروسیسنگ کی درخواست کو منتقل کرنے کی ضرورت ہے۔
پہلے سے طے شدہ خدمات کا معائنہ کریں اور درج ذیل تلاش کریں:
async function processOrder(orderId: string) {
const connection = await mysql.getConnection();
const [rows] = await connection.query(
"SELECT * FROM orders WHERE id = ?",
[orderId]
);
const order = rows[0];
if (!order) {
throw new Error("Order not found");
}
if (order.customer_type === "PREMIUM") {
order.total = order.total * 0.9;
}
if (
order.country === "AR" &&
order.payment_method === "TRANSFER"
) {
order.total -= 500;
}
await connection.query(
"UPDATE orders SET total = ?, status = ? WHERE id = ?",
[order.total, "PROCESSED", order.id]
);
await paymentProvider.createPayment({
orderId: order.id,
amount: order.total,
});
await eventBus.publish("order.processed", {
id: order.id,
total: order.total,
});
await emailClient.send({
to: order.customer_email,
template: "order-processed",
});
return order;
}
فرض کریں کہ آپ کے ہجرت کے اہداف ہیں:
MySQL → PostgreSQL
Old runtime → New runtime
Legacy API → New service
واضح فتنہ اس فنکشن کو ٹارگٹ اسٹیک میں تبدیل کرنا شروع کرنا ہے۔
لیکن آپ اصل میں کیا ہجرت کر رہے ہیں؟
ان خصوصیات میں شامل ہیں:
database access
business rules
state transition
payment integration
event publication
email delivery
application orchestration
اب، اگر آپ اپنا ڈیٹا بیس تبدیل کرتے ہیں تو آپ کو قیمتوں پر اثر انداز ہونے کا خطرہ ہوتا ہے، اور اگر آپ اپنے ادائیگی کے کلائنٹ کو تبدیل کرتے ہیں تو آپ کو مستقل مزاجی پر اثر انداز ہونے کا خطرہ ہوتا ہے۔ اور فعالیت کو دوسری سروس میں منتقل کرنے کا مطلب ہے کہ اس کے تمام انحصار کو ایک ساتھ منتقل کرنا۔
نقل مکانی کی مشکل جزوی طور پر موجودہ ساخت کی وجہ سے ہے۔ لہذا ہجرت کرنے سے پہلے، آپ ان مسائل کو آزادانہ طور پر منتقل کرنے کی اجازت دینے کے لیے کافی علیحدگی پیدا کرنا چاہیں گے۔
ری فیکٹرنگ سے پہلے ہجرت کی حد کا انتخاب
اس سے شروع نہ کریں:
آئیے درخواست کا خلاصہ کرتے ہیں۔
کے ساتھ شروع کریں:
آپ سب سے پہلے کیا ہجرت کرنا چاہتے ہیں؟
فرض کریں کہ آپ نے پراسیس آرڈر کے طور پر پہلی خصوصیت کا فیصلہ کر لیا ہے۔ یہ ری فیکٹرنگ کے لیے حدود فراہم کرتا ہے۔
اب آپ نقشہ بنا سکتے ہیں:
Input:
orderId
Business behavior:
load order
calculate adjustments
mark as processed
Side effects:
persist order
create payment
publish event
send email
Output:
processed order
یہ ریفیکٹر کا فیصلہ کرنے سے کہیں زیادہ مفید ہے۔
src/services/
اس کی وجہ یہ ہے کہ ضروری نہیں کہ فولڈرز کاروباری حدود ہوں۔
جب آپ خصوصیات کے بارے میں استدلال کرسکتے ہیں تو منتقلی بہتر کام کرتی ہے۔
مثال کے طور پر:
Process Order
Cancel Order
Generate Invoice
Register Customer
Renew Subscription
ہر ایک ممکنہ طور پر منتقلی یونٹ بن سکتا ہے۔
پوری درخواست کو ری فیکٹر نہ کریں۔
ایک بار جب آپ آرکیٹیکچرل مسائل کی نشاندہی کرنا شروع کر دیتے ہیں، تو ان سب کو حل کرنے کے لیے پرکشش ہوتا ہے۔
آپ درج ذیل کو چیک کر سکتے ہیں:
circular dependencies
duplicated repositories
global configuration
large services
static helpers
direct database access
inconsistent error handling
mixed domain models
اگرچہ یہ سب قابل توجہ ہیں، لیکن نقل مکانی کے لیے تمام تکنیکی قرضوں کو ختم کرنے کی ضرورت نہیں ہے۔
فرض کریں کہ آپ کا مقصد ایک آرڈر پروسیسنگ فنکشن ہے۔ ایک مفید اصول صرف ری فیکٹر کرنا ہے جو اس خصوصیت کو محفوظ طریقے سے منتقل ہونے سے روکتا ہے۔
مثال کے طور پر:
Problem:
Order processing calls MySQL directly.
Relevant?
Yes.
Problem:
The reporting module uses inconsistent date formatting.
Relevant?
Probably not.
Problem:
Order processing calls the payment SDK directly.
Relevant?
Yes.
Problem:
The admin UI contains duplicated CSS.
Relevant?
No.
یہ آپ کی تیاری کو کبھی نہ ختم ہونے والا آرگنائزنگ پروجیکٹ بننے سے روکے گا۔
میراثی جدیدیت کے لیے دائرہ کار کے نظم و ضبط کی ضرورت ہے۔
بنیادی ڈھانچے سے کاروباری قوانین کو الگ کریں۔
سب سے اہم ساختی تبدیلی کاروباری کارروائیوں کو تکنیکی تفصیلات سے الگ کرنا ہے۔
درج ذیل کوڈ کا استعمال کریں:
async function processOrder(orderId: string) {
const order = await mysqlOrders.find(orderId);
if (order.customerType === "PREMIUM") {
order.total *= 0.9;
}
if (
order.country === "AR" &&
order.paymentMethod === "TRANSFER"
) {
order.total -= 500;
}
await mysqlOrders.update(order);
await stripe.createPayment({
orderId: order.id,
amount: order.total,
});
}
قیمتوں کا تعین کرنے کے عمل کو خود MySQL یا Stripe کی ضرورت نہیں ہے۔
آپ اسے اس طرح نکال سکتے ہیں:
type Order = {
id: string;
total: number;
customerType: "STANDARD" | "PREMIUM";
country: string;
paymentMethod: "CARD" | "TRANSFER";
};
function calculateOrderTotal(order: Order): number {
let total = order.total;
if (order.customerType === "PREMIUM") {
total *= 0.9;
}
if (
order.country === "AR" &&
order.paymentMethod === "TRANSFER"
) {
total -= 500;
}
return Math.max(total, 0);
}
اب:
pricing behavior
یہ اب اس کے ساتھ نہیں ملا ہوا ہے:
MySQL
Stripe
اس کے لیے مکمل ڈومین سے چلنے والے دوبارہ ڈیزائن کی ضرورت نہیں ہے۔ یہ صرف ایک مفید علیحدگی ہے۔
منتقلی کے اگلے مرحلے میں، آپ اس رویے کو تبدیل کیے بغیر اپنے بنیادی ڈھانچے کو تبدیل کر سکتے ہیں۔
سخت انحصار کے ارد گرد seams متعارف کرانا
میراثی کوڈ اکثر انحصار پر مشتمل ہوتا ہے جنہیں جانچ یا منتقلی کوڈ میں آسانی سے تبدیل نہیں کیا جا سکتا۔
مثال کے طور پر:
class OrderService {
async process(orderId: string) {
const client = new LegacyDatabaseClient();
const order = await client.findOrder(orderId);
// ...
}
}
ڈیٹا بیس پر انحصار طریقہ کے اندر پیدا ہوتا ہے۔
پھر متبادل مشکل ہو جاتا ہے۔
چھوٹی پری ری فیکٹرنگ سیون کا باعث بن سکتی ہے۔
interface OrderRepository {
findById(id: string): Promise;
save(order: Order): Promise;
}
پھر:
class OrderService {
constructor(
private readonly orders: OrderRepository
) {}
async process(orderId: string) {
const order = await this.orders.findById(orderId);
if (!order) {
throw new Error("Order not found");
}
// existing behavior
}
}
موجودہ MySQL نفاذ اب انٹرفیس کو پورا کر سکتے ہیں۔
class MySqlOrderRepository implements OrderRepository {
async findById(id: string) {
// existing MySQL behavior
}
async save(order: Order) {
// existing MySQL behavior
}
}
بعد میں ہجرت متعارف کر سکتی ہے:
class PostgresOrderRepository implements OrderRepository {
// new implementation
}
غور کریں کہ ہم نے کیا تبدیل نہیں کیا ہے۔ ہم نے اپنے کاروباری رویے میں کوئی تبدیلی نہیں کی ہے۔ ہم ہیں انحصار کا متبادل.
یہ ری فیکٹرنگ کی ایک قسم ہے جو ہجرت میں مدد کرتی ہے۔
فیصلے کی منطق سے ضمنی اثرات کو الگ کریں۔
ایک اور مفید علیحدگی ہے:
deciding
اور:
performing
فرض کریں کہ منسوخی فی الحال اس طرح ہے:
async function cancelOrder(order: Order) {
if (order.status === "SHIPPED") {
throw new Error("Cannot cancel shipped order");
}
order.status = "CANCELLED";
await orders.save(order);
await inventory.release(order.id);
await payment.refund(order.id);
await audit.log("ORDER_CANCELLED", order.id);
}
یہاں دو مختلف ذمہ داریاں ہیں۔
کاروباری فیصلے:
Can this order be cancelled?
What should its new state be?
اور آپریشنل اثرات درج ذیل ہیں:
persist
release inventory
refund
audit
سب سے پہلے، ہم کرسٹل نکال سکتے ہیں.
function cancelOrderState(order: Order): Order {
if (order.status === "SHIPPED") {
throw new Error("Cannot cancel shipped order");
}
return {
...order,
status: "CANCELLED",
};
}
اس کے بعد آرکیسٹریشن کو برقرار رکھا جاتا ہے۔
async function cancelOrder(order: Order) {
const cancelled = cancelOrderState(order);
await orders.save(cancelled);
await inventory.release(cancelled.id);
await payment.refund(cancelled.id);
await audit.log("ORDER_CANCELLED", cancelled.id);
return cancelled;
}
رویہ اب بھی وہی ہے، لیکن ریاستی تبدیلیوں کو اب آزمایا جا سکتا ہے اور آزادانہ طور پر منتقل کیا جا سکتا ہے۔
یہ اہم ہے اگر ٹارگٹ آرکیٹیکچر بدل جائے کہ ضمنی اثرات کیسے انجام پاتے ہیں۔
مثال کے طور پر، مستقبل کے ورژن میں آپ استعمال کر سکیں گے:
transactional outbox
event-driven workflow
queue
workflow engine
ابھی تک ان میکانزم کو متعارف کرانے کی ضرورت نہیں ہے۔ آپ کو صرف اپنے موجودہ فیصلے کی منطق پر براہ راست انحصار کرنا چھوڑنا ہوگا۔
اڈاپٹر کے پیچھے ایک بیرونی سسٹم رکھیں
بیرونی SDKs اکثر میراثی کوڈ میں گہرائی سے لیک ہوتے ہیں۔
مثال کے طور پر:
const result = await stripe.paymentIntents.create({
amount: order.total,
currency: "usd",
metadata: {
orderId: order.id,
},
});
جب درجنوں ایپلیکیشن ماڈیول براہ راست اسٹرائپ SDK پر انحصار کرتے ہیں، تو ادائیگی کی کارروائی کو تبدیل کرنا یا دوبارہ تعینات کرنا مشکل ہو جاتا ہے۔
اس کے بجائے، درخواست کی سطح کی حد بنائیں۔
type PaymentRequest = {
orderId: string;
amount: number;
};
type PaymentResult = {
paymentId: string;
};
interface PaymentGateway {
charge(
request: PaymentRequest
): Promise;
}
اسٹرائپ اڈاپٹر میں فراہم کنندہ کی مخصوص تفصیلات شامل ہیں۔
class StripePaymentGateway implements PaymentGateway {
async charge(
request: PaymentRequest
): Promise {
const result =
await stripe.paymentIntents.create({
amount: request.amount,
currency: "usd",
metadata: {
orderId: request.orderId,
},
});
return {
paymentId: result.id,
};
}
}
اب آپ کی درخواست جانتی ہے:
PaymentGateway
بجائے:
Stripe SDK
یہ منتقلی کے لیے مفید ہے کیونکہ فراہم کنندہ کے لیے مخصوص کوڈ مقامی ہے۔
یہی پیٹرن مندرجہ ذیل پر لاگو ہوتا ہے:
email providers
message brokers
cloud storage
ERP integrations
CRM APIs
identity providers
search engines
اڈاپٹر اس کے قابل نہیں ہیں کیونکہ انٹرفیس جدید ہے۔ وہ قیمتی ہیں کیونکہ وہ حدود بناتے ہیں جنہیں منتقل کیا جاسکتا ہے۔
ہر چیز کو دوبارہ تعمیر کیے بغیر انحصار کی سمت کو بہتر بنایا
میراثی نظام میں اکثر انحصار ہوتا ہے جیسے:
business logic
↓
database SDK
↓
framework utilities
اس سے انفراسٹرکچر کو تبدیل کرنا مشکل ہو جاتا ہے۔
ضروری نہیں کہ آپ کو مکمل طور پر صاف فن تعمیر کو لاگو کرنا پڑے۔ جب منتقلی ضروری ہو تو آپ کو اپنی انحصار کی سمت کو بہتر بنانے کی ضرورت ہے۔
مثال کے طور پر:
پہلے:
OrderService
↓
MySQL
بعد میں:
OrderService
↓
OrderRepository
↑
MySqlOrderRepository
ایپلی کیشنز خلاصہ پر منحصر ہیں۔ انفراسٹرکچر ایسا کرتا ہے۔
ادائیگیوں کے لیے بھی یہی ہے۔
OrderService
↓
PaymentGateway
↑
StripePaymentGateway
پیغام:
OrderService
↓
OrderEvents
↑
KafkaOrderEvents
اب جب آپ اپنے بنیادی ڈھانچے کو تبدیل کرتے ہیں تو آپ کو اپنی درخواست کی خدمات کو دوبارہ لکھنے کی ضرورت نہیں ہے۔ یہی اہم نتیجہ ہے۔
کئی چھوٹی ری فیکٹرنگ کے بعد، آپ کا فنکشن اس طرح نظر آ سکتا ہے:
interface OrderRepository {
findById(id: string): Promise;
save(order: Order): Promise;
}
interface PaymentGateway {
charge(request: {
orderId: string;
amount: number;
}): Promise;
}
interface OrderEvents {
processed(order: Order): Promise;
}
class ProcessOrder {
constructor(
private readonly orders: OrderRepository,
private readonly payments: PaymentGateway,
private readonly events: OrderEvents
) {}
async execute(orderId: string) {
const order = await this.orders.findById(orderId);
if (!order) {
throw new Error("Order not found");
}
const total = calculateOrderTotal(order);
const processed: Order = {
...order,
total,
status: "PROCESSED",
};
await this.orders.save(processed);
await this.payments.charge({
orderId: processed.id,
amount: processed.total,
});
await this.events.processed(processed);
return processed;
}
}
ضروری نہیں کہ یہ حتمی فن تعمیر ہو۔ یہ ضروری ہے۔
ہم یہ دعویٰ نہیں کرتے کہ:
اس طرح آپ کی درخواست ہمیشہ کے لیے نظر آنی چاہیے۔
ہم صرف یہ کہہ رہے ہیں:
اس خصوصیت میں اب حدود ہیں جو منتقلی کو آسان بناتی ہیں۔
انفراسٹرکچر آزادانہ طور پر بدل سکتا ہے۔
کاروباری قوانین قابل امتحان ہیں۔ آرکیسٹریشن دکھایا گیا ہے۔ اور بیرونی معاہدے واضح ہیں۔
ہجرت پر غور کرنے کے لیے یہی کافی ہے۔
ری فیکٹرنگ کے دوران طرز عمل کے ٹیسٹ چلانا جاری رکھیں۔
یہ وہ جگہ ہے جہاں پچھلے مراحل سے خصوصیت کے ٹیسٹ کام آتے ہیں۔
فرض کریں کہ اصل رویے کی حفاظت کی گئی تھی:
it("preserves premium order processing behavior", async () => {
const result = await processOrder("order-1");
expect(result.total).toBe(9000);
expect(result.status).toBe("PROCESSED");
expect(payment.charge).toHaveBeenCalledWith({
orderId: "order-1",
amount: 9000,
});
expect(events.processed).toHaveBeenCalled();
});
اب آپ درج ذیل کو تبدیل کر سکتے ہیں:
direct database access
میں:
repository
اور ٹیسٹ چلائیں۔
پھر درج ذیل کو تبدیل کریں:
direct payment SDK
میں:
payment adapter
اور ٹیسٹ چلائیں۔
پھر نکالیں:
pricing logic
اور ٹیسٹ چلائیں۔
تال درج ذیل ہے:
small structural change
↓
test
↓
small structural change
↓
test
↓
small structural change
↓
test
یہ اہم ہے کیونکہ جب طرز عمل میں تبدیلیاں بیک وقت نہیں ہوتی ہیں تو ساختی ریفیکٹرنگ کا اندازہ لگانا بہت آسان ہوتا ہے۔
اگر ایک چھوٹی سی تبدیلی کے بعد کوئی ٹیسٹ ناکام ہو جاتا ہے، تو ممکنہ وجوہات تنگ ہیں۔
اگر دوبارہ لکھنے کے دو ہفتوں کے بعد کوئی ٹیسٹ ناکام ہوجاتا ہے تو، ممکنہ وجوہات کچھ بھی ہیں۔
ساختی ریفیکٹرنگ کے دوران AI کا استعمال کیسے کریں۔
AI اس مرحلے میں بہت مدد کر سکتا ہے۔
تاہم، مفید اشارے مختلف ہیں:
Refactor this application using Clean Architecture.
اس کے بجائے، اپنے ماڈل میں محدود تبدیلیاں فراہم کریں۔
مثال کے طور پر:
This service currently accesses MySQL directly.
I want to introduce an OrderRepository seam without
changing observable behavior.
Tasks:
1. identify every database operation used by this service,
2. propose the smallest repository interface needed,
3. move existing database calls behind an adapter,
4. preserve return values, errors, and call order where relevant,
5. do not change business rules,
6. do not introduce additional abstractions.
Explain every structural change before generating code.
یہ AI کو کاموں کا ایک بہت کم دائرہ فراہم کرتا ہے۔
ایک اور مفید درخواست ہے:
Compare the implementation before and after this refactor.
Identify any observable behavior that may have changed.
Check specifically:
- exceptions,
- return values,
- side effects,
- ordering of side effects,
- null handling,
- transaction boundaries,
- retry behavior.
Do not assume equivalence because the code looks similar.
یہ وہ جگہ ہے جہاں AI دوسرے جائزہ لینے والے کے طور پر قابل قدر ہو سکتا ہے۔
آپ بڑی تبدیلیوں کو دستی طور پر تلاش کرنے سے زیادہ تیزی سے اختلافات کو چیک کر سکتے ہیں۔ لیکن ٹیسٹ اب بھی مضبوط ثبوت فراہم کرتا ہے۔
AI سے اپنے ٹارگٹ فن تعمیر کو بہت جلد ڈیزائن کرنے کو نہ کہیں۔
AI عام تعمیراتی نمونوں کو پہچاننے میں بہت اچھا ہے۔ لیکن یہ خطرناک بھی ہو سکتا ہے۔
اپنے ماڈل کو ایک بڑی میراثی سروس فراہم کریں اور پوچھیں:
How should this be modernized?
اور آپ وصول کریں گے:
microservices
event-driven architecture
CQRS
repository pattern
domain events
message broker
API gateway
distributed cache
یہ سب جائز تکنیک یا نمونے ہیں، لیکن ان میں سے کوئی بھی خود بخود جائز نہیں ہے۔
ٹارگٹ فن تعمیر کو منتخب کرنے سے پہلے رکاوٹوں کی ضرورت ہے۔
مثال کے طور پر:
deployment frequency
team size
transactional requirements
latency
failure tolerance
data ownership
integration boundaries
operational maturity
traffic
cost
regulatory requirements
مائیکرو سروسز کے مقابلے میں ایک اچھی طرح سے پابند مونولتھ بہتر ہدف ہو سکتا ہے۔ ہم وقت ساز ورک فلو ایونٹ پر مبنی پروسیسنگ سے بہتر ہو سکتا ہے۔ اور ڈیٹا بیس کی منتقلی کے لیے ڈومین ماڈل میں تبدیلی کی ضرورت نہیں ہو سکتی۔
فن تعمیر کو رکاوٹوں کی پیروی کرنی چاہئے، پیٹرن کی شناخت نہیں۔
اپنے اختیارات کا جائزہ لینے کے لیے AI کا استعمال کریں۔ کسی مانوس نمونہ کی موجودگی کو اسے اپنانے کی وجہ نہ بننے دیں۔
یہ کیسے جانیں کہ آیا کوئی خصوصیت منتقل ہونے کے لیے تیار ہے۔
کسی وقت آپ کو ری فیکٹرنگ کو روکنا ہوگا۔ اور یہ فیصلہ اہم ہے۔
آپ کو کامل کوڈ کی ضرورت نہیں ہے۔ ان سوالات کا واضح جواب دینے کے قابل ہونا عام طور پر آپ کی خصوصیت کو منتقلی کے لیے تیار ہونے کے بہت قریب لے جاتا ہے۔
کیا آپ اپنے ان پٹ کی وضاحت کر سکتے ہیں؟
مثال کے طور پر:
orderId
customer
request payload
event
کیا آپ نتائج کی وضاحت کر سکتے ہیں؟
مثال کے طور پر:
processed order
HTTP response
event
database change
کیا آپ اہم کاروباری اصول دیکھتے ہیں؟
ان کا کامل ہونا ضروری نہیں ہے، لیکن آپ کو یہ جاننے کی ضرورت ہے کہ وہ کہاں رہتے ہیں۔
کیا بیرونی انحصار واضح ہیں؟
مثال کے طور پر:
OrderRepository
PaymentGateway
OrderEvents
EmailSender
کیا انفراسٹرکچر کو تبدیل کیا جا سکتا ہے؟
اگر MySQL کو تبدیل کرنے کے لیے آپ کی قیمتوں کی منطق میں تبدیلی کی ضرورت ہے، تو ہو سکتا ہے آپ کا دائرہ تیار نہ ہو۔
کیا اہم اعمال محفوظ ہیں؟
حادثاتی تبدیلیوں کا پتہ لگانے کے لیے کافی جانچ کی ضرورت ہے۔
کیا آپ ضمنی اثرات سے واقف ہیں؟
مثال کے طور پر:
persist order
create payment
publish event
send email
کیا کلیدی نامعلوم دستاویزی ہیں؟
کچھ غیر یقینی صورتحال باقی رہ سکتی ہے۔ لیکن یہ باہر کھڑا نہیں ہونا چاہئے.
اگر آپ ان سوالات کا جواب دے سکتے ہیں، تو آپ کے پاس اس فعالیت کو منتقل کرنے کے لیے کافی ڈھانچہ ہے۔
عملی پری مائیگریشن ری فیکٹرنگ ورک فلو
میں جو ورک فلو استعمال کروں گا وہ درج ذیل ہے:
1. ایک خصوصیت منتخب کریں۔
پوری درخواست کو ری فیکٹر نہ کریں۔
منتخب کریں:
Process Order
Generate Invoice
Renew Subscription
2. رویے کے تحفظ کی تصدیق کریں۔
ساختی تبدیلیاں کرنے سے پہلے اس بات کو یقینی بنائیں کہ آپ کے پاس تنقیدی رویے کے لیے ٹیسٹ ہیں۔
قبضہ:
outputs
state transitions
side effects
errors
contracts
3. نقل مکانی کی رکاوٹوں کی نشاندہی کریں۔
مندرجہ ذیل مجموعہ تلاش کریں:
direct database access
provider SDKs
global state
framework-specific objects
static dependencies
shared mutable state
حسابات اور فیصلوں کو انفراسٹرکچر سے دور رکھیں۔
مثال کے طور پر:
calculate price
validate transition
choose status
calculate commission
5. سیون کا تعارف
اپنے ارد گرد کم سے کم حدود بنائیں۔
database
payments
events
email
storage
external APIs
نقل مکانی کی وجہ کے بغیر تجرید تخلیق نہ کریں۔
6. انفراسٹرکچر لوکلائزیشن
ٹکنالوجی کی مخصوص کارروائیوں کو اڈیپٹرز میں منتقل کریں۔
مثال کے طور پر:
MySqlOrderRepository
StripePaymentGateway
KafkaOrderEvents
SendGridEmailSender
7. آرکیسٹریشن کو مرئی بنائیں
ترتیب کو سمجھنے کی صلاحیت کا مقصد۔
load
↓
decide
↓
persist
↓
perform side effects
↓
return
8. ہر قدم کے بعد طرز عمل کے ٹیسٹ چلائیں۔
ایک بیچ میں 10 ریفیکٹرز نہ کریں۔ تبدیلیاں چھوٹی رکھیں۔
9. مقابلے سے پہلے اور بعد میں
چیک کریں:
inputs
outputs
errors
side effects
data shapes
ordering
transactions
10. جب ممکن ہو تو ہجرت روک دیں۔
ری فیکٹرنگ جاری نہ رکھیں کیونکہ یہ آپ کے کوڈ کو صاف کر سکتا ہے۔ ایسا ہمیشہ ہو سکتا ہے۔
مقصد ہجرت کے لیے تیاری کرنا ہے۔
ہجرت سے پہلے کیا نہیں ریفیکٹر
کچھ چیزیں ایسی ہیں جو آپ کو عام طور پر اس مرحلے پر تبدیل کرنے سے گریز کرنا چاہئے جب تک کہ وہ براہ راست منتقلی کو روک دیں۔
اسے کہیں بھی نام دیں۔
شاید آپ کو سینکڑوں پرانے نام پسند نہ ہوں۔ تاہم، ہر چیز کا نام تبدیل کرنے سے منتقلی کی تھوڑی قیمت کے ساتھ بڑا فرق پڑتا ہے۔
مکمل اسٹوریج کی شکل
ایک ہی مسئلہ۔ شور رویے کی تبدیلیوں کی جانچ کرنا زیادہ مشکل بناتا ہے۔
تمام پیٹرن کو تبدیل کریں
میراثی نظام میں شامل ہو سکتے ہیں:
singletons
service locators
static utilities
large classes
کچھ عارضی طور پر رہ سکتے ہیں۔ ہجرت کی حدود کو عبور کرنے والے مسائل کو ٹھیک کریں۔
مستحکم الگورتھم دوبارہ لکھنا
پرانے حسابات بدصورت ہو سکتے ہیں، لیکن اگر وہ محفوظ اور الگ تھلگ ہیں، تو پہلے منتقل ہونا اور بعد میں بہتر ہونا زیادہ محفوظ ہو سکتا ہے۔
پائے جانے والے کسی بھی کیڑے کو درست کریں۔
یہ خاص طور پر اہم ہے۔
اگر آپ کو نقل مکانی کی تیاری کے دوران کوئی کیڑے نظر آتے ہیں، تو انہیں لاگ ان کریں۔ اس کے بعد یہ طے کرتا ہے کہ آیا ترمیمات کا تعلق اسی تبدیلی سے ہے۔
شامل کرنا:
structural refactor
+
behavioral correction
+
platform migration
ناکامی کو سمجھنا زیادہ مشکل ہو جاتا ہے۔
کبھی کبھی صحیح جواب ہوتا ہے:
preserve bug
migrate
fix bug intentionally afterward
یہ غیر آرام دہ لگتا ہے۔ تاہم، نقل مکانی کے دوران حادثاتی رویے میں تبدیلیاں بہت زیادہ خطرناک ہو سکتی ہیں۔
ریفیکٹرنگ تیاری ہے، منتقلی نہیں۔
پری مائیگریشن ری فیکٹرنگ کے لیے کبھی نہ ختم ہونے والا آرکیٹیکچرل پروجیکٹ بننا آسان ہے۔
آپ اس طرح شروع کریں:
ڈیٹا بیس کو الگ تھلگ کیا جانا چاہیے۔
پھر:
آپ کے ڈومین ماڈل کو دوبارہ ڈیزائن کرنے کی ضرورت ہے۔
پھر:
شاید میں ایک واقعہ پیش کروں۔
پھر:
اگر آپ ایسا کرتے ہیں، تو یہ شاید مائیکرو سروس ہونا پڑے گا۔
مہینے گزر گئے اور کچھ بھی ہجرت نہیں ہوا۔ ریفیکٹرنگ ایک پروجیکٹ بن گیا۔
وہ بھی فیل موڈ ہے۔ اہداف مخصوص رہنے چاہئیں۔
پہلے:
ProcessOrder
├── MySQL
├── pricing rules
├── Stripe
├── Kafka
├── email
└── framework internals
بعد میں:
ProcessOrder
├── OrderRepository
├── pricing rules
├── PaymentGateway
├── OrderEvents
└── EmailSender
یہ کافی ہو سکتا ہے.
اب آپ کے پاس ایک انتخاب ہے۔
آپ ہجرت کر سکتے ہیں:
MySQL → PostgreSQL
قیمتوں کو دوبارہ ڈیزائن کیے بغیر۔
آپ تبدیل کر سکتے ہیں:
Stripe adapter
ترتیب کو تبدیل کیے بغیر۔
آپ منتقل کر سکتے ہیں:
ProcessOrder
معاہدے کو برقرار رکھتے ہوئے دوسرے رن ٹائم پر سوئچ کریں۔
ریفیکٹرنگ نے اختیارات بنائے ہیں: یہ قدر ہے۔
نتیجہ
وراثت کی منتقلی اس وقت خطرناک ہو جاتی ہے جب متعدد قسم کی تبدیلیاں بیک وقت ہوتی ہیں۔
درج ذیل کو تبدیل کریں:
behavior
architecture
infrastructure
runtime
data
deployment
پھر یہ سمجھنے کی کوشش کریں کہ کن تبدیلیوں کی وجہ سے ناکامی ہوئی۔
نقل مکانی شروع ہونے سے پہلے غیر یقینی صورتحال کو کم کرنا ایک محفوظ طریقہ ہے۔
پہلے فنکشن کو سمجھیں، پھر رویے کی خصوصیات بنائیں، اور فنکشن کو جان بوجھ کر تبدیل کیے بغیر ساخت کو تبدیل کریں۔
انحصار کے ارد گرد حدود بنائیں. انفراسٹرکچر اور کاروباری فیصلے الگ۔ بیرونی نظاموں کو مقامی بنائیں۔ ضمنی اثرات کو نظر میں رکھیں۔ جب بھی ڈھانچہ تبدیل ہوتا ہے تو ہم طرز عمل کے ٹیسٹ چلاتے ہیں۔ اور فیچر پورٹیبل ہونے کے بعد ری فیکٹرنگ بند کر دیں۔
حکم درج ذیل ہے:
Understand
↓
Characterize
↓
Refactor
↓
Migrate
AI ری فیکٹرنگ کے مرحلے کو ڈرامائی طور پر تیز تر بنا سکتا ہے۔
آپ انحصار کی شناخت کر سکتے ہیں، انٹرفیس نکال سکتے ہیں، کالز کو اڈاپٹر کے پیچھے منتقل کر سکتے ہیں، نفاذ کا موازنہ کر سکتے ہیں، اور بڑے فرقوں کا جائزہ لے سکتے ہیں۔
تاہم، تیزی سے ری فیکٹرنگ آرکیٹیکچرل فیصلے کی ضرورت کو ختم نہیں کرتی ہے۔ یہ فیصلہ زیادہ اہم ہو جاتا ہے۔
کیونکہ مقصد میراثی نظام کا صاف ترین ورژن بنانا نہیں ہے۔ مقصد تخلیق کرنا ہے۔ محفوظ طریقے سے منتقل کرنے کے لیے کافی ڈھانچہ.
اور جب آپ رویے کو تبدیل کیے بغیر بنیادی ڈھانچے کو تبدیل کر سکتے ہیں، تو نقل مکانی دوبارہ لکھنے کی طرح نہیں لگتی ہے۔
یہ کنٹرول شدہ تبدیلیوں کی ایک سیریز کی طرح نظر آنے لگتا ہے۔