মূল বিষয়বস্তুতে যান
Clean Code Mastery

Facade Pattern: একটা ফোন কলেই পুরো জটিল ট্রিপ বুক

ট্রাভেল এজেন্টের গল্প দিয়ে Facade pattern শেখো। অনেকগুলো জটিল subsystem-কে একটা সহজ method-এর পেছনে লুকিয়ে রাখো, যাতে client code ছোট আর পরিষ্কার থাকে।

20 মিনিট আপডেট: June 11, 2026beginner
design-patternsstructural-patternsfacadesimplificationsubsystemstypescriptcsharp

☎️ একটা ফোন কলেই ট্রিপ বুক

ধরো, তারিক সাহেব একজন স্কুল শিক্ষক। অঙ্ক বোঝাতে ওস্তাদ, কিন্তু বুকিং ওয়েবসাইট খুললেই মাথা ঘুরে যায়। তার ছেলে রুবেল CSE তৃতীয় বর্ষে পড়ে। কাজিনের বিয়ে আসছে ঢাকায় — চারজন যাবে, ২০ থেকে ২৪ ডিসেম্বর।

এখন ভাবো কতগুলো কাজ করতে হবে।

ট্রেনের availability চেক করতে হবে, চারটা টিকিট বুক করতে হবে। বিয়ের ভেন্যুর কাছে হোটেল খুঁজতে হবে, দাম তুলনা করতে হবে, দুটো রুম রিজার্ভ করতে হবে। ট্যাক্সির ব্যবস্থা করতে হবে যাতে সকাল ছটায় পরিবারকে স্টেশন থেকে তুলতে পারে। আর এই কাজগুলো একে অপরের সাথে জড়িত — ট্রেনে ওয়েটলিস্ট থাকলে হোটেলের তারিখ বদলাতে হতে পারে, হোটেল ফুল হলে এলাকা বদলাবে, আর সেটা ট্যাক্সির পরিকল্পনাও বদলে দেবে।

তারিক সাহেব নিজে করতে গেলে লাগবে একটা রেলওয়ে অ্যাকাউন্ট, তিনটা হোটেল অ্যাপ, দুটো ট্যাক্সির নম্বর, আর পুরো একটা সন্ধ্যার ঝামেলা। একটু ভুল হলেই — হোটেল আগে বুক করে ফেললে ট্রেন confirm হওয়ার আগেই — সপ্তাহের পর সপ্তাহ refund-এর ঝামেলায় টাকা আটকে থাকবে।

তার বদলে, তিনি ফোন করলেন বাজারের পাশে করিম ট্রাভেলস-এ। সেখানে সুমাইয়া পনেরো বছর ধরে ট্রিপ বুক করছেন। তারিক সাহেব একটাই কথা বললেন: "ঢাকা, ৪ জন, ২০ থেকে ২৪ ডিসেম্বর।" ব্যস। সুমাইয়া ট্রেন বুক করলেন, তারপর হোটেল, তারপর ট্যাক্সি — সঠিক ক্রমে, পথে সব ছোট সমস্যা সামলে। ট্রেন না পাওয়া গেলে পনেরোটা confusing error-এর বদলে একটাই পরিষ্কার উত্তর নিয়ে call back করলেন।

রুবেল বাবাকে মাত্র দুই মিনিটে ফোন রাখতে দেখে একটু ভাবল। সুমাইয়া রেলওয়ে বুকিং, হোটেল বা ট্যাক্সি স্ট্যান্ড মুছে ফেলেননি। সেগুলো আছে, আসল কাজও করছে। তিনি শুধু তাদের সামনে দাঁড়িয়েছেন আর বাবাকে পুরো জটিল সিস্টেমের জন্য একটাই সহজ মুখ দিয়েছেন।

সেই একটাই সহজ মুখ হলো Facade pattern। (Facade ফরাসি শব্দ — মানে বিল্ডিং-এর সামনের দেয়াল। তুমি সুন্দর দেয়াল দেখো, পেছনের পাইপ-তার দেখো না।)

চিত্র ১: সুমাইয়ার আগে আর পরে — যাত্রা হিসেবে

satisfaction score-গুলো দেখো। কাজ উবে যায়নি — সুমাইয়া এখনো সব করছেন। কিন্তু জটিলতাটা সরে গেছে একজন confused customer-এর কাছ থেকে একজন বিশেষজ্ঞের কাছে যিনি এই কাজ প্রতিদিন করেন। জটিলতাকে একটা ভালোভাবে trained জায়গায় সরিয়ে দেওয়াটাই এই pattern-এর পুরো ব্যাপার।

Facade pattern কী?

Facade হলো একটা structural design pattern। এটা একটা বড়, জটিল code-এর — একগুচ্ছ class, একটা library, বা পুরো একটা subsystem-এর — জন্য একটাই সহজ entry point দেয়। Facade class জানে কোন subsystem object-গুলোকে call করতে হবে, কোন ক্রমে, কোন input দিয়ে। Client facade-এ একটামাত্র method call করে, facade বাকি সব orchestration করে।

তিনটা গুরুত্বপূর্ণ কথা মনে রাখো:

  1. Facade client-কে দেয় একটা call-এ যা চায় তা — ছয়টা class না শিখে।
  2. Subsystem-গুলো জানেই না facade আছে। তাদের দিক থেকে facade শুধু আরেকটা caller।
  3. Facade দরজায় তালা দেয় না। বিশেষ দরকারে client facade পাশ কাটিয়ে সরাসরি subsystem call করতে পারে।
💡

এভাবে মনে রাখো: Facade = অনেক অংশের একটা মুখ। এটা নতুন ক্ষমতা যোগ করে না, কারো interface বদলায় না। শুধু একটা সাধারণ call sequence-কে একটা সহজ method-এ ভরে দেয় — যাতে ৯৫% caller কখনো messy অংশ না ছোঁয়।

বাস্তব প্রজেক্টে এই pattern অন্য নামেও চলে: business logic-এর সামনে থাকলে Service বা Service Layer, আর বাইরের system-এর সামনে থাকলে Gateway। ধারণাটা একই।

client-এর দৃষ্টিকোণ থেকে before/after পাশাপাশি দেখো:

client-কে কী জানতে হবেFacade ছাড়াFacade দিয়ে
class-এর সংখ্যাতিনটা service আর তাদের quirkএকটাই: TripFacade
method signatureতিনটা style-এ প্রায় আটটাএকটাই: bookTrip(...)
সঠিক ধাপের ক্রমট্রেন, তারপর হোটেল, তারপর ট্যাক্সি — মুখস্থকিছুই না — facade জানে
ব্যর্থতায় rollbackপ্রতিটি call site-এ হাতে লেখাকিছুই না — facade-এ built-in
subsystem API বদলের প্রভাববিশটা ফাইল এডিটএকটা ফাইল এডিট

কলেজ কর্নার — coupling কমানো: facade-এর formal সুবিধা হলো coupling কমে যাওয়া। facade ছাড়া প্রতিটি client class সব subsystem class-এর reference রাখে: m client আর n subsystem থাকলে m × n dependency edge হয়। Facade দিয়ে হয় m + n edge — প্রতিটি client facade-এ point করে, facade subsystem-এ। ১০টা screen আর ৫টা service-এর জন্য ৫০-এর বদলে মাত্র ১৫টা edge। কম edge মানে কম জায়গায় ভাঙে যখন subsystem signature বদলায়, বড় codebase-এ দ্রুত compile, আর unit testing অনেক সহজ — পাঁচটা service-এর বদলে একটা facade mock করো। এটা Law of Demeter ("শুধু তোমার immediate friend-এর সাথে কথা বলো") subsystem boundary-তে প্রয়োগ করা।

কোন সমস্যাটা সমাধান করে

ধরো রুবেল agent-এর ধারণাটা বাদ দিয়ে সরাসরি app-এর button-click handler-এ trip-booking logic লিখে ফেলল। যতবার trip বুক হবে এই কাজগুলো করতে হবে:

// WITHOUT a facade: the client does everything itself
const train = trainService.searchTrains("KOAA", "JP", "2026-12-20");
if (train.waitlist > 10) throw new Error("Train not safe to book");
const pnr = trainService.bookTickets(train.trainNo, 4);
 
const hotels = hotelService.searchNear("Wedding Venue, Jaipur");
const cheapest = hotels.sort((a, b) => a.price - b.price)[0];
const hotelRef = hotelService.reserve(cheapest.id, "2026-12-20", "2026-12-24", 2);
 
const taxiRef = taxiService.schedulePickup("Jaipur Junction", train.arrivalTime);
 
// And if the taxi fails? Now YOU must cancel the hotel AND the train,
// in the right order, at every single call site. Good luck.

সমস্যাগুলো দেখো:

  • client-কে তিনটা আলাদা service, তাদের method name, আর parameter shape জানতে হচ্ছে।
  • client-কে সঠিক ক্রম মনে রাখতে হচ্ছে: আগে ট্রেন, তারপর হোটেল, তারপর ট্যাক্সি।
  • client-কে rollback handle করতে হচ্ছে: step ৩ fail হলে step ২ আর ১ undo করো।
  • এই জ্ঞান trip বুক করে এমন প্রতিটি screen-এ copy-paste হয়। hotelService.reserve signature বদলালে বিশটা ফাইল এডিট করতে হবে।

subsystem-গুলো কখনো প্রতিটি caller-এর ব্যাপার হওয়ার কথা ছিল না। এগুলো implementation detail, কিন্তু পুরো codebase-এ ছড়িয়ে পড়েছে।

চিত্র ২: client প্রতিটি subsystem-এ জড়িয়ে vs সামনে একটা facade

বাঁ দিকে client-এর চারটা dependency আর চারটা উপায়ে ভুল হওয়ার সুযোগ। ডান দিকে client শুধু একটা class আর একটা method জানে। সব wiring একটা জায়গায়।

app বড় হলে সেই choreography copy-paste করার খরচ কত — client screen-এর ভেতরে থাকা booking logic-এর লাইন গুনো:

চিত্র ৩: client-এ duplicate booking logic vs একটা facade-এ রাখা

প্রথম line হলো facade-ছাড়া পথ: প্রতি screen-এ পঁচিশ লাইনের choreography, সব screen-এ গুণ করো, আর একটা API বদলালে সব screen বদলাতে হবে। দ্বিতীয় line হলো facade পথ: প্রতি screen-এ মাত্র দুই লাইন (agent তৈরি করো, bookTrip call করো), আর পঁচিশ লাইন ঠিক একবার আছে — facade-এর ভেতরে।

⚙️ কীভাবে কাজ করে, ধাপে ধাপে

Facade তৈরি করা সবচেয়ে সহজ pattern-গুলোর একটা। এই ধাপগুলো follow করো:

  1. বারবার হওয়া dance খুঁজে বের করো। subsystem call-এর যে group client-রা একই লক্ষ্যে একই ক্রমে বারবার করছে সেটা খোঁজো। ("ট্রেন বুক করো, তারপর হোটেল, তারপর ট্যাক্সি।")
  2. Facade class তৈরি করো। দরকারি subsystem object-গুলোর private reference দাও — সাধারণত constructor-এর মাধ্যমে pass করো।
  3. প্রতিটি client goal-এর জন্য একটা method লেখো। bookTrip(...) পুরো sequence করবে: ordering, error handling, rollback। method name-এ ধাপ না, লক্ষ্য বলো।
  4. Client-দের facade-এর মাধ্যমে route করো। client code-এ copy-paste করা choreography-কে একটা facade call দিয়ে replace করো।
  5. পাশের দরজা খোলা রাখো। subsystem-গুলোকে দুনিয়া থেকে private করো না। power user-এর অস্বাভাবিক ক্ষেত্রে সরাসরি access লাগতে পারে।
  6. মোটা হলে ভাগ করো। একটা facade যদি unrelated কাজ জমাতে শুরু করে (trip + payroll + reports), দুই বা তিনটা ছোট facade-এ ভাগ করো। facade-রা অন্য facade-ও call করতে পারে।
চিত্র ৪: Facade pattern-এর structure

arrow-গুলো দেখো। client শুধু facade-এ point করে। facade subsystem-গুলোতে point করে। কোনো arrow subsystem থেকে facade-এর দিকে ফিরে আসছে না — subsystem-গুলো innocent আর reusable থাকে।

facade-এর ভেতরে একটা booking একটা strict sequence of state-এর মধ্য দিয়ে যায়। মাঝপথে কিছু fail হলে rollback path আছে:

চিত্র ৫: state হিসেবে facade-এর মধ্য দিয়ে একটা booking-এর যাত্রা

এই state machine হলো সুমাইয়ার মাথায় রাখা জ্ঞান — কোনটার পর কোনটা, আর ধাপ fail হলে কী undo করতে হবে। facade ছাড়া এই exact machine app-এর প্রতিটি screen-এ আবার implement হয়, সাধারণত একটু ভুলভাবে।

বাস্তব code উদাহরণ

এখানে TypeScript-এ পুরো ট্রাভেল এজেন্ট: একটা সহজ method-এর পেছনে তিনটা messy subsystem।

// ----- Subsystem 1: trains (its own fussy API) -----
class TrainService {
  searchTrains(from: string, to: string, date: string) {
    console.log(`  [Train] Searching ${from} -> ${to} on ${date}`);
    return { trainNo: "12307", waitlist: 2, arrivalTime: "06:30" };
  }
  bookTickets(trainNo: string, count: number): string {
    console.log(`  [Train] Booked ${count} tickets on ${trainNo}`);
    return "PNR-4521234567";
  }
  cancel(pnr: string) {
    console.log(`  [Train] Cancelled ${pnr}`);
  }
}
 
// ----- Subsystem 2: hotels (different style, different shapes) -----
class HotelService {
  searchNear(place: string) {
    console.log(`  [Hotel] Searching near "${place}"`);
    return [
      { id: "H1", name: "Hotel Pink City", price: 2200 },
      { id: "H2", name: "Raj Mahal Stay", price: 1800 },
    ];
  }
  reserve(id: string, inDate: string, outDate: string, rooms: number): string {
    console.log(`  [Hotel] Reserved ${rooms} room(s) at ${id}, ${inDate} to ${outDate}`);
    return "HOTEL-REF-889";
  }
  cancel(ref: string) {
    console.log(`  [Hotel] Cancelled ${ref}`);
  }
}
 
// ----- Subsystem 3: taxis (yet another API) -----
class TaxiService {
  schedulePickup(place: string, time: string): string {
    console.log(`  [Taxi] Pickup at ${place}, ${time}`);
    return "TAXI-77";
  }
}
 
// ----- The Facade: Sharma Travels -----
class TripFacade {
  constructor(
    private trains = new TrainService(),
    private hotels = new HotelService(),
    private taxis = new TaxiService(),
  ) {}
 
  // ONE simple method. All the mess lives inside.
  bookTrip(from: string, to: string, inDate: string, outDate: string, people: number) {
    console.log(`Priya: booking ${to} trip for ${people} people...`);
 
    // Step 1: train first — no train, no trip.
    const train = this.trains.searchTrains(from, to, inDate);
    if (train.waitlist > 10) {
      throw new Error("Sorry, train waitlist too long. Try other dates.");
    }
    const pnr = this.trains.bookTickets(train.trainNo, people);
 
    // Step 2: hotel — pick the cheapest; roll back train if it fails.
    let hotelRef: string;
    try {
      const options = this.hotels.searchNear(`Wedding Venue, ${to}`);
      const cheapest = options.sort((a, b) => a.price - b.price)[0];
      hotelRef = this.hotels.reserve(cheapest.id, inDate, outDate, 2);
    } catch (err) {
      this.trains.cancel(pnr); // rollback in the right order
      throw err;
    }
 
    // Step 3: taxi to receive the family at the station.
    const taxiRef = this.taxis.schedulePickup(`${to} Junction`, train.arrivalTime);
 
    console.log("Priya: all done! Have a great trip.");
    return { pnr, hotelRef, taxiRef };
  }
}
 
// ----- The client: one line, like one phone call -----
const agent = new TripFacade();
const booking = agent.bookTrip("Kolkata", "Jaipur", "2026-12-20", "2026-12-24", 4);
console.log("Client received:", booking);

Output:

Priya: booking Jaipur trip for 4 people...
  [Train] Searching Kolkata -> Jaipur on 2026-12-20
  [Train] Booked 4 tickets on 12307
  [Hotel] Searching near "Wedding Venue, Jaipur"
  [Hotel] Reserved 2 room(s) at H2, 2026-12-20 to 2026-12-24
  [Taxi] Pickup at Jaipur Junction, 06:30
Priya: all done! Have a great trip.
Client received: { pnr: 'PNR-4521234567', hotelRef: 'HOTEL-REF-889', taxiRef: 'TAXI-77' }

client-এর লাইন গুনো: দুটো। agent তৈরি করো, call করো। ordering rule (হোটেলের আগে ট্রেন), সবচেয়ে সস্তা হোটেলের logic, আর rollback — সব একটাই জায়গায়: TripFacade। কাল হোটেল API বদলালে একটা ফাইল ঠিক করো, app-এর প্রতিটি screen কাজ করতে থাকবে।

sequence হিসেবে ফোন কল। client থেকে একটা arrow ঢোকে, সব ব্যস্ত arrow facade আর তার subsystem-এর মধ্যে থাকে:

চিত্র ৬: একটা bookTrip call facade-এর পেছনে ছড়িয়ে পড়ছে

আর লক্ষ্য করো আমরা কী করিনি: TrainService delete বা hide করিনি। কোনো special admin screen যদি শুধু ট্রেন cancel করতে চায়, সে এখনো সরাসরি trains.cancel(pnr) call করতে পারবে। facade হলো সুবিধাজনক সামনের দরজা, তালা দেওয়া গেট না।

একটা সাধারণ booking-এ সুমাইয়ার আসল কষ্ট কোথায় যায়? এর প্রায় কিছুই customer দেখতে পায় না:

চিত্র ৭: একটা সহজ ফোন কলের পেছনে এজেন্টের কষ্ট কোথায় যায়

শুধু পাতলা "customer-এর সাথে কথা বলা" slice-টাই public interface। বাকি ৯৫% হলো orchestration যেটা client কখনো দেখে না — একটা ভালো facade-এর ভেতরে এই অনুপাতটাই চাই।

C#-এ একই ধারণা

একই ট্রাভেল এজেন্ট, সংক্ষিপ্তভাবে, C#-এ:

// Three subsystems (imagine each has many more methods)
public class TrainService
{
    public string Book(string from, string to, string date, int people)
    { Console.WriteLine("  [Train] booked"); return "PNR-123"; }
    public void Cancel(string pnr) => Console.WriteLine("  [Train] cancelled");
}
 
public class HotelService
{
    public string Reserve(string city, string inDate, string outDate)
    { Console.WriteLine("  [Hotel] reserved"); return "HOTEL-9"; }
}
 
public class TaxiService
{
    public string Schedule(string station)
    { Console.WriteLine("  [Taxi] scheduled"); return "TAXI-7"; }
}
 
// The Facade
public class TripFacade
{
    private readonly TrainService _trains = new();
    private readonly HotelService _hotels = new();
    private readonly TaxiService _taxis = new();
 
    public (string Pnr, string Hotel, string Taxi) BookTrip(
        string from, string to, string inDate, string outDate, int people)
    {
        var pnr = _trains.Book(from, to, inDate, people);
        try
        {
            var hotel = _hotels.Reserve(to, inDate, outDate);
            var taxi = _taxis.Schedule($"{to} Junction");
            return (pnr, hotel, taxi);
        }
        catch
        {
            _trains.Cancel(pnr);   // rollback lives here, once
            throw;
        }
    }
}
 
// Client
var agent = new TripFacade();
var trip = agent.BookTrip("Kolkata", "Jaipur", "2026-12-20", "2026-12-24", 4);
Console.WriteLine($"Done: {trip.Pnr}, {trip.Hotel}, {trip.Taxi}");

বাস্তব .NET প্রজেক্টে তুমি প্রায়ই এই exact shape দেখবে — যেটাকে application service বলে। controller পাতলা থাকে, আর একটা service class repository, payment gateway, আর email sender orchestrate করে।

Python-এ একটু ভিন্নভাবে: স্কুলের reception desk

একই pattern, ভিন্ন counter। ধরো রুবেলের পুরনো স্কুলে নতুন ছাত্র ভর্তি হলে তিনটা আলাদা অফিস লাগে — আর parents-দের তাদের মধ্যে দৌড়াদৌড়ি করতে না হওয়াই ভালো। reception desk হলো সেই facade:

# ----- Three school subsystems, each with its own fussy steps -----
class FeesOffice:
    def pay(self, student_id, amount):
        print(f"  [Fees] Received Rs.{amount} for {student_id}")
        return f"RCPT-{student_id}"
    def refund(self, receipt):
        print(f"  [Fees] Refunded {receipt}")
 
class AdmissionOffice:
    def verify_documents(self, student_id):
        print(f"  [Admission] Documents OK for {student_id}")
    def allot_section(self, student_id):
        print(f"  [Admission] {student_id} put in section B")
        return "B"
 
class CertificateCell:
    def issue_id_card(self, student_id, section):
        print(f"  [IDCell] Card printed for {student_id}, section {section}")
        return f"CARD-{student_id}"
 
# ----- The Facade: one reception desk for the parents -----
class ReceptionFacade:
    def __init__(self):
        self.fees = FeesOffice()
        self.admission = AdmissionOffice()
        self.certificates = CertificateCell()
 
    def admit_student(self, student_id, amount):
        """One method = one goal: 'admit my child'."""
        receipt = self.fees.pay(student_id, amount)
        try:
            self.admission.verify_documents(student_id)
            section = self.admission.allot_section(student_id)
        except Exception:
            self.fees.refund(receipt)      # rollback lives here, once
            raise
        card = self.certificates.issue_id_card(student_id, section)
        return {"receipt": receipt, "section": section, "card": card}
 
# ----- The parent: one visit, one sentence -----
desk = ReceptionFacade()
result = desk.admit_student("STU-2026-114", 25000)
print("Parent receives:", result)

একজন parent desk-এ একবার বলে "আমার সন্তানকে ভর্তি করুন।" desk fee দেয়, documents verify করে, section allot করে, ID card print করে — মাঝের ধাপ fail হলে automatically refund করে। তিনটা অফিস, কোনো দৌড়াদৌড়ি নেই। একটু ভাবো — এটা হুবহু ট্রাভেল এজেন্টের মতো, শুধু noun বদলে গেছে। একবার shape বুঝলে, জীবনের প্রতিটি counter-এ খুঁজে পাবে।

বাস্তব software-এ কোথায় দেখা যায়

Facade সব জায়গায় আছে। shape শিখলে আর চোখ এড়ায় না।

  • jQuery। পুরনো browser API element খোঁজা আর AJAX call করার জন্য দীর্ঘ আর browser-ভেদে inconsistent ছিল। jQuery-এর $() আর $.ajax() হলো facade — একটা ছোট call, পেছনে messy browser-specific code-এর স্তূপ।
  • SDK client। AWS SDK, Stripe SDK, আর Razorpay SDK হলো raw HTTPS-এর উপর facade। তুমি s3.putObject(...) call করো, SDK একটা method-এর পেছনে signing, authentication, retry, আর serialization handle করে।
  • Spring-এর JdbcTemplate Raw JDBC-তে connection খুলতে হয়, statement তৈরি করতে হয়, result set loop করতে হয়, finally block-এ সব বন্ধ করতে হয়। JdbcTemplate সেই পুরো dance-কে একটা query(...) call-এ ভরে — textbook facade। Spring Boot-কে প্রায়ই Spring-এর configuration-এর উপর একটা বিশাল facade বলা হয়।
  • Web app-এ service layer। Martin Fowler-এর Service Layer pattern হলো domain logic-এর উপর facade। controller-রা orderService.placeOrder(...) call করে, service নিচে inventory, payment, আর email coordinate করে।
  • Game আর media engine। videoConverter.convert("clip.ogg", "mp4") style call যেটা codec factory, bitrate reader, আর audio mixer-এর উপর বসে — এটাই classic facade demo। বাস্তব multimedia library-ও একইভাবে structured।
  • Open-source reference। java-design-patterns facade example-এ একটা FacadeMine class অনেক dwarf-worker subsystem পরিচালনা করে।

কখন ব্যবহার করবে, কখন না

পরিস্থিতিFacade ব্যবহার করবে?
client-রা অনেক call site-এ একই multi-step dance repeat করছেহ্যাঁ — dance-কে একটা method-এ ভরো
third-party বা legacy library wrap করছো যেটা তুমি control করো নাহ্যাঁ — একটা জায়গা upstream পরিবর্তন absorb করবে
system layered করতে চাও (UI থেকে service থেকে data) পরিষ্কারভাবেহ্যাঁ — প্রতিটি layer boundary-তে একটা facade
নতুন team member বারবার জিজ্ঞেস করছে কোথা থেকে শুরু করবহ্যাঁ — facade হলো documented সামনের দরজা
subsystem শুধু এক বা দুটো সহজ classনা — facade pure overhead
প্রতিটি caller ধাপের আলাদা fine-grained control চায়না — একটা সহজ method সবার কাজ করতে পারবে না
app-এর সব operation একটা class-এ ঢালতে প্রলুব্ধ হচ্ছোথামো — এটা god object, একাধিক facade-এ ভাগ করো
শুধু একটা interface অন্যটায় convert করতে চাওনা — ওটা Adapter-এর কাজ

একই সিদ্ধান্ত, map হিসেবে:

চিত্র ৮: সামনে facade রাখা উচিত?

উপরে-ডানে হলো facade-এর জায়গা: messy subsystem আর সব caller একই সহজ জিনিস চায়। নিচে-ডানে আকর্ষণীয় কেস — messy subsystem কিন্তু প্রতিটি caller আলাদা control চায়। সেখানে সাধারণ পথের জন্য facade তৈরি করো, কিন্তু পাশের দরজা চওড়া খোলা রাখো।

ছাত্রছাত্রীরা যে সাধারণ ভুল করে

⚠️

ভুল ১: God object তৈরি করা। ছাত্ররা "আরেকটা method" যোগ করতে থাকে যতক্ষণ না AppFacade চল্লিশটা class জানে। একটা facade-এর একটা cohesive goal area cover করা উচিত। সেটা পার হলে ভাগ করো — facade অন্য facade call করতে পারে।

ভুল ২: পাশের দরজা বন্ধ করা। সব subsystem class private করা "সবাই facade ব্যবহার করবে" বলে পরিষ্কার মনে হয়, কিন্তু advanced caller-কে আটকায়। facade হলো default পথ, জেল না।

ভুল ৩: শুধু forward করা facade। facade.bookTrain() যদি শুধু one-to-one trainService.bookTrain() call করে, তুমি কোনো মূল্য ছাড়া একটা layer যোগ করেছ। facade তার জায়গা earn করে orchestrate করে — ordering, combining, error handling।

ভুল ৪: client-এ এখনো business rule রাখা। caller-রা যদি facade-এর পরেও call order আর rollback সিদ্ধান্ত নেয়, তুমি শুধু method name সরিয়েছ। পুরো sequence, failure handling সহ, facade-এর ভেতরে থাকা উচিত।

ভুল ৫: Facade-এর মাধ্যমে subsystem type leak করা। bookTrip যদি raw IrctcTrainResponse object return করে, প্রতিটি client এখনো train library-র সাথে coupled। Simple, facade-owned type return করো (plain object, ছোট DTO), নাহলে coupling return value-এর মাধ্যমে ফিরে আসে।

কাছের cousin-দের সাথে তুলনা

Facade প্রায়ই Adapter আর Mediator-এর সাথে গুলিয়ে ফেলা হয়। পার্থক্যটা পরিষ্কার করা যাক:

প্রশ্নFacadeAdapterMediator
পেছনে কতগুলো object?অনেক (একটা subsystem)একটাঅনেক colleague
দেওয়া interfaceনতুন, সহজ একটাclient যা expect করে সেটাএকটা central coordination hub
জ্ঞানের দিকsubsystem-রা facade জানে নাadaptee adapter জানে নাcolleague-রা mediator জানে আর তার মাধ্যমে কথা বলে
এক কথায় লক্ষ্যSimplifyConvertCoordinate
উদাহরণট্রেন+হোটেল+ট্যাক্সির উপর ট্রাভেল এজেন্টবিদেশি charger-এর জন্য plug converterবিমানের মধ্যে air traffic controller
চিত্র ৯: Facade vs Adapter vs Mediator

আরো দুটো দ্রুত সম্পর্ক:

  • Proxy একটা object-এর সামনে দাঁড়ায় আর একই interface রাখে। Facade অনেক object-এর সামনে দাঁড়ায় আর সহজ একটা নতুন interface তৈরি করে।
  • Abstract Factory facade-কে replace করতে পারে যখন শুধু subsystem object কীভাবে তৈরি হয় সেটা লুকাতে চাও।
  • facade প্রায়ই Singleton করা হয়, কারণ পুরো app-এর জন্য সাধারণত একটা shared agent যথেষ্ট।

এই article-এর সব কিছু একটা tree-তে:

চিত্র ১০: Facade pattern-এর পুরো mind map

দ্রুত revision box

+--------------------------------------------------------------+
|                    FACADE — QUICK REVISION                    |
+--------------------------------------------------------------+
| Idea      : One simple entry point to many messy classes.    |
| Analogy   : Travel agent books train + hotel + taxi          |
|             from your ONE phone call.                        |
| Offers    : A NEW, simpler interface (not the same one).     |
| Inside    : Ordering of steps, error handling, rollback.     |
| Key rule  : Subsystems never know the facade exists.         |
| Key rule  : Side door stays open for power users.            |
| Wins      : Client depends on 1 class; changes stop at the   |
|             facade; obvious starting point for newcomers.    |
| Coupling  : m x n dependency edges drop to m + n.            |
| Watch out : God object! Split fat facades into smaller ones. |
| Cousins   : Adapter converts ONE interface;                  |
|             Mediator coordinates peers BETWEEN themselves.   |
| Real life : jQuery, AWS/Stripe SDK clients, JdbcTemplate,    |
|             service layers in web apps                       |
+--------------------------------------------------------------+

Practice exercise

  1. স্কুলের reception desk। উপরের Python ReceptionFacade বাড়াও — একটা TransportOffice subsystem যোগ করো (assign_bus_route(student_id)) আর admit_student-এর চার নম্বর ধাপ হিসেবে call করো। bus assignment throw করলে কী roll back হওয়া উচিত সেটা ঠিক করো, আর rollback-এর ক্রম কেন গুরুত্বপূর্ণ সে বিষয়ে একটা বাক্য লেখো।
  2. মুভি নাইট। Lights (dim()), SoundSystem (setVolume(n)), আর Screen (down(), play(film)) তৈরি করো। watchMovie(film) আর endMovie() দিয়ে একটা HomeTheatreFacade লেখো। তারপর একটা বাক্যে উত্তর দাও: অন্য brand-এর remote control যদি তোমার SoundSystem interface-এ fit করাতে হয়, কোন pattern ব্যবহার করবে?
  3. Challenge — god object ভাগ করো। Exercise ১ নাও আর পাঁচটা unrelated method যোগ করো (library, transport, hostel...)। facade কত ভারী হয় অনুভব করো। এখন এটাকে AcademicsFacade আর FacilitiesFacade-এ ভাগ করো আর একটা পাতলা SchoolFrontDesk facade দিয়ে উভয়কে call করো। তুমি এইমাত্র "facades using facades" practice করলে।
  4. কলেজ challenge — edge গুনো। তোমার project (বা যেকোনো পুরনো assignment) dependency graph হিসেবে আঁকো: বাঁ দিকে client, ডান দিকে service, প্রতিটি dependency-তে একটা arrow। arrow গুনো। এখন একটা facade ঢোকাও আর আবার গুনো। নিজের সংখ্যা দিয়ে কলেজ কর্নারের m × n বনাম m + n দাবি verify করো।

সচরাচর জিজ্ঞাসা

Facade pattern সহজ কথায় কী?
Facade হলো অনেকগুলো জটিল class-এর জন্য একটাই সহজ entry point। Client একটা method call করে, facade চুপচাপ সব messy subsystem-কে সঠিক ক্রমে call করে — ঠিক যেমন একজন ট্রাভেল এজেন্ট তোমার হয়ে ট্রেন, হোটেল, আর ট্যাক্সি বুক করে দেয়।
Facade কি subsystem-গুলোকে পুরোপুরি লুকিয়ে ফেলে?
না। subsystem-গুলো এখনো আছে, সরাসরি ব্যবহারও করা যায়। Facade শুধু সাধারণ কাজের জন্য একটা সহজ default পথ দেয়। বিশেষ দরকার হলে যে কেউ সরাসরি subsystem call করতে পারবে।
Facade আর Adapter-এর পার্থক্য কী?
Adapter একটা object-এর interface বদলায় যাতে incompatible code সংযুক্ত হতে পারে। Facade অনেকগুলো object-এর উপর একটা নতুন, সহজ interface তৈরি করে। সহজ কথায়: Adapter convert করে, Facade simplify করে।
Facade কি কখনো খারাপ হতে পারে?
হ্যাঁ। একটা facade-এ একের পর এক সব operation ঢালতে থাকলে সেটা god object হয়ে যায়। সমাধান হলো একাধিক ছোট facade-এ ভাগ করা, প্রতিটি একটা নির্দিষ্ট কাজের দল handle করবে।
বাস্তব software-এ facades কোথায় দেখা যায়?
jQuery-এর সহজ function যেগুলো messy browser API-র উপর বসে, AWS SDK client যেগুলো raw HTTP call লুকায়, Spring-এর JdbcTemplate যেটা low-level JDBC লুকায় — এগুলো সবই facade।

আরো দেখো

সম্পর্কিত পাঠ

Adapter Pattern: চল্লিশ টাকার প্লাগ যা পুরনো আর নতুন কোডকে মিলিয়ে দেয়

একটা সহজ ৩-পিন প্লাগ আর ২-পিন সকেটের গল্পের মাধ্যমে Adapter pattern শিখো। কোনো পক্ষ না বদলেই পুরনো কোডকে নতুন কোডের সাথে কাজ করাও।

আরও পড়ুন

Mediator Pattern: Control Tower যেটা ক্যাওস থামায়

বিমানবন্দরের control tower-এর গল্প দিয়ে Mediator pattern শেখো — সহজ TypeScript আর C# কোড, MediatR উদাহরণ, diagram, table আর practice task সহ।

আরও পড়ুন

Proxy Pattern: দারোয়ান যে মালিকের সাথে দেখা করার আগেই চেক করে

দারোয়ানের গল্প দিয়ে Proxy pattern শেখো। একটা real object-এর সামনে same interface-এর একটা stand-in রেখে সেই object-এ access নিয়ন্ত্রণ করো।

আরও পড়ুন

Abstract Factory Pattern: এক অর্ডার, এক ম্যাচিং থালি

বাংলাদেশি বিয়ের ক্যাটারিং গল্পের মাধ্যমে Abstract Factory design pattern বুঝে নাও — সহজ TypeScript ও Python কোড, diagram, আর practice সহ।

আরও পড়ুন