<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ design patterns - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ design patterns - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 14 Aug 2026 16:25:03 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/design-patterns/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ A Deep Dive into Behavioral Patterns: The Visitor Design Pattern and its Clean Operations Across Complex Object Structures ]]>
                </title>
                <description>
                    <![CDATA[ There's a problem that shows up in almost every growing software system, and most developers don't even realize they're hitting it until the damage is already done. You have a set of objects: differen ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-visitor-design-pattern-and-its-clean-operations-across-complex-object-structures/</link>
                <guid isPermaLink="false">6a74b21fcf90c22a668963b6</guid>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design principles ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mobile ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mobile Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ visitor design pattern ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwaseyi Fatunmole ]]>
                </dc:creator>
                <pubDate>Thu, 06 Aug 2026 16:11:11 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/ff25cbd5-72fc-4f17-8d37-ba8dc909de46.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>There's a problem that shows up in almost every growing software system, and most developers don't even realize they're hitting it until the damage is already done.</p>
<p>You have a set of objects: different types, shapes, and data. And at some point, someone asks you to perform an operation on all of them, like exporting them them to PDF, sending them a notification, generating a report, or calculating their fees.</p>
<p>Your first instinct might be to write a function that checks the type and branches accordingly, like an if-else block or switch statement. Something that says: if this is a NewUser, do this. If this is a JointAccountUser, do that. It works, you ship it, and everyone is happy.</p>
<p>Then another operation comes in. And another. Every single time, you go back to the same place and add another branch. The function grows. The class grows. The test surface grows. What started as a clean model is now a god object that knows how to do everything for everyone.</p>
<p>The Visitor Design Pattern exists to break this cycle completely.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-the-visitor-design-pattern">What is the Visitor Design Pattern?</a></p>
</li>
<li><p><a href="#heading-the-problem-the-visitor-pattern-solves">The Problem the Visitor Pattern Solves</a></p>
</li>
<li><p><a href="#heading-core-components">Core Components</a></p>
</li>
<li><p><a href="#heading-real-world-example-one-document-export">Real World Example One: Document Export</a></p>
</li>
<li><p><a href="#heading-real-world-example-two-notification-system">Real World Example Two: Notification System</a></p>
</li>
<li><p><a href="#heading-real-world-example-three-fee-calculation">Real World Example Three: Fee Calculation</a></p>
</li>
<li><p><a href="#heading-the-power-of-combining-all-three-operations">The Power of Combining All Three Operations</a></p>
</li>
<li><p><a href="#heading-when-to-use-the-visitor-pattern">When to Use the Visitor Pattern</a></p>
</li>
<li><p><a href="#heading-when-not-to-use-it">When Not to Use It</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-visitor-design-pattern">What is the Visitor Design Pattern?</h2>
<p>The Visitor pattern is a behavioral design pattern that lets you define a new operation on a family of objects without changing the objects themselves.</p>
<p>The key word there is behavioral. Behavioral patterns are about how objects communicate and distribute responsibility. Where creational patterns deal with how objects are created and structural patterns deal with how they are composed, behavioral patterns deal with how they interact and who is responsible for what.</p>
<p>The Visitor pattern specifically deals with the question of who should own an operation when that operation needs to work differently across multiple object types.</p>
<p>The classic answer is: put the operation on each object. Give each class a method that handles the operation for its own type. But this breaks down the moment you have multiple operations, because now every new operation means touching every class. You're spreading one concern across your entire object hierarchy.</p>
<p>The Visitor pattern flips this. Instead of spreading the operation across the objects, you collect it into one place called a Visitor. The objects simply accept the visitor and let it do its work. Adding a new operation means creating a new Visitor. The existing objects don't change at all.</p>
<p>This is the Open/Closed Principle working exactly as intended: open for extension, closed for modification.</p>
<h2 id="heading-the-problem-the-visitor-pattern-solves">The Problem the Visitor Pattern Solves</h2>
<p>Let me show you exactly what this looks like without the Visitor pattern.</p>
<p>Say you have a fintech platform with four types of users: existing customers, new customers, minor account holders, and joint account holders. Your product manager comes in and asks you to add document export. Every user type should be exportable to PDF, Excel, and CSV.</p>
<p>Without Visitor, the natural approach looks something like this:</p>
<pre><code class="language-dart">class ExistingUser {
  final int id;
  final String firstName;
  final String lastName;
  final DateTime lastPaymentDate;
  final num accountBalance;

  String exportToPdf() {
    return '$firstName\n$lastName\n$lastPaymentDate\n$accountBalance';
  }

  String exportToExcel() {
    return '$firstName,$lastName,$lastPaymentDate,$accountBalance';
  }

  String exportToCsv() {
    return '"$firstName","$lastName","$lastPaymentDate","$accountBalance"';
  }
}
</code></pre>
<p>And you repeat this for NewUser, MinorAccountUser, and JointAccountUser. Twelve methods spread across four classes just for document export.</p>
<p>Now the product manager comes back. They want notifications: email, SMS, and Push. Back you go to all four classes, adding three more methods each. Twelve more methods spread across the same four classes.</p>
<p>Then they want fee calculation. Then they want KYC status checks. Every new operation multiplies across every user type. The classes grow, the reasons to change multiply, and testing becomes painful.</p>
<p>This is the exact problem the Visitor pattern was built to solve.</p>
<h2 id="heading-core-components">Core Components</h2>
<p>The Visitor pattern has four core components. Understanding each one before looking at code makes the implementation much easier to follow.</p>
<h3 id="heading-the-visitor-interface">The Visitor Interface</h3>
<p>This is the contract that every visitor must implement. It declares one method per object type it needs to visit. A visitor that handles four user types declares four visit methods, one for each type.</p>
<h3 id="heading-the-concrete-visitors">The Concrete Visitors</h3>
<p>These are the real implementations of the Visitor interface. Each one represents a single operation and knows how to handle every object type. A PdfHandler is a concrete visitor. An ExcelHandler is a concrete visitor. A SmsNotificationHandler is a concrete visitor. Each one has one job and knows how to do that job for every user type.</p>
<h3 id="heading-the-consumer-interface-also-called-element-or-acceptor">The Consumer Interface (also called Element or Acceptor)</h3>
<p>This is the contract that every object in the hierarchy must implement. It declares a single accept method that takes a Visitor and calls the right visit method on it. This is the double dispatch mechanism that makes the pattern work.</p>
<h3 id="heading-the-concrete-consumers">The Concrete Consumers</h3>
<p>These are the real objects in the hierarchy: ExistingCustomers, NewCustomers, MinorCustomer, and JointCustomer. Each one implements accept by calling the specific visit method that corresponds to its own type.</p>
<p>Think of it this way. The Visitor interface is implemented by every operation you want to perform: PdfHandler, ExcelHandler, and CsvHandler. Each of these knows how to handle all four user types.</p>
<p>The Consumer interface is implemented by every object in the hierarchy: ExistingCustomers, NewCustomers, MinorCustomer, and JointCustomer. Each of these knows how to receive a visitor and route it to the correct method.</p>
<p>When you call <code>existingCustomer.accept(pdfHandler)</code>, ExistingCustomers calls <code>pdfHandler.visitExistingCustomer(this)</code> and passes itself as the argument. The right method fires automatically. There's no type checking, if-else, or switch. The object tells the visitor who it is, and the visitor knows exactly what to do with that information.</p>
<h2 id="heading-real-world-example-one-document-export">Real World Example One: Document Export</h2>
<p>This is a real scenario from a fintech platform. There are four user types with different data structures, all needing to export their information to three document formats: PDF, Excel, and CSV.</p>
<h3 id="heading-step-1-define-the-user-models">Step 1: Define the User Models</h3>
<pre><code class="language-dart">class ExistingUser {
  final int id;
  final String firstName;
  final String lastName;
  final DateTime lastPaymentDate;
  final num accountBalance;

  const ExistingUser({
    required this.id,
    required this.firstName,
    required this.lastName,
    required this.lastPaymentDate,
    required this.accountBalance,
  });
}

class NewUser {
  final String firstName;
  final String lastName;

  const NewUser({
    required this.firstName,
    required this.lastName,
  });
}

class MinorAccountUser {
  final int age;
  final int guardianId;
  final String firstName;
  final String lastName;
  final String guardianName;

  const MinorAccountUser({
    required this.age,
    required this.guardianId,
    required this.firstName,
    required this.lastName,
    required this.guardianName,
  });
}

class JointAccountUser {
  final int jointAccountId;
  final List&lt;String&gt; accountHoldersInfo;
  final num accountBalance;

  const JointAccountUser({
    required this.jointAccountId,
    required this.accountHoldersInfo,
    required this.accountBalance,
  });
}
</code></pre>
<p>We have four models. Each one owns its own data and nothing else. There's no export logic or notification logic. And no business operations of any kind. Just clean data structures.</p>
<p>This is exactly how it should be. The model's job is to hold data. The visitor's job is to operate on it.</p>
<h3 id="heading-step-2-define-the-visitor-and-consumer-interfaces">Step 2: Define the Visitor and Consumer Interfaces</h3>
<pre><code class="language-dart">abstract class UserVisitor&lt;T&gt; {
  T visitExistingCustomer(ExistingUser user);
  T visitNewCustomer(NewUser user);
  T visitMinorCustomer(MinorAccountUser user);
  T visitJointCustomer(JointAccountUser user);
}

abstract class UserConsumer {
  T accept&lt;T&gt;(UserVisitor&lt;T&gt; visitor);
}
</code></pre>
<p><code>UserVisitor&lt;T&gt;</code> is generic. The type parameter <code>T</code> represents what the visitor returns. A document export visitor returns a String. A fee calculation visitor might return a double. A validation visitor might return a bool. The same pattern works for any return type.</p>
<p><code>UserConsumer</code> declares the accept method. Every object in the hierarchy must implement this. The accept method is what makes the double dispatch work. The object receives the visitor and immediately calls the right visit method on it, passing itself as the argument.</p>
<h3 id="heading-step-3-implement-the-concrete-consumers">Step 3: Implement the Concrete Consumers</h3>
<pre><code class="language-dart">class ExistingCustomers implements UserConsumer {
  final ExistingUser user;
  ExistingCustomers({required this.user});

  @override
  T accept&lt;T&gt;(UserVisitor&lt;T&gt; visitor) {
    return visitor.visitExistingCustomer(user);
  }
}

class NewCustomers implements UserConsumer {
  final NewUser user;
  NewCustomers({required this.user});

  @override
  T accept&lt;T&gt;(UserVisitor&lt;T&gt; visitor) {
    return visitor.visitNewCustomer(user);
  }
}

class MinorCustomer implements UserConsumer {
  final MinorAccountUser user;
  MinorCustomer({required this.user});

  @override
  T accept&lt;T&gt;(UserVisitor&lt;T&gt; visitor) {
    return visitor.visitMinorCustomer(user);
  }
}

class JointCustomer implements UserConsumer {
  final JointAccountUser user;
  JointCustomer({required this.user});

  @override
  T accept&lt;T&gt;(UserVisitor&lt;T&gt; visitor) {
    return visitor.visitJointCustomer(user);
  }
}
</code></pre>
<p>Each consumer wraps one user model and implements accept by forwarding to the correct visit method. This is the entire job of a concrete consumer. It knows who it is, and it tells the visitor by calling the right method.</p>
<p>Notice that none of these classes know anything about PDF, Excel, CSV, email, SMS, or any operation. They're completely decoupled from every operation that will ever be performed on them.</p>
<h3 id="heading-step-4-implement-the-concrete-visitors">Step 4: Implement the Concrete Visitors</h3>
<pre><code class="language-dart">class PdfHandler implements UserVisitor&lt;String&gt; {
  @override
  String visitExistingCustomer(ExistingUser user) {
    return '${user.firstName} ${user.lastName}'
        '\nBalance: ${user.accountBalance}'
        '\nLast Payment: ${user.lastPaymentDate}';
  }

  @override
  String visitNewCustomer(NewUser user) {
    return '${user.firstName} ${user.lastName}';
  }

  @override
  String visitMinorCustomer(MinorAccountUser user) {
    return '${user.firstName} ${user.lastName}'
        '\nAge: ${user.age}'
        '\nGuardian: ${user.guardianName} (ID: ${user.guardianId})';
  }

  @override
  String visitJointCustomer(JointAccountUser user) {
    final holders = user.accountHoldersInfo.join(', ');
    return 'Joint Account ID: ${user.jointAccountId}'
        '\nHolders: $holders'
        '\nBalance: ${user.accountBalance}';
  }
}

class ExcelHandler implements UserVisitor&lt;String&gt; {
  @override
  String visitExistingCustomer(ExistingUser user) {
    return '${user.firstName}\t${user.lastName}'
        '\t${user.accountBalance}\t${user.lastPaymentDate}';
  }

  @override
  String visitNewCustomer(NewUser user) {
    return '${user.firstName}\t${user.lastName}';
  }

  @override
  String visitMinorCustomer(MinorAccountUser user) {
    return '${user.firstName}\t${user.lastName}'
        '\t${user.age}\t${user.guardianName}\t${user.guardianId}';
  }

  @override
  String visitJointCustomer(JointAccountUser user) {
    final holders = user.accountHoldersInfo.join('\t');
    return '${user.jointAccountId}\t$holders\t${user.accountBalance}';
  }
}

class CsvHandler implements UserVisitor&lt;String&gt; {
  @override
  String visitExistingCustomer(ExistingUser user) {
    return '"${user.firstName}","${user.lastName}"'
        ',"${user.accountBalance}","${user.lastPaymentDate}"';
  }

  @override
  String visitNewCustomer(NewUser user) {
    return '"${user.firstName}","${user.lastName}"';
  }

  @override
  String visitMinorCustomer(MinorAccountUser user) {
    return '"${user.firstName}","${user.lastName}"'
        ',"${user.age}","${user.guardianName}","${user.guardianId}"';
  }

  @override
  String visitJointCustomer(JointAccountUser user) {
    final holders = user.accountHoldersInfo.map((h) =&gt; '"$h"').join(',');
    return '"${user.jointAccountId}",$holders,"${user.accountBalance}"';
  }
}
</code></pre>
<p>Each handler implements the visitor interface and knows exactly how to format each user type for its specific document format. PdfHandler uses newlines and labels. ExcelHandler uses tabs. CsvHandler wraps values in quotes and separates with commas.</p>
<p>The formatting logic for each document type lives in exactly one class. If the PDF format changes, you touch only PdfHandler. If the CSV format changes, you touch only CsvHandler. The user models never change.</p>
<h3 id="heading-step-5-use-it">Step 5: Use It</h3>
<pre><code class="language-dart">void existingUserLogic() {
  final customer = ExistingCustomers(
    user: ExistingUser(
      id: 10,
      firstName: 'Oluwaseyi',
      lastName: 'Fatunmole',
      lastPaymentDate: DateTime.now(),
      accountBalance: 7373773.39,
    ),
  );

  final pdf = customer.accept(PdfHandler());
  final excel = customer.accept(ExcelHandler());
  final csv = customer.accept(CsvHandler());

  print('PDF:\n$pdf\n');
  print('Excel:\n$excel\n');
  print('CSV:\n$csv\n');
}

void newUserLogic() {
  final customer = NewCustomers(
    user: NewUser(
      firstName: 'Oluwaseyi',
      lastName: 'Fatunmole',
    ),
  );

  customer.accept(PdfHandler());
  customer.accept(ExcelHandler());
  customer.accept(CsvHandler());
}

void minorUserLogic() {
  final customer = MinorCustomer(
    user: MinorAccountUser(
      age: 15,
      guardianId: 82882,
      firstName: 'Oluwaseyi',
      lastName: 'Fatunmole',
      guardianName: 'Inioluwa',
    ),
  );

  customer.accept(PdfHandler());
  customer.accept(ExcelHandler());
  customer.accept(CsvHandler());
}

void jointUserLogic() {
  final customer = JointCustomer(
    user: JointAccountUser(
      jointAccountId: 92,
      accountHoldersInfo: [
        'Oluwaseyi',
        'Aderonke',
        'Inioluwa',
        'Tiwaloluwa',
      ],
      accountBalance: 9200020202.22,
    ),
  );

  customer.accept(PdfHandler());
  customer.accept(ExcelHandler());
  customer.accept(CsvHandler());
}
</code></pre>
<p>The same customer object accepts any visitor with the same call. The type dispatch happens automatically through the accept method. There's no type checking anywhere in the calling code, and no if-else or switch. Just <code>customer.accept(handler)</code> and the right method fires.</p>
<p>Now think about what happens when you need to add an XML export. You create one new class, XmlHandler, implement the four visit methods, and that's it. You don't touch ExistingUser, NewUser, MinorAccountUser, JointAccountUser, or any of the existing handlers. The system is genuinely open for extension and closed for modification.</p>
<h2 id="heading-real-world-example-two-notification-system">Real World Example Two: Notification System</h2>
<p>Here we have the same four user types and the same pattern. But it's a different operation entirely.</p>
<p>Your platform needs to notify users about account events. But not every user type should be notified the same way.</p>
<p>Existing users get email and push notifications. New users only get email because they haven't fully set up their profile yet. Minor account users get SMS to their guardian's number. Joint account users get notified on all channels because multiple people share the account.</p>
<p>Without the Visitor pattern, this logic would spread across all four user models or collapse into one enormous function full of type checks. With Visitor, it lives in three focused classes.</p>
<h3 id="heading-the-notification-visitor-interface">The Notification Visitor Interface</h3>
<pre><code class="language-dart">abstract class NotificationVisitor {
  void visitExistingCustomer(ExistingUser user);
  void visitNewCustomer(NewUser user);
  void visitMinorCustomer(MinorAccountUser user);
  void visitJointCustomer(JointAccountUser user);
}
</code></pre>
<p>This visitor returns void because notifications are side effects. They send messages, they don't return values.</p>
<h3 id="heading-the-concrete-notification-visitors">The Concrete Notification Visitors</h3>
<pre><code class="language-dart">class EmailNotificationHandler implements NotificationVisitor {
  @override
  void visitExistingCustomer(ExistingUser user) {
    print('Sending email to existing customer: ${user.firstName}');
    // email service call with full account details
  }

  @override
  void visitNewCustomer(NewUser user) {
    print('Sending welcome email to new customer: ${user.firstName}');
    // welcome email with onboarding steps
  }

  @override
  void visitMinorCustomer(MinorAccountUser user) {
    print('Sending email to guardian: ${user.guardianName}');
    // email goes to guardian, not the minor
  }

  @override
  void visitJointCustomer(JointAccountUser user) {
    for (final holder in user.accountHoldersInfo) {
      print('Sending email to joint holder: $holder');
      // all account holders get notified
    }
  }
}

class SmsNotificationHandler implements NotificationVisitor {
  @override
  void visitExistingCustomer(ExistingUser user) {
    print('Sending SMS to existing customer: ${user.firstName}');
  }

  @override
  void visitNewCustomer(NewUser user) {
    // new users are not SMS-verified yet, skip
    print('New customer ${user.firstName} not SMS-eligible yet');
  }

  @override
  void visitMinorCustomer(MinorAccountUser user) {
    print('Sending SMS to guardian ${user.guardianName} for minor ${user.firstName}');
    // SMS goes to guardian's registered number
  }

  @override
  void visitJointCustomer(JointAccountUser user) {
    for (final holder in user.accountHoldersInfo) {
      print('Sending SMS to joint holder: $holder');
    }
  }
}

class PushNotificationHandler implements NotificationVisitor {
  @override
  void visitExistingCustomer(ExistingUser user) {
    print('Push notification to existing customer: ${user.firstName}');
  }

  @override
  void visitNewCustomer(NewUser user) {
    print('Push notification to new customer: ${user.firstName}');
  }

  @override
  void visitMinorCustomer(MinorAccountUser user) {
    // minors do not have the app installed yet, guardian gets push
    print('Push notification to guardian: ${user.guardianName}');
  }

  @override
  void visitJointCustomer(JointAccountUser user) {
    for (final holder in user.accountHoldersInfo) {
      print('Push notification to joint holder: $holder');
    }
  }
}
</code></pre>
<p>Each handler knows the specific rules for each user type. <code>SmsNotificationHandler</code> knows that new users aren't SMS-verified yet. <code>PushNotificationHandler</code> knows that minor account notifications go to the guardian. <code>EmailNotificationHandler</code> knows that joint account holders all need to be notified individually.</p>
<p>This business logic lives in exactly one place per notification channel. When the rules change (and they always change), you update one class.</p>
<h3 id="heading-using-the-notification-visitors">Using the Notification Visitors</h3>
<pre><code class="language-dart">void notifyExistingUser() {
  final customer = ExistingCustomers(
    user: ExistingUser(
      id: 10,
      firstName: 'Oluwaseyi',
      lastName: 'Fatunmole',
      lastPaymentDate: DateTime.now(),
      accountBalance: 7373773.39,
    ),
  );

  customer.accept(EmailNotificationHandler());
  customer.accept(SmsNotificationHandler());
  customer.accept(PushNotificationHandler());
}

void notifyMinorUser() {
  final customer = MinorCustomer(
    user: MinorAccountUser(
      age: 15,
      guardianId: 82882,
      firstName: 'Oluwaseyi',
      lastName: 'Fatunmole',
      guardianName: 'Inioluwa',
    ),
  );

  // all three channels fire, each with minor-specific rules
  customer.accept(EmailNotificationHandler());
  customer.accept(SmsNotificationHandler());
  customer.accept(PushNotificationHandler());
}

void notifyJointUser() {
  final customer = JointCustomer(
    user: JointAccountUser(
      jointAccountId: 92,
      accountHoldersInfo: [
        'Oluwaseyi',
        'Aderonke',
        'Inioluwa',
        'Tiwaloluwa',
      ],
      accountBalance: 9200020202.22,
    ),
  );

  customer.accept(EmailNotificationHandler());
  customer.accept(SmsNotificationHandler());
  customer.accept(PushNotificationHandler());
}
</code></pre>
<p>The calling code is identical regardless of the user type or the notification channel. The dispatch is automatic. The rules live inside the visitors.</p>
<p>When WhatsApp notifications become a requirement (and they will), you create one <code>WhatsAppNotificationHandler</code> class with four visit methods. Nothing else changes.</p>
<h2 id="heading-real-world-example-three-fee-calculation">Real World Example Three: Fee Calculation</h2>
<p>Again, we have the same four user types and the same pattern. And once again, we have a completely different operation.</p>
<p>Your platform needs to calculate monthly maintenance fees. But each user type has different rules.</p>
<p>Existing customers pay a flat monthly fee based on their account balance. New customers are fee-exempt for their first three months. Minor account holders pay a reduced fee because their accounts have restricted features. Joint account holders have their fee split equally across all account holders.</p>
<p>Without Visitor, this logic ends up as a giant method somewhere with four branches, or worse, it leaks into the user models themselves. With Visitor, it lives in one focused class.</p>
<h3 id="heading-the-fee-visitor-interface">The Fee Visitor Interface</h3>
<pre><code class="language-dart">abstract class FeeVisitor {
  double visitExistingCustomer(ExistingUser user);
  double visitNewCustomer(NewUser user);
  double visitMinorCustomer(MinorAccountUser user);
  double visitJointCustomer(JointAccountUser user);
}
</code></pre>
<p>This visitor returns a double because fee calculation produces a numeric value.</p>
<h3 id="heading-the-concrete-fee-visitor">The Concrete Fee Visitor</h3>
<pre><code class="language-dart">class MonthlyFeeCalculator implements FeeVisitor {
  @override
  double visitExistingCustomer(ExistingUser user) {
    // 0.5% of account balance, minimum 500, maximum 5000
    final fee = user.accountBalance * 0.005;
    return fee.clamp(500, 5000).toDouble();
  }

  @override
  double visitNewCustomer(NewUser user) {
    // new customers are fee-exempt for the first 3 months
    return 0.0;
  }

  @override
  double visitMinorCustomer(MinorAccountUser user) {
    // flat reduced fee for minor accounts
    return 150.0;
  }

  @override
  double visitJointCustomer(JointAccountUser user) {
    // standard fee split equally across all holders
    const standardFee = 2000.0;
    return standardFee / user.accountHoldersInfo.length;
  }
}
</code></pre>
<p>Every fee rule for every user type lives in this one class. When the fee structure changes for existing customers, you touch one method in one class. When minor account fees are updated, same thing. None of the user models change, and no other visitor changes.</p>
<h3 id="heading-using-the-fee-visitor">Using the Fee Visitor</h3>
<pre><code class="language-dart">void calculateFees() {
  final existingCustomer = ExistingCustomers(
    user: ExistingUser(
      id: 10,
      firstName: 'Oluwaseyi',
      lastName: 'Fatunmole',
      lastPaymentDate: DateTime.now(),
      accountBalance: 7373773.39,
    ),
  );

  final newCustomer = NewCustomers(
    user: NewUser(
      firstName: 'Aderonke',
      lastName: 'Fatunmole',
    ),
  );

  final minorCustomer = MinorCustomer(
    user: MinorAccountUser(
      age: 15,
      guardianId: 82882,
      firstName: 'Inioluwa',
      lastName: 'Fatunmole',
      guardianName: 'Oluwaseyi',
    ),
  );

  final jointCustomer = JointCustomer(
    user: JointAccountUser(
      jointAccountId: 92,
      accountHoldersInfo: [
        'Oluwaseyi',
        'Aderonke',
        'Inioluwa',
        'Tiwaloluwa',
      ],
      accountBalance: 9200020202.22,
    ),
  );

  final calculator = MonthlyFeeCalculator();

  final existingFee = existingCustomer.accept(calculator);
  final newFee = newCustomer.accept(calculator);
  final minorFee = minorCustomer.accept(calculator);
  final jointFee = jointCustomer.accept(calculator);

  print('Existing customer fee: NGN $existingFee');
  print('New customer fee: NGN $newFee');
  print('Minor account fee: NGN $minorFee');
  print('Joint account fee per holder: NGN $jointFee');
}
</code></pre>
<p>The output:</p>
<pre><code class="language-plaintext">Existing customer fee: NGN 5000.0
New customer fee: NGN 0.0
Minor account fee: NGN 150.0
Joint account fee per holder: NGN 500.0
</code></pre>
<p>When a <code>PremiumFeeCalculator</code> is needed for a new tier of customers, you create one new class that implements <code>FeeVisitor</code>. The user models stay exactly as they are. The <code>MonthlyFeeCalculator</code> stays exactly as it is. The accept methods on all four consumers stay exactly as they are.</p>
<h2 id="heading-the-power-of-combining-all-three-operations">The Power of Combining All Three Operations</h2>
<p>Here's what makes the Visitor pattern truly shine in a system like this. You have the same four user types, and you can run any combination of visitors on any of them in the same call chain.</p>
<pre><code class="language-dart">void processUser(UserConsumer customer) {
  final pdf = customer.accept(PdfHandler());
  final csv = customer.accept(CsvHandler());

  customer.accept(EmailNotificationHandler());
  customer.accept(PushNotificationHandler());

  final fee = customer.accept(MonthlyFeeCalculator());

  print('Fee: NGN $fee');
  print('Documents generated and notifications sent');
}
</code></pre>
<p>One function, any user type, any combination of operations. The consumer doesn't care which visitors it receives. The visitors don't care which consumers call them. They speak to each other through the interface, and the interface guarantees everything works correctly.</p>
<p>We have three completely different operations (document export, notifications, and fee calculation) all applied to the same object with the same call pattern. None of these operations know about each other. None of them touch the user models. Each one lives in its own focused class with its own single reason to change.</p>
<h2 id="heading-when-to-use-the-visitor-pattern">When to Use the Visitor Pattern</h2>
<p>Use Visitor when you have a stable set of object types and a growing set of operations on them.</p>
<p>The pattern shines when the object hierarchy is unlikely to change frequently. It's optimized for adding new operations, not new types. Adding a new user type means updating every existing visitor. If your object types change constantly, Visitor creates more work than it saves.</p>
<p>It's also very effective when you need to perform multiple unrelated operations on a family of objects without polluting their classes with that logic. Document export, notification handling, fee calculation, and KYC validation are all unrelated operations. Each belongs in its own visitor, not scattered across the user models.</p>
<p>Visitor also works well when you want clean separation between data and behavior. The models hold data and the visitors define behavior. This makes both easier to understand, easier to test, and easier to maintain independently.</p>
<h2 id="heading-when-not-to-use-it">When Not to Use It</h2>
<p>Avoid Visitor when the object hierarchy changes frequently. Every time you add a new type, you must update every existing visitor. In a system where new user types appear regularly, this becomes painful quickly.</p>
<p>It's also not helpful when you only have one or two operations. For simple cases, the overhead of creating visitor interfaces, consumer interfaces, and multiple classes is not worth the benefit.</p>
<p>And avoid it when the operations are tightly coupled to the object's internal state in ways that make sense to keep together. Some behavior naturally belongs on the object itself.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The Visitor Design Pattern solves a problem that most developers only recognize after they've already made a mess of it. You have a family of objects with different types and different data. Operations come in one after another. Without a deliberate structure, those operations spread everywhere: into the models, utility classes, and massive switch statements that nobody wants to touch.</p>
<p>Visitor collects each operation into one focused class. The models stay clean and the operations stay isolated. Adding a new operation means creating one new class. The existing code doesn't change.</p>
<p>In the fintech examples above, we have three entirely different concerns: document export, notifications, and fee calculation. All are handled by handled by focused classes, none of which know anything about each other. The user models don't know about PDF or email or fees. The PdfHandler doesn't know about SMS. The MonthlyFeeCalculator doesn't know about push notifications. Each class has exactly one reason to exist and exactly one reason to change.</p>
<p>That s what a well-applied Visitor pattern looks like in practice. Clean, focused, and genuinely extensible.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Fix the Dual-Write Problem in Node.js with the Outbox Pattern ]]>
                </title>
                <description>
                    <![CDATA[ Imagine you're building an e-commerce platform where placing an order needs to trigger several things at once: the warehouse has to be told to prepare the shipment, the email service has to send a con ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-fix-the-dual-write-problem-in-node-js-with-the-outbox-pattern/</link>
                <guid isPermaLink="false">6a736f87fcec1e65edd2a703</guid>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gabor Koos ]]>
                </dc:creator>
                <pubDate>Wed, 05 Aug 2026 17:14:47 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/bcec9aaf-d418-4e5a-b8aa-f3c75b35f482.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Imagine you're building an e-commerce platform where placing an order needs to trigger several things at once: the warehouse has to be told to prepare the shipment, the email service has to send a confirmation, and the fraud checker has to review the transaction.</p>
<p>The order service handles the checkout, saves the order to its database, and then publishes an <code>order.created</code> event to a message queue so every downstream system can react independently.</p>
<p>This is a common and reasonable design, but it has a reliability problem that's easy to miss until something goes wrong in production.</p>
<p>When a customer places an order and the payment goes through, the application needs to do two things: save the order to the database and publish the event to the queue. These are two separate writes to two separate systems, and there's no way to make them share a single atomic transaction. If the process crashes, the network hiccups, or a deployment rolls out between the two writes, one side commits and the other does not. The order sits confirmed on the customer's screen while the warehouse has no idea it exists.</p>
<p>The <a href="https://microservices.io/patterns/data/transactional-outbox.html">transactional outbox pattern</a> is the standard solution to this problem. In this article, we'll build it from scratch in Node.js, using PostgreSQL for the order service database, SQS for the queue, and DynamoDB as the fulfillment service's database. For local development, we'll use <a href="https://floci.io">floci</a>, a free open-source AWS emulator that runs all three with a single Docker container.</p>
<h2 id="heading-what-well-cover">What We'll Cover</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-the-problem-with-two-writes">The Problem with Two Writes</a></p>
</li>
<li><p><a href="#heading-the-outbox-pattern">The Outbox Pattern</a></p>
</li>
<li><p><a href="#heading-what-well-build">What We'll Build</a></p>
</li>
<li><p><a href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a href="#heading-database-schema">Database Schema</a></p>
</li>
<li><p><a href="#heading-the-request-handler">The Request Handler</a></p>
</li>
<li><p><a href="#heading-the-relay-worker">The Relay Worker</a></p>
</li>
<li><p><a href="#heading-the-consumer">The Consumer</a></p>
</li>
<li><p><a href="#heading-running-the-whole-thing">Running the Whole Thing</a></p>
</li>
<li><p><a href="#heading-going-to-production">Going to Production</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along, you should be comfortable with:</p>
<ul>
<li><p>Node.js and async/await</p>
</li>
<li><p>Database transactions (BEGIN, COMMIT, ROLLBACK)</p>
</li>
<li><p>The general concept of a message queue</p>
</li>
</ul>
<p>You don't need prior experience with AWS, SQS, or DynamoDB. We'll be running everything locally.</p>
<p>You will need Node.js 20 or later and Docker installed on your machine.</p>
<h2 id="heading-the-problem-with-two-writes">The Problem with Two Writes</h2>
<p>The order service scenario from the intro is one place this problem appears, but the same pattern comes up in many other contexts.</p>
<p>A user registers and the app inserts their account record, then sends a message to trigger the welcome email and the onboarding workflow. A file is uploaded and the API writes the metadata to the database, then publishes a message to kick off a processing worker for virus scanning or thumbnail generation. A payment webhook arrives, the handler records it in the database, then notifies downstream services that the payment is confirmed.</p>
<p>In every case, the application needs two writes to succeed together: one to the database and one to a queue or external system. If the second one is lost, the first one has no way of knowing.</p>
<p>If you want a deeper look at what database transactions actually guarantee and where they stop helping, see <a href="https://blog.gaborkoos.com/posts/2026-08-01-Beyond-Happy-Path-Engineering-Databases/">Beyond Happy Path Engineering: Databases</a>.</p>
<p>The naïve implementation looks straightforward:</p>
<pre><code class="language-js">await db.query('INSERT INTO orders (customer_id, amount_cents) VALUES ($1, $2)', [customerId, amountCents]);
await sqs.send(new SendMessageCommand({ QueueUrl: QUEUE_URL, MessageBody: JSON.stringify({ customerId, amountCents }) }));
</code></pre>
<p>The database write happens first, then the queue write. Under normal conditions this works fine. The problem is what happens when something goes wrong between the two.</p>
<p>If the process crashes, runs out of memory, or gets killed mid-deployment after the database write but before <code>sqs.send</code> is called, the order record exists in the database but no event is ever published. The warehouse, email service, and fraud checker never find out the order happened. From the customer's perspective the order went through. From every downstream system's perspective it doesn't exist.</p>
<p>The failure can also go the other way. If <code>sqs.send</code> succeeds but the database write is later rolled back due to a constraint violation or an error in a subsequent step, you've published an event for an order that doesn't actually exist. A consumer acting on that event may try to fulfill an order with no corresponding record, or charge a customer for something that was never saved.</p>
<p>There's also a timing window even when both writes eventually succeed. Between the database commit and the successful <code>sqs.send</code>, a consumer that queries the database after receiving the event may not find the order yet, depending on transaction isolation and replication lag. These are two separate systems with no shared transaction boundary, and no amount of careful sequencing fully closes the gap.</p>
<p>These aren't edge cases that only happen under extraordinary circumstances. Deploys restart processes mid-request. Out-of-memory kills happen without warning. Networks drop connections at any point. Any of these can interrupt the two-write sequence, and the result is a system that's silently inconsistent with no error logged and no alert fired.</p>
<p>A variation I've seen a few times that looks safer but is actually worse is wrapping both operations in a database transaction:</p>
<pre><code class="language-js">// PLEASE DO NOT EVER DO THIS
const client = await pool.connect();
await client.query('BEGIN');
await client.query('INSERT INTO orders (customer_id, amount_cents) VALUES ($1, $2)', [customerId, amountCents]);
await sqs.send(new SendMessageCommand({ QueueUrl: QUEUE_URL, MessageBody: JSON.stringify({ customerId, amountCents }) }));
await client.query('COMMIT');
</code></pre>
<p>The intent is to make the two writes feel like a unit, but a database transaction has no authority over SQS. The transaction can only roll back database operations. If <code>sqs.send</code> succeeds and then <code>COMMIT</code> fails, the message is already in the queue and can't be taken back. If the process crashes after <code>COMMIT</code> but before the function returns, the transaction committed and the message was sent, but the caller may retry, potentially inserting a duplicate order.</p>
<p>Beyond the correctness problems, this pattern holds an open database connection and any row locks for the entire duration of the SQS network call. SQS is normally fast, but under load, retries, or a degraded queue, that call can take seconds. Every other request trying to read or write the same rows has to wait. In a busy application, this is a reliable way to exhaust the connection pool and bring down unrelated parts of the service.</p>
<h2 id="heading-the-outbox-pattern">The Outbox Pattern</h2>
<p>The core idea is to stop treating the queue publish as a second write that happens after the database write, and instead make it part of the same database transaction.</p>
<p>Rather than calling <code>sqs.send</code> directly, the application inserts a row into an <code>outbox</code> table in the same transaction as the business record. A separate relay process reads the outbox table and publishes the messages to SQS. On the other end, a consumer receives the messages and writes to its own data store. In our case that is a fulfillment service writing to DynamoDB, completely separate from the order service's PostgreSQL database.</p>
<p>If the transaction rolls back for any reason, the outbox row disappears with it. There's no orphaned message in the queue because the message was never sent. If the application crashes after committing but before the relay runs, the outbox row is still there with <code>status='pending'</code>, and the relay will pick it up on its next iteration.</p>
<p>The only guarantee the pattern relies on is the one the database already provides: atomicity within a single transaction.</p>
<p>The relay worker is responsible for the eventual delivery guarantee. It runs on an interval, selects pending rows, publishes them to SQS, and marks them as sent only after SQS confirms receipt. If the relay crashes mid-run, it will reprocess the same rows on the next iteration, which means SQS may receive some messages more than once.</p>
<p>That's why the consumer needs to be <strong>idempotent</strong>: it must handle receiving the same message twice without creating duplicate fulfillment records. We'll cover how to implement that when we build the consumer.</p>
<p>This separation of concerns is what makes the pattern practical. The request handler commits one atomic database transaction and returns. The relay handles the network call to SQS asynchronously, at its own pace, with its own retry logic, without holding database connections open or blocking request handling. The consumer is fully decoupled from the order service and owns its own data store.</p>
<p>The diagram below illustrates the flow:</p>
<img src="https://cdn.hashnode.com/uploads/covers/68b08746916c71e1ed2db58e/ab0620f0-65c6-43f1-a406-00bfd4880cdc.svg" alt="Diagram: outbox pattern flow" style="display:block;margin:0 auto" width="960" height="640" loading="lazy">

<h2 id="heading-what-well-build">What We'll Build</h2>
<p>Now let's see the whole thing in practice. We'll implement a simple order placement API. When a customer sends a request to place an order, the order service saves it to PostgreSQL and inserts a row into the outbox table, all in one atomic transaction. A relay worker wakes up periodically, reads the pending outbox rows, and publishes each one as a message to SQS. A separate fulfillment service receives those messages from the queue and creates fulfillment records in DynamoDB.</p>
<p>By the end, you'll have an HTTP endpoint you can call, and you'll be able to verify that placing an order triggers the creation of a fulfillment record in a completely separate database, owned by a completely separate service, without either service ever talking to the other directly.</p>
<p>You can find the complete working code at <a href="https://github.com/gkoos/article-outbox">github.com/gkoos/article-outbox</a>.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Before you can run any code, you need to get floci running so you have local instances of PostgreSQL, SQS, and DynamoDB. You'll also need Node.js 20 or later and Docker installed.</p>
<p>Start by cloning the repository and installing dependencies:</p>
<pre><code class="language-bash">git clone https://github.com/gkoos/article-outbox
cd article-outbox
npm install
</code></pre>
<p>Next, start floci. This command pulls the latest floci image and starts a Docker container that exposes a local AWS API endpoint (make sure Docker is running):</p>
<pre><code class="language-bash">npm run floci:start
</code></pre>
<p>On Linux and macOS, this just works. On Windows with Docker Desktop, <strong>you need to edit the</strong> <code>floci:start</code> <strong>script in your</strong> <code>package.json</code> <strong>to change the Docker socket mount from</strong> <code>/var/run/docker.sock</code> <strong>to</strong> <code>//var/run/docker.sock</code>.</p>
<p>The floci container is now listening on port 4566 and can spin up RDS (PostgreSQL), SQS, and DynamoDB instances on demand.</p>
<p>Now provision the AWS resources with a single setup command:</p>
<pre><code class="language-bash">npm run setup
</code></pre>
<p>This script creates an RDS PostgreSQL database instance, an SQS queue named <code>orders</code>, and a DynamoDB table named <code>fulfillments</code>. It waits for RDS to become available and then writes a <code>.env</code> file with the correct connection details. The environment variables <code>PG_PORT</code>, <code>SQS_QUEUE_URL</code>, and <code>DYNAMODB_TABLE_NAME</code> now point to the local emulated services.</p>
<p>Finally, create the PostgreSQL tables:</p>
<pre><code class="language-bash">npm run migrate
</code></pre>
<p>This creates the <code>orders</code> table and the <code>outbox</code> table in PostgreSQL. You now have a fully functional local environment ready to build against.</p>
<h2 id="heading-database-schema">Database Schema</h2>
<p>The two tables are simple. <code>orders</code> holds the business records: each order has a customer ID, an amount in cents, and a timestamp. The <code>outbox</code> table is the heart of the pattern: it's where the application writes the event that needs to be published.</p>
<pre><code class="language-sql">CREATE TABLE orders (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  customer_id TEXT NOT NULL,
  amount_cents INTEGER NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE outbox (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  event_type TEXT NOT NULL,
  payload JSONB NOT NULL,
  status TEXT NOT NULL DEFAULT 'pending',
  created_at TIMESTAMPTZ DEFAULT now(),
  sent_at TIMESTAMPTZ
);

CREATE INDEX ON outbox (status, created_at) WHERE status = 'pending';
</code></pre>
<p>The <code>orders</code> table needs nothing special. The <code>outbox</code> table stores the event metadata: what type of event it is (<code>event_type</code>), what data it contains (<code>payload</code> as JSON), and whether it has been sent yet (<code>status</code>).</p>
<p>The status starts as <code>pending</code>. When the relay publishes it to SQS, it will mark it as <code>sent</code> and record the timestamp. The index on <code>(status, created_at) WHERE status = 'pending'</code> lets the relay quickly find the next batch of unsent events without scanning the entire table.</p>
<h2 id="heading-the-request-handler">The Request Handler</h2>
<p>This is where the pattern starts. The request handler receives an HTTP POST, inserts an order into the database, inserts a corresponding row into the outbox table, and commits everything in a single atomic transaction. The key insight is that neither write succeeds unless both succeed.</p>
<pre><code class="language-js">const client = await pool.connect();
try {
  await client.query('BEGIN');

  // Insert the order record
  const { rows } = await client.query(
    'INSERT INTO orders (customer_id, amount_cents) VALUES ($1, $2) RETURNING *',
    [customerId, amountCents]
  );
  const order = rows[0];

  // Insert the outbox record in the same transaction
  await client.query(
    `INSERT INTO outbox (event_type, payload)
     VALUES ($1, $2)`,
    ['order.created', JSON.stringify({ orderId: order.id, customerId: order.customer_id, amountCents: order.amount_cents, createdAt: order.created_at })],
  );

  await client.query('COMMIT');
  res.status(201).json(order);
} catch (err) {
  await client.query('ROLLBACK');
  next(err);
} finally {
  client.release();
}
</code></pre>
<p>The handler gets <code>customerId</code> and <code>amountCents</code> from the request body, starts an explicit transaction with <code>BEGIN</code>, and inserts the order. Then it inserts an outbox row with the order data as the payload.</p>
<p>Everything commits atomically. If anything fails, everything rolls back and the client gets an error. If the process crashes between the commit and the response, the client won't get a 201, but the order and the outbox row are still safely committed to the database and the relay will eventually pick it up. The handler doesn't call SQS at all. That is the relay's job.</p>
<h2 id="heading-the-relay-worker">The Relay Worker</h2>
<p>The relay worker is a separate process that polls the outbox table every second and publishes pending rows to SQS. It runs independently of the HTTP server and has no shared state with it.</p>
<pre><code class="language-js">async function relay() {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');

    const { rows } = await client.query(`
      SELECT *
      FROM outbox
      WHERE status = 'pending'
      ORDER BY created_at
      LIMIT 10
      FOR UPDATE SKIP LOCKED -- prevents multiple relays from processing the same rows
    `);

    for (const row of rows) {
      await sqsClient.send(new SendMessageCommand({
        QueueUrl: QUEUE_URL,
        MessageBody: JSON.stringify(row.payload),
        MessageAttributes: {
          EventType: { DataType: 'String', StringValue: row.event_type },
        },
      }));

      await client.query(
        `UPDATE outbox SET status = 'sent', sent_at = now() WHERE id = $1`,
        [row.id],
      );
    }

    await client.query('COMMIT');
  } catch (err) {
    await client.query('ROLLBACK');
    console.error('Relay error:', err.message);
  } finally {
    client.release();
  }
}

setInterval(relay, 1000);
</code></pre>
<p><code>FOR UPDATE SKIP LOCKED</code> is the key to running multiple relay instances safely: when a relay picks up a batch of rows, it locks them. Any other relay instance trying to select the same rows will skip them and move to the next available ones, so you never get two relays publishing the same message from the same run.</p>
<p>The relay marks each row as <code>sent</code> only after <code>sqsClient.send</code> returns. If the relay crashes after sending to SQS but before updating the row, the row stays <code>pending</code> and the relay will resend it on the next iteration.</p>
<p>Note that the <code>UPDATE</code> happens inside the same transaction as the <code>SELECT FOR UPDATE</code>, so if the relay crashes mid-batch, the entire batch rolls back and all rows in it will be retried, including any that were already successfully sent to SQS.</p>
<p>The at-least-once delivery guarantee applies at the batch level, not the individual row level. You can read about this problem in <a href="https://blog.gaborkoos.com/posts/2026-07-01-Beyond-Happy-Path-Engineering-the-Network/">Beyond Happy Path Engineering: the Network</a>: when a response is lost, the caller can't know whether the operation succeeded, so it retries, and the receiver may see the same request twice. This means the consumer may see the same message more than once, which is why idempotency matters on the consumer side.</p>
<h2 id="heading-the-consumer">The Consumer</h2>
<p>The consumer is a completely separate service. It knows nothing about the order service's PostgreSQL database. Its only input is the SQS queue, and its only output is the DynamoDB <code>fulfillments</code> table. This is the point of the pattern: the two services are decoupled by the queue, and each owns its own data store.</p>
<p>As we saw earlier, because SQS delivers at least once (meaning a message might be delivered more than once), the consumer must be idempotent. The <code>PutItem</code> call uses a <code>ConditionExpression</code> that makes the write a no-op if a fulfillment record for that order already exists, so redelivered messages are handled safely.</p>
<pre><code class="language-js">async function consume() {
  const { Messages } = await sqsClient.send(new ReceiveMessageCommand({
    QueueUrl:              QUEUE_URL,
    WaitTimeSeconds:       20,   // long-poll: wait up to 20s for messages
    MaxNumberOfMessages:   10,
    MessageAttributeNames: ['All'],
  }));

  for (const msg of Messages ?? []) {
    const event = JSON.parse(msg.Body);

    try {
      await dynamoClient.send(new PutItemCommand({
        TableName: 'fulfillments',
        Item: {
          orderId:     { S: event.orderId },
          customerId:  { S: event.customerId },
          amountCents: { N: String(event.amountCents) },
          status:      { S: 'received' },
          createdAt:   { S: new Date().toISOString() },
        },
        ConditionExpression: 'attribute_not_exists(orderId)', // idempotency check
      }));
    } catch (err) {
      if (err.name !== 'ConditionalCheckFailedException') throw err;
      // already processed, safe to continue
    }

    // delete the message only after the write succeeds (or was already done)
    await sqsClient.send(new DeleteMessageCommand({
      QueueUrl:      QUEUE_URL,
      ReceiptHandle: msg.ReceiptHandle,
    }));
  }
}
</code></pre>
<p><code>ConditionExpression: 'attribute_not_exists(orderId)'</code> tells DynamoDB to reject the write if a record with that <code>orderId</code> already exists. When that happens, DynamoDB throws a <code>ConditionalCheckFailedException</code>. The consumer catches that specific error and ignores it, then deletes the message from the queue and moves on. Any other error is rethrown and the message stays in the queue to be retried.</p>
<p>The <code>DeleteMessage</code> call happens after the DynamoDB write, not before. If the process crashes between the write and the delete, SQS will redeliver the message and the condition check will handle it. If the process crashes before the write, the message stays in the queue and will be processed normally on the next delivery.</p>
<h2 id="heading-running-the-whole-thing">Running the Whole Thing</h2>
<p>With floci running and the resources provisioned, open three terminal tabs and start each process:</p>
<pre><code class="language-bash">node src/server.js    # the order API on port 3000
node src/relay.js     # the outbox relay
node src/consumer.js  # the fulfillment consumer
</code></pre>
<p>Now place an order:</p>
<pre><code class="language-bash">curl -X POST localhost:3000/orders \
  -H 'Content-Type: application/json' \
  -d '{"customerId":"c1","amountCents":4999}'
</code></pre>
<p>You should get back a 201 with the new order record:</p>
<pre><code class="language-bash">{"id":"1768d35b-083d-45f1-adb5-4063d8d7fcab","customer_id":"c1","amount_cents":4999,"created_at":"2026-07-30T20:27:10.628Z"}
</code></pre>
<p>Within a second the relay will pick up the outbox row and publish it to SQS. The consumer will receive the message and write a fulfillment record to DynamoDB. The repo includes a convenience script to verify this:</p>
<pre><code class="language-bash">npm run check
</code></pre>
<p>You should see a fulfillment record with the <code>orderId</code> from the order you just placed:</p>
<pre><code class="language-bash">{
  orderId: 'c335640e-bc4a-47e4-afed-484c95fbd6d3',
  customerId: 'c1',
  amountCents: '4999',
  status: 'received',
  createdAt: '2026-07-30T19:02:54.929Z'
}
</code></pre>
<h2 id="heading-going-to-production">Going to Production</h2>
<p>Because the local setup uses floci to emulate AWS, switching to real AWS requires no code changes at all. The AWS SDK reads the endpoint from <code>AWS_ENDPOINT_URL</code> in the environment. In production, you simply don't set that variable and the SDK talks to real AWS using the credentials and region from the standard environment variables (<code>AWS_REGION</code>, <code>AWS_ACCESS_KEY_ID</code>, <code>AWS_SECRET_ACCESS_KEY</code>, or an IAM role if you are running on EC2 or ECS).</p>
<p>Running multiple relay instances is safe out of the box because of <code>FOR UPDATE SKIP LOCKED</code>. You can scale the relay horizontally and each instance will pick up a different set of rows without duplicating messages.</p>
<p>One thing worth adding before going to production is handling permanent failures in the relay. Right now the relay only uses <code>pending</code> and <code>sent</code>. You should add a <code>failed</code> status and a retry counter: after a row has failed N times, mark it <code>failed</code> and stop retrying it. Then configure a dead-letter queue on the <code>orders</code> SQS queue as well, so that messages the consumer can't process after the maximum number of retries land somewhere you can inspect rather than disappearing silently.</p>
<p>For high-throughput systems where polling latency matters, <a href="https://en.wikipedia.org/wiki/Change_data_capture">change data capture</a> (CDC) is a common alternative to the polling relay. Tools like <a href="https://debezium.io/">Debezium</a> read directly from the PostgreSQL write-ahead log and publish changes to <a href="https://kafka.apache.org/">Kafka</a> or SQS without any polling delay. The outbox table and the consumer stay exactly the same, only the relay is replaced.</p>
<p>This is a bigger operational commitment than a polling worker, so polling is the right starting point for most systems.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The dual-write problem is easy to overlook because the naïve implementation works correctly most of the time. It only fails in the gaps between two separate system writes, and those gaps only become visible when something goes wrong at exactly the wrong moment. By the time you notice it in production, data is already inconsistent and there is no clean way to recover.</p>
<p>The transactional outbox pattern closes that gap at the database level. The outbox row is part of the same atomic commit as the business record, so the two are always in sync. The relay handles the network call to SQS independently, with its own retry logic, without touching the request lifecycle. The consumer handles at-least-once delivery with a single condition check on the write.</p>
<p>Each piece is simple on its own, and together they give you reliable, decoupled event delivery without distributed transactions.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Observer Design Pattern Handbook: Event-Driven Architecture & Domain-Driven Design in Dart ]]>
                </title>
                <description>
                    <![CDATA[ Every application, at some point, has to deal with a fundamental challenge: something happens, and several other things need to react to it. A user logs in, and the app needs to save a token, cache th ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-observer-design-pattern-handbook-event-driven-architecture-domain-driven-design-in-dart/</link>
                <guid isPermaLink="false">6a59593c2c971321745e7720</guid>
                
                    <category>
                        <![CDATA[ #Domain-Driven-Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mobile Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Observer Pattern ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ behavioural patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Riverpod ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Clean Architecture ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwaseyi Fatunmole ]]>
                </dc:creator>
                <pubDate>Thu, 16 Jul 2026 22:20:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/0621293d-e82e-4f24-bb6e-40dec481c7cd.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Every application, at some point, has to deal with a fundamental challenge: something happens, and several other things need to react to it.</p>
<p>A user logs in, and the app needs to save a token, cache the user profile, fire an analytics event, and navigate to the home screen.</p>
<p>A payment is confirmed, and the inventory needs to update, the user needs a receipt, and the fulfillment system needs to kick off delivery.</p>
<p>A sensor reading changes, and three different UI panels need to reflect the new value simultaneously.</p>
<p>The naïve solution is to write all of that logic in one place. One function that does everything or one class that knows about everything.</p>
<p>This works at first. Then requirements change. A new reaction needs to be added. An existing one needs to be removed. A side effect starts failing and takes everything else down with it. The code becomes a wall of responsibilities that's impossible to test, painful to extend, and dangerous to touch.</p>
<p>The Observer Design Pattern exists to solve exactly this problem. It gives you a structured, production-grade way to say: when this event happens, notify everyone who cares, without the event source knowing who those people are.</p>
<p>In this handbook, you'll learn the Observer pattern from first principles. You'll see how it's implemented in Dart, understand how it connects to Event-Driven Architecture, and discover how it integrates cleanly with Domain-Driven Design and Riverpod in a real Flutter application.</p>
<p>By the end, you won't just understand the pattern. You'll know how to use it deliberately in production code.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-the-observer-design-pattern">What is the Observer Design Pattern?</a></p>
</li>
<li><p><a href="#heading-the-problem-it-solves">The Problem It Solves</a></p>
</li>
<li><p><a href="#heading-core-components">Core Components</a></p>
</li>
<li><p><a href="#heading-implementing-observer-in-dart">Implementing Observer in Dart</a></p>
</li>
<li><p><a href="#heading-a-real-world-example-the-login-flow">A Real-World Example: The Login Flow</a></p>
</li>
<li><p><a href="#heading-making-it-production-grade-with-a-generic-eventbus">Making It Production-Grade with a Generic EventBus</a></p>
</li>
<li><p><a href="#heading-observer-is-already-in-your-flutter-code">Observer Is Already in Your Flutter Code</a></p>
</li>
<li><p><a href="#heading-deep-dive-into-event-driven-architecture">Deep Dive Into Event-Driven Architecture</a></p>
</li>
<li><p><a href="#heading-application-in-domain-driven-design">Application in Domain-Driven Design</a></p>
</li>
<li><p><a href="#heading-the-riverpod-hybrid-clean-architecture-in-practice">The Riverpod Hybrid: Clean Architecture in Practice</a></p>
</li>
<li><p><a href="#heading-testing-the-observer-architecture">Testing the Observer Architecture</a></p>
</li>
<li><p><a href="#heading-when-to-use-the-observer-pattern">When to Use the Observer Pattern</a></p>
</li>
<li><p><a href="#heading-when-not-to-use-it">When Not to Use It</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-observer-design-pattern">What is the Observer Design Pattern?</h2>
<p>The Observer pattern is a behavioural design pattern that defines a one-to-many dependency between objects. When one object changes state or fires an event, all of its dependents are notified and updated automatically.</p>
<p>Think of a newspaper subscription service. The newspaper publisher doesn't know who its individual subscribers are. It doesn't call each reader personally. It publishes the paper, and every subscriber who signed up receives it.</p>
<p>A subscriber can cancel at any time. A new subscriber can join at any time. The publisher's job never changes. It just publishes.</p>
<p>That's the Observer pattern in plain terms.</p>
<p>The publisher is called the <strong>Subject</strong>. The subscribers are called <strong>Observers</strong>. The newspaper is the <strong>event</strong>.</p>
<p>The pattern was formally defined in the Gang of Four book, Design Patterns: Elements of Reusable Object-Oriented Software. It remains one of the most widely used patterns in software engineering, especially in reactive and event-driven systems.</p>
<h2 id="heading-the-problem-it-solves">The Problem It Solves</h2>
<p>Let's look at what happens without the Observer pattern.</p>
<p>Say you have a login feature. When login succeeds, you need to do four things:</p>
<ul>
<li><p>Save the authentication token to secure storage</p>
</li>
<li><p>Cache the user profile data</p>
</li>
<li><p>Navigate to the home screen</p>
</li>
<li><p>Fire an analytics event</p>
</li>
</ul>
<p>The straightforward approach puts all of this inside the login function:</p>
<pre><code class="language-dart">Future&lt;void&gt; login(String email, String password) async {
  final response = await _authRepository.login(email, password);

  await _secureStorage.write(key: 'token', value: response.token);
  await _userCache.save(response.user);
  _navigationService.navigateTo('/home');
  _analytics.track('login_success', {'userId': response.user.id});
}
</code></pre>
<p>This looks fine at first glance. But count how many reasons this single function has to change:</p>
<ul>
<li><p>The token storage strategy changes. You modify this function.</p>
</li>
<li><p>The navigation destination changes. You modify this function.</p>
</li>
<li><p>The analytics event name or payload changes. You modify this function.</p>
</li>
<li><p>The user caching logic changes. You modify this function.</p>
</li>
</ul>
<p>Every single change to any of these four concerns forces you back into this one function. And every time you touch it, you risk breaking all the other three things it's doing.</p>
<p>Now imagine you need to add a fifth thing, such as enrolling the user in push notifications. You open this function again. You add more code. The function grows. Testing it requires mocking four, then five different dependencies. New teammates struggle to understand what this function is actually responsible for. The answer, of course, is everything. And that's the problem.</p>
<p>This is called tight coupling. The login logic is coupled to every single consequence of a successful login.</p>
<p>The Observer pattern breaks these couplings completely. The login logic does one thing: it performs the login and announces the result. Every consequence is handled by a separate, independent observer. Each observer has one job. None of them know about each other. The login logic doesn't know they exist.</p>
<h2 id="heading-core-components">Core Components</h2>
<p>The Observer pattern has four core building blocks. Understanding each one before writing code makes the implementation much easier to follow.</p>
<h3 id="heading-subject">Subject</h3>
<p>The Subject is the object that something happens to. It holds a list of observers and is responsible for notifying them when an event occurs. It exposes methods for observers to register and unregister themselves. The Subject doesn't care what observers do with the notification. It just delivers it.</p>
<h3 id="heading-observer">Observer</h3>
<p>The Observer is an interface or abstract class that defines the contract all observers must follow. It declares the method or methods the Subject will call when notifying. Any class that wants to react to an event must implement this interface.</p>
<h3 id="heading-concrete-subject">Concrete Subject</h3>
<p>The Concrete Subject is the real implementation of the Subject. It manages the actual list of observers, handles subscriptions, and fires notifications at the right moment.</p>
<h3 id="heading-concrete-observers">Concrete Observers</h3>
<p>These are the real classes that implement the Observer interface. Each one has a specific, focused job to do when notified. One saves the token. One navigates. One fires analytics. They don't know about each other and don't need to.</p>
<p>Here's how they relate to each other:</p>
<pre><code class="language-cpp">Subject (LoginService)
    |
    |-- subscribe(observer)    &lt;- observer registers itself
    |-- unsubscribe(observer)  &lt;- observer removes itself
    |-- notifySuccess(data)    &lt;- fires when login succeeds
    |-- notifyFailure(error)   &lt;- fires when login fails
         |
         |-----&gt; TokenObserver.onLoginSuccess()
         |-----&gt; UserObserver.onLoginSuccess()
         |-----&gt; NavigationObserver.onLoginSuccess()
         |-----&gt; AnalyticsObserver.onLoginSuccess()
</code></pre>
<p>The Subject notifies all of them. They each handle their own job independently.</p>
<h2 id="heading-implementing-observer-in-dart">Implementing Observer in Dart</h2>
<p>Let's build the pattern step by step.</p>
<h3 id="heading-step-1-define-the-observer-interface">Step 1: Define the Observer Interface</h3>
<pre><code class="language-dart">abstract class LoginObserver {
  void onLoginSuccess(UserDto user);
  void onLoginFailed(AppException error);
}
</code></pre>
<p>This is the contract that every observer must sign. Any class that wants to react to login events must implement both of these methods.</p>
<p><code>onLoginSuccess</code> is called when the login succeeds and receives the user data. <code>onLoginFailed</code> is called when the login fails and receives the error.</p>
<h3 id="heading-step-2-define-the-subject-interface">Step 2: Define the Subject Interface</h3>
<pre><code class="language-dart">abstract class LoginSubject {
  void subscribe(LoginObserver observer);
  void unsubscribe(LoginObserver observer);
  void notifySuccess(UserDto user);
  void notifyFailure(AppException error);
}
</code></pre>
<p><code>subscribe</code> lets an observer join the notification list. <code>unsubscribe</code> lets an observer leave the notification list. <code>notifySuccess</code> broadcasts a success event with the user data to all registered observers. <code>notifyFailure</code> broadcasts a failure event with the error to all registered observers.</p>
<p>Defining this as an abstract class instead of going straight to a concrete class is important. It means anything that depends on the subject depends on the abstraction, not the implementation. This makes your code testable and swappable.</p>
<h3 id="heading-step-3-implement-the-concrete-subject">Step 3: Implement the Concrete Subject</h3>
<pre><code class="language-dart">class LoginService implements LoginSubject {
  final List&lt;LoginObserver&gt; _observers = [];

  @override
  void subscribe(LoginObserver observer) {
    _observers.add(observer);
  }

  @override
  void unsubscribe(LoginObserver observer) {
    _observers.remove(observer);
  }

  @override
  void notifySuccess(UserDto user) {
    for (final observer in List.of(_observers)) {
      try {
        observer.onLoginSuccess(user);
      } catch (e) {
        debugPrint('Observer error on success: $e');
      }
    }
  }

  @override
  void notifyFailure(AppException error) {
    for (final observer in List.of(_observers)) {
      try {
        observer.onLoginFailed(error);
      } catch (e) {
        debugPrint('Observer error on failure: $e');
      }
    }
  }
}
</code></pre>
<p>There are two important decisions in this implementation that are easy to miss.</p>
<h4 id="heading-1-snapshot-iteration-with-listof">1. Snapshot iteration with <code>List.of()</code></h4>
<p>Instead of iterating directly over <code>_observers</code>, we iterate over <code>List.of(_observers)</code>, which creates a copy of the list before the loop runs.</p>
<p>Why does this matter? Imagine a <code>NavigationObserver</code> that, after navigating to the home screen, unsubscribes itself because it no longer needs to listen. If it calls <code>unsubscribe</code> while the <code>notifySuccess</code> loop is still running over the same list, Dart throws a <code>ConcurrentModificationError</code>. The list is being modified while it's being read.</p>
<p><code>List.of()</code> prevents this entirely. The loop runs over the snapshot. The original list can be modified freely during iteration without any errors.</p>
<h4 id="heading-2-per-observer-trycatch">2. Per-observer try/catch</h4>
<p>Each observer call is wrapped in its own try/catch block. This is a deliberate choice. If <code>TokenObserver</code> throws an exception while writing to secure storage, you don't want <code>NavigationObserver</code> and <code>AnalyticsObserver</code> to silently never fire. Each observer gets its chance to run regardless of what the others do.</p>
<p>Without this, one failing observer would stop the entire notification chain. That's a hidden bug that's extremely difficult to trace in production.</p>
<h2 id="heading-a-real-world-example-the-login-flow">A Real-World Example: The Login Flow</h2>
<p>Now let's build the full login flow using this foundation.</p>
<h3 id="heading-the-login-logic">The Login Logic</h3>
<pre><code class="language-cpp">class LoginLogic {
  final LoginSubject _subject;
  final AuthRepository _repository;

  LoginLogic({
    required LoginSubject subject,
    required AuthRepository repository,
  })  : _subject = subject,
        _repository = repository;

  Future&lt;void&gt; callLogin(LoginRequest request) async {
    try {
      final user = await _repository.login(request);
      _subject.notifySuccess(user);
    } on AppException catch (e) {
      _subject.notifyFailure(e);
    } catch (e) {
      _subject.notifyFailure(AppException.unknown(message: e.toString()));
    }
  }
}
</code></pre>
<p>Let's walk through this carefully.</p>
<p><code>LoginLogic</code> takes two dependencies through its constructor: a <code>LoginSubject</code> and an <code>AuthRepository</code>. Notice it takes <code>LoginSubject</code>, the abstraction, not <code>LoginService</code>, the concrete class. This means you can swap the implementation in tests or in different environments without changing <code>LoginLogic</code> at all.</p>
<p>Inside <code>callLogin</code>, the logic is straightforward. It calls the repository to perform the actual login. If that succeeds, it calls <code>notifySuccess</code> on the subject with the returned user. If it throws an <code>AppException</code>, it calls <code>notifyFailure</code> with that error. If it throws anything unexpected, it wraps it in an <code>AppException.unknown</code> and notifies failure.</p>
<p>Notice what <code>LoginLogic</code> does NOT do. It doesn't save a token. It doesn't navigate anywhere. It doesn't cache anything. It doesn't fire analytics. And it doesn't know how many observers exist or what they do.</p>
<p>Its entire responsibility is: perform the login, announce the result.</p>
<h3 id="heading-the-concrete-observers">The Concrete Observers</h3>
<pre><code class="language-cpp">class TokenObserver implements LoginObserver {
  final SecureStorageService _storage;

  TokenObserver(this._storage);

  @override
  void onLoginSuccess(UserDto user) {
    _storage.write(key: 'auth_token', value: user.token);
  }

  @override
  void onLoginFailed(AppException error) {
    _storage.delete(key: 'auth_token');
  }
}
</code></pre>
<p><code>TokenObserver</code> has one job: manage the authentication token. On success, it saves the token. On failure, it clears any stale token that might be sitting in storage. It knows nothing about navigation, caching, or analytics.</p>
<pre><code class="language-cpp">class UserObserver implements LoginObserver {
  final UserCacheService _cache;

  UserObserver(this._cache);

  @override
  void onLoginSuccess(UserDto user) {
    _cache.save(user);
  }

  @override
  void onLoginFailed(AppException error) {
    _cache.clear();
  }
}
</code></pre>
<p><code>UserObserver</code> has one job: manage the user cache. On success, it saves the user profile. On failure, it clears the cache. It knows nothing about tokens, navigation, or analytics.</p>
<pre><code class="language-cpp">class NavigationObserver implements LoginObserver {
  final NavigationService _navigation;

  NavigationObserver(this._navigation);

  @override
  void onLoginSuccess(UserDto user) {
    _navigation.navigateTo('/home');
  }

  @override
  void onLoginFailed(AppException error) {
    _navigation.showError(error.message);
  }
}
</code></pre>
<p><code>NavigationObserver</code> has one job: handle navigation after a login attempt. It uses an injected <code>NavigationService</code> abstraction rather than a <code>BuildContext</code>. This is intentional. An observer that depends on <code>BuildContext</code> is tied to the widget lifecycle. Using an abstraction keeps this observer completely independent of the UI layer.</p>
<pre><code class="language-cpp">class AnalyticsObserver implements LoginObserver {
  final AnalyticsService _analytics;

  AnalyticsObserver(this._analytics);

  @override
  void onLoginSuccess(UserDto user) {
    _analytics.track('login_success', {'userId': user.id});
  }

  @override
  void onLoginFailed(AppException error) {
    _analytics.track('login_failed', {'reason': error.message});
  }
}
</code></pre>
<p><code>AnalyticsObserver</code> has one job: fire the right analytics event for each outcome. It has no knowledge of storage, navigation, or caching.</p>
<p>Each observer has exactly one responsibility. Each one has exactly one reason to change. When the analytics payload needs to change, you touch only <code>AnalyticsObserver</code>. When navigation logic changes, you touch only <code>NavigationObserver</code>. Nothing else is affected.</p>
<h3 id="heading-wiring-it-together">Wiring It Together</h3>
<pre><code class="language-cpp">void setupLogin() {
  final service = LoginService();

  service
    ..subscribe(TokenObserver(secureStorage))
    ..subscribe(UserObserver(userCache))
    ..subscribe(NavigationObserver(navigationService))
    ..subscribe(AnalyticsObserver(analyticsService));

  final loginLogic = LoginLogic(
    subject: service,
    repository: authRepository,
  );
}
</code></pre>
<p>This is the composition step. All observers are created with their dependencies and registered onto the service. The cascade operator <code>..</code> calls <code>subscribe</code> multiple times on the same <code>service</code> object, which keeps the setup readable.</p>
<p><code>LoginLogic</code> receives the <code>service</code> as its <code>LoginSubject</code>. From this point forward, every time <code>callLogin</code> is called and an outcome occurs, all four observers are notified automatically.</p>
<p>Adding a fifth observer, say a <code>PushNotificationObserver</code>, means creating the class and adding one line here: <code>..subscribe(PushNotificationObserver(pushService))</code>. Nothing else in the entire codebase changes.</p>
<h2 id="heading-making-it-production-grade-with-a-generic-eventbus">Making It Production-Grade with a Generic EventBus</h2>
<p>The login example above works well, but it's specific to login. In a real application, many features have the same fan-out requirement. Payment confirmed, order placed, profile updated, session expired. All of them need one event to trigger multiple independent reactions.</p>
<p>Rewriting the Subject and Observer interfaces per feature is repetitive and unnecessary. The better approach is a generic <code>EventBus</code> that any feature can use.</p>
<pre><code class="language-cpp">abstract class DomainObserver&lt;T&gt; {
  void onSuccess(T data);
  void onFailure(AppException error);
}
</code></pre>
<p><code>DomainObserver&lt;T&gt;</code> is a generic observer. The type parameter <code>T</code> represents the data type the observer expects on success. A login observer would be <code>DomainObserver&lt;UserDto&gt;</code>. A payment observer would be <code>DomainObserver&lt;PaymentDto&gt;</code>. The interface is the same. The data type changes per feature.</p>
<pre><code class="language-cpp">class EventBus&lt;T&gt; {
  final List&lt;DomainObserver&lt;T&gt;&gt; _observers = [];

  void subscribe(DomainObserver&lt;T&gt; observer) {
    _observers.add(observer);
  }

  void unsubscribe(DomainObserver&lt;T&gt; observer) {
    _observers.remove(observer);
  }

  void publishSuccess(T data) {
    for (final observer in List.of(_observers)) {
      try {
        observer.onSuccess(data);
      } catch (e) {
        debugPrint('[EventBus] Observer error on success: $e');
      }
    }
  }

  void publishFailure(AppException error) {
    for (final observer in List.of(_observers)) {
      try {
        observer.onFailure(error);
      } catch (e) {
        debugPrint('[EventBus] Observer error on failure: $e');
      }
    }
  }
}
</code></pre>
<p><code>EventBus&lt;T&gt;</code> is a generic subject. It manages a list of typed observers and notifies them with the same snapshot iteration and per-observer error isolation we established earlier.</p>
<p>Now every feature gets the same infrastructure without duplicating a single line of the pattern:</p>
<pre><code class="language-cpp">final loginBus = EventBus&lt;UserDto&gt;();
final paymentBus = EventBus&lt;PaymentDto&gt;();
final orderBus = EventBus&lt;OrderDto&gt;();
</code></pre>
<p>Each bus is typed to its domain concept. Observers registered on <code>loginBus</code> will never accidentally receive payment events. The type system enforces correctness.</p>
<h2 id="heading-observer-is-already-in-your-flutter-code">Observer Is Already in Your Flutter Code</h2>
<p>Before going further into architecture, here's something worth pausing on. You've been using the Observer pattern all along without calling it by that name.</p>
<p><strong>Streams and StreamController:</strong></p>
<pre><code class="language-cpp">final controller = StreamController&lt;String&gt;();

controller.stream.listen((event) {
  print('Observed: $event');
});

controller.sink.add('Login succeeded');
</code></pre>
<p><code>StreamController</code> is a Subject. <code>stream.listen</code> is <code>subscribe</code>. <code>sink.add</code> is <code>notifyObservers</code>. Every stream subscription is an Observer. The pattern is identical. Flutter just gave it different names.</p>
<p><strong>ChangeNotifier:</strong></p>
<pre><code class="language-cpp">class CounterModel extends ChangeNotifier {
  int _count = 0;

  void increment() {
    _count++;
    notifyListeners();
  }
}
</code></pre>
<p><code>notifyListeners()</code> iterates over every registered listener and calls them. Those listeners are Observers. <code>addListener</code> is <code>subscribe</code>. <code>removeListener</code> is <code>unsubscribe</code>. <code>ChangeNotifier</code> is a concrete Subject.</p>
<p><strong>BLoC:</strong></p>
<p>When a BLoC emits a new state, every widget that wrapped itself in a <code>BlocBuilder</code> or <code>BlocListener</code> reacts. The BLoC is the Subject. The builders and listeners are Observers. The state emission is the notification.</p>
<p>Flutter's entire reactive system (Streams, ChangeNotifier, BLoC, ValueNotifier) is the Observer pattern with lifecycle management built in. Understanding the pattern at this fundamental level means you understand why all of these tools work the way they do. You aren't just using them. You understand them.</p>
<h2 id="heading-deep-dive-into-event-driven-architecture">Deep Dive Into Event-Driven Architecture</h2>
<p>Understanding Observer at the class level is the foundation. The pattern becomes significantly more powerful when applied at the architectural level, and that's where Event-Driven Architecture comes in.</p>
<h3 id="heading-what-is-event-driven-architecture">What is Event-Driven Architecture?</h3>
<p>Event-Driven Architecture is a design paradigm where the flow of the application is determined by events. Instead of components calling each other directly, they communicate by producing and consuming events through a shared bus or channel.</p>
<p>In a traditional request-driven flow, this is what happens:</p>
<pre><code class="language-cpp">Component A calls Component B directly
Component B does its work and returns a result
Component A waits for that result and then continues
</code></pre>
<p>Component A knows about Component B. It depends on it by name. It waits for it to finish. If you want Component C to also react to whatever Component A is doing, you have to go back into Component A and add that call.</p>
<p>But then Component A grows. Component A becomes responsible for orchestrating consequences it should know nothing about.</p>
<p>In an event-driven flow, this is what happens instead:</p>
<pre><code class="language-cpp">Component A publishes an event to the EventBus
EventBus delivers the event to whoever is registered

Component B handles the event
Component C handles the event
Component D handles the event
</code></pre>
<p>Component A doesn't know about B, C, or D. It doesn't wait for them. It publishes what happened and moves on. New handlers can be added without touching Component A at all. This is the Observer pattern scaled to the architectural level.</p>
<h3 id="heading-events-are-facts-not-commands">Events Are Facts, Not Commands</h3>
<p>This distinction is one of the most important concepts in Event-Driven Architecture.</p>
<p>A command says: "do this." It's an instruction that can be rejected. It expects a response.</p>
<p>An event says: "this happened." It's an immutable record of a fact. It doesn't expect a response. It doesn't care who handles it.</p>
<p><code>SaveUserToken</code> is a command. <code>UserLoggedIn</code> is an event.</p>
<p>When you model your system with events as facts, you get a historical record of everything that happened in your application. You can replay events to reconstruct state. You can add new handlers that process historical events. Your system becomes auditable and predictable in ways that command-driven systems are not.</p>
<h3 id="heading-modelling-domain-events-in-dart">Modelling Domain Events in Dart</h3>
<p>Events should be immutable value objects. They're facts. Facts don't change after they happen.</p>
<pre><code class="language-cpp">abstract class DomainEvent {
  final DateTime occurredAt;
  final String eventId;

  const DomainEvent({
    required this.occurredAt,
    required this.eventId,
  });
}
</code></pre>
<p><code>DomainEvent</code> is the base class for all events in the system. Every event has a timestamp (<code>occurredAt</code>) recording when it happened, and a unique identifier (<code>eventId</code>) for traceability.</p>
<pre><code class="language-cpp">class UserLoggedIn extends DomainEvent {
  final UserDto user;

  const UserLoggedIn({
    required this.user,
    required super.occurredAt,
    required super.eventId,
  });
}

class LoginFailed extends DomainEvent {
  final AppException error;

  const LoginFailed({
    required this.error,
    required super.occurredAt,
    required super.eventId,
  });
}
</code></pre>
<p><code>UserLoggedIn</code> carries the user data. <code>LoginFailed</code> carries the error. Both are immutable. Both have timestamps and identifiers. Both are concrete facts about something that happened in the domain.</p>
<h3 id="heading-a-type-safe-domaineventbus">A Type-Safe DomainEventBus</h3>
<p>Now we can build an event bus that's typed to domain events specifically:</p>
<pre><code class="language-cpp">abstract class EventHandler&lt;T extends DomainEvent&gt; {
  void handle(T event);
}
</code></pre>
<p><code>EventHandler&lt;T&gt;</code> is the Observer interface for this architecture. Any class that wants to handle a domain event implements this with the specific event type it cares about.</p>
<pre><code class="language-cpp">class DomainEventBus {
  final _handlers = &lt;Type, List&lt;EventHandler&gt;&gt;{};

  void register&lt;T extends DomainEvent&gt;(EventHandler&lt;T&gt; handler) {
    _handlers.putIfAbsent(T, () =&gt; []).add(handler);
  }

  void publish&lt;T extends DomainEvent&gt;(T event) {
    final handlers = List.of(_handlers[T] ?? []);
    for (final handler in handlers) {
      try {
        (handler as EventHandler&lt;T&gt;).handle(event);
      } catch (e) {
        debugPrint('[DomainEventBus] Handler error for ${T}: $e');
      }
    }
  }
}
</code></pre>
<p>Let's go through <code>DomainEventBus</code> carefully.</p>
<p><code>_handlers</code> is a map where the key is a <code>Type</code> (the event class itself, like <code>UserLoggedIn</code>) and the value is a list of all handlers registered for that event type.</p>
<p><code>register&lt;T&gt;</code> takes a handler and adds it to the list for type <code>T</code>. <code>putIfAbsent</code> ensures the list is created if this is the first handler for that event type.</p>
<p><code>publish&lt;T&gt;</code> looks up all handlers registered for the type of event being published and calls each one's <code>handle</code> method. The snapshot with <code>List.of()</code> and the per-handler try/catch are both present for the same reasons we established earlier.</p>
<p>Here's how you register handlers and publish events:</p>
<pre><code class="language-dart">// Registration happens once at startup
eventBus.register&lt;UserLoggedIn&gt;(TokenHandler(secureStorage));
eventBus.register&lt;UserLoggedIn&gt;(UserCacheHandler(userCache));
eventBus.register&lt;UserLoggedIn&gt;(NavigationHandler(navigationService));
eventBus.register&lt;UserLoggedIn&gt;(AnalyticsHandler(analyticsService));

eventBus.register&lt;PaymentConfirmed&gt;(ReceiptHandler(receiptService));
eventBus.register&lt;PaymentConfirmed&gt;(InventoryHandler(inventoryService));

// Publishing happens at the use case level
eventBus.publish(UserLoggedIn(
  user: user,
  occurredAt: DateTime.now(),
  eventId: const Uuid().v4(),
));
</code></pre>
<p>When <code>UserLoggedIn</code> is published, only its registered handlers fire. Payment handlers aren't touched. Every handler for <code>UserLoggedIn</code> runs independently with full error isolation.</p>
<h2 id="heading-application-in-domain-driven-design">Application in Domain-Driven Design</h2>
<p>Event-Driven Architecture and the Observer pattern find their most structured home inside Domain-Driven Design. DDD gives us the vocabulary and structure to know exactly where events belong, who creates them, and who handles them.</p>
<h3 id="heading-key-ddd-concepts-you-need-to-know">Key DDD Concepts You Need to Know</h3>
<p><strong>Domain Events</strong> are first-class citizens in DDD. They represent something meaningful that happened in the business domain. Not a technical detail, not an HTTP response, but a business fact.</p>
<p><code>UserLoggedIn</code> is a domain event. <code>LoginResponseDto</code> is a data transfer object. The distinction matters deeply. The event belongs to the domain model and expresses business language. The DTO belongs to the data layer and expresses data structure.</p>
<p><strong>Aggregates</strong> are the natural source of domain events. An Aggregate is a cluster of domain objects that form a consistency boundary. The Aggregate enforces business rules and raises domain events when significant state changes occur within it.</p>
<p><strong>Use Cases</strong> are the orchestrators. A use case calls the repository, gets the result, raises the appropriate domain event, and returns the outcome. It doesn't handle side effects directly. It announces what happened and lets the registered handlers take over.</p>
<h3 id="heading-where-everything-lives-in-clean-architecture">Where Everything Lives in Clean Architecture</h3>
<pre><code class="language-plaintext">lib/
  core/
    events/
      domain_event.dart           &lt;- Base DomainEvent class
      domain_event_bus.dart       &lt;- The DomainEventBus
      event_handler.dart          &lt;- Base EventHandler interface

  features/
    auth/
      domain/
        events/
          user_logged_in.dart     &lt;- Domain event (pure Dart, no Flutter)
          login_failed.dart       &lt;- Domain event
        handlers/
          token_handler.dart      &lt;- Handles token storage
          user_cache_handler.dart &lt;- Handles user caching
          analytics_handler.dart  &lt;- Handles analytics
        entities/
          user.dart
        repositories/
          auth_repository.dart    &lt;- Abstract interface only
        usecases/
          login_usecase.dart      &lt;- Orchestrates, publishes events

      data/
        repositories/
          auth_repository_impl.dart
        datasources/
          auth_remote_datasource.dart

      presentation/
        providers/
          login_provider.dart     &lt;- Riverpod notifier (thin)
        pages/
          login_page.dart
</code></pre>
<p>The critical rule: the domain layer is pure Dart. No Flutter imports. No Riverpod imports. No HTTP imports. The <code>DomainEventBus</code>, domain events, handlers, and use cases all live in the domain layer and have zero framework dependencies.</p>
<p>This means that the same domain logic works in Flutter, server-side Dart, or a CLI tool without changing a single line. Framework upgrades, say from Riverpod 2.x to a future version, never touch the domain. Unit tests for the domain run in milliseconds with no widget test overhead.</p>
<h3 id="heading-the-login-use-case-in-ddd">The Login Use Case in DDD</h3>
<pre><code class="language-cpp">class LoginUseCase {
  final AuthRepository _repository;
  final DomainEventBus _eventBus;

  LoginUseCase({
    required AuthRepository repository,
    required DomainEventBus eventBus,
  })  : _repository = repository,
        _eventBus = eventBus;

  Future&lt;Result&lt;UserDto, AppException&gt;&gt; execute(LoginRequest request) async {
    try {
      final user = await _repository.login(request);

      _eventBus.publish(UserLoggedIn(
        user: user,
        occurredAt: DateTime.now(),
        eventId: const Uuid().v4(),
      ));

      return Result.success(user);
    } on AppException catch (e) {
      _eventBus.publish(LoginFailed(
        error: e,
        occurredAt: DateTime.now(),
        eventId: const Uuid().v4(),
      ));

      return Result.failure(e);
    }
  }
}
</code></pre>
<p>Let's walk through this step by step.</p>
<p><code>LoginUseCase</code> receives two dependencies: an <code>AuthRepository</code> abstraction and a <code>DomainEventBus</code>. Neither is a concrete class. Both can be swapped in tests.</p>
<p>Inside <code>execute</code>, it calls the repository to perform the login. If the login succeeds, it publishes a <code>UserLoggedIn</code> event to the bus, which immediately notifies all registered handlers. Then it returns a <code>Result.success</code> wrapping the user data.</p>
<p>If an <code>AppException</code> is caught, it publishes a <code>LoginFailed</code> event to the bus, which notifies all failure handlers. Then it returns a <code>Result.failure</code> wrapping the error.</p>
<p>The use case doesn't know how many handlers are registered. It doesn't know what they do. It performs the operation, publishes the outcome as a domain event, and returns the result.</p>
<p>The <code>Result</code> type is a return value for the caller (the Riverpod notifier) to know the outcome. The domain event is the broadcast for all side effect handlers. Both travel from the same single use case call. This is what makes the architecture clean.</p>
<h2 id="heading-the-riverpod-hybrid-clean-architecture-in-practice">The Riverpod Hybrid: Clean Architecture in Practice</h2>
<p>This is where everything comes together in a real Flutter application.</p>
<h3 id="heading-the-problem-we-are-solving">The Problem We Are Solving</h3>
<p>There are two common pain points in Flutter apps that use Riverpod:</p>
<p>Fat ref.listen in widgets:</p>
<pre><code class="language-cpp">// This is messy
ref.listen&lt;AsyncValue&lt;UserDto?&gt;&gt;(loginProvider, (previous, next) {
  next.whenData((user) {
    if (user != null) {
      secureStorage.write(key: 'token', value: user.token);
      userCache.save(user);
      context.go('/home');
      analytics.track('login_success');
    }
  });
});
</code></pre>
<p>The widget is mounted. If it unmounts before all of this completes, some side effects may never run. Business consequences like token storage and navigation shouldn't depend on whether a widget is still alive. This is fragile architecture.</p>
<p>Fat notifiers:</p>
<pre><code class="language-dart">// Notifier doing too much
Future&lt;void&gt; login(LoginRequest request) async {
  state = const AsyncLoading();
  try {
    final user = await _loginUseCase.execute(request);
    await _secureStorage.write(key: 'token', value: user.token);
    await _userCache.save(user);
    _navigationService.navigateTo('/home');
    _analytics.track('login_success');
    state = AsyncData(user);
  } catch (e, st) {
    state = AsyncError(e, st);
  }
}
</code></pre>
<p>The notifier is violating the Single Responsibility Principle. It's performing the login, saving the token, caching the user, navigating, tracking analytics, and managing UI state. That's six responsibilities in one class. It's impossible to test cleanly and painful to maintain.</p>
<h3 id="heading-the-clean-rule">The Clean Rule</h3>
<p>Before looking at the solution, establish this rule clearly:</p>
<p><strong>The use case owns domain consequences. The notifier owns UI state. Widgets own nothing.</strong></p>
<p>The use case performs the operation and publishes domain events. Handlers fire when those events are published and run completely independently of the widget lifecycle. The notifier receives the result from the use case and emits loading, success, or error state so the UI knows what to display. Widgets read that state and render accordingly.</p>
<p>That's the full picture. And it means this architecture works correctly whether login is triggered from a widget, a biometric prompt, a deep link, or a background service. The use case always publishes. The handlers always fire. The notifier only deals with UI state.</p>
<h3 id="heading-understanding-asyncnotifier">Understanding AsyncNotifier</h3>
<p>Before writing the notifier, let's understand what <code>AsyncNotifier</code> is and how it works.</p>
<p><code>AsyncNotifier</code> is a Riverpod 2.0 class designed specifically for asynchronous state. It holds an <code>AsyncValue&lt;T&gt;</code>, which is a sealed type that can be one of three things:</p>
<p><code>AsyncData&lt;T&gt;</code> means the operation succeeded and data is available. <code>AsyncLoading</code> means an operation is in progress. <code>AsyncError</code> means an operation failed.</p>
<p>When you extend <code>AsyncNotifier&lt;T&gt;</code>, you implement a <code>build</code> method that returns the initial state, and you write methods that mutate <code>state</code> as async operations progress.</p>
<p>With code generation using <code>@riverpod</code>, you annotate your class and run <code>flutter pub run build_runner build</code>. The generator creates the provider and all the boilerplate automatically. You focus entirely on the logic.</p>
<p>Here's the full setup for code generation:</p>
<pre><code class="language-yaml"># pubspec.yaml
dependencies:
  flutter_riverpod: ^2.5.1
  riverpod_annotation: ^2.3.5

dev_dependencies:
  riverpod_generator: ^2.4.0
  build_runner: ^2.4.9
</code></pre>
<h3 id="heading-the-thin-notifier">The Thin Notifier</h3>
<pre><code class="language-cpp">// login_provider.dart
part 'login_provider.g.dart';

@riverpod
class LoginNotifier extends _$LoginNotifier {

  @override
  AsyncValue&lt;UserDto?&gt; build() {
    return const AsyncData(null);
  }

  Future&lt;void&gt; login(LoginRequest request) async {
    state = const AsyncLoading();

    final result = await ref.read(loginUseCaseProvider).execute(request);

    result.fold(
      onSuccess: (user) =&gt; state = AsyncData(user),
      onFailure: (error) =&gt; state = AsyncError(error, StackTrace.current),
    );
  }
}
</code></pre>
<p>Let's go through this line by line.</p>
<p><code>part 'login_provider.g.dart'</code> tells Dart that the generated file is part of this library. The <code>@riverpod</code> annotation and <code>_$LoginNotifier</code> base class come from the generated file.</p>
<p><code>build()</code> is the initialisation method. It runs when the provider is first read. It returns <code>AsyncData(null)</code>, meaning the initial state is a successful state with no user yet. This is correct because no login has been attempted.</p>
<p>Inside <code>login</code>, the first thing we do is set <code>state = const AsyncLoading()</code>. This immediately notifies any widget watching this provider that an operation is in progress. The UI can show a loading indicator.</p>
<p>We then call the use case and <code>await</code> its result. The use case returns a <code>Result&lt;UserDto, AppException&gt;</code>, which is a type that holds either a success value or a failure value, never both. We call <code>fold</code> on it to handle each case.</p>
<p>In the <code>onSuccess</code> branch, we set <code>state = AsyncData(user)</code>. This tells the UI the operation succeeded and here is the user data to render.</p>
<p>In the <code>onFailure</code> branch, we set <code>state = AsyncError(error, StackTrace.current)</code>. This tells the UI something went wrong so it can display the appropriate error state.</p>
<p>That's the entire notifier. It does exactly one thing: reflect the outcome of the use case as UI state.</p>
<p>Notice there's no token saving here. No navigation, caching, or analytics. All of that is already handled. The moment the use case called <code>_eventBus.publish(UserLoggedIn(...))</code> inside <code>execute</code>, every registered handler fired automatically. By the time <code>result</code> is returned to this notifier, all side effects are already done. The notifier just needs to update the UI.</p>
<p>This is the cleanest possible separation. The use case owns domain consequences. The notifier owns render state. Each has exactly one responsibility.</p>
<h3 id="heading-the-widget">The Widget</h3>
<pre><code class="language-cpp">class LoginPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final loginState = ref.watch(loginNotifierProvider);

    return Scaffold(
      body: loginState.when(
        data: (_) =&gt; const LoginForm(),
        loading: () =&gt; const Center(child: CircularProgressIndicator()),
        error: (error, _) =&gt; ErrorView(message: error.toString()),
      ),
    );
  }
}

class LoginForm extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: () {
            ref.read(loginNotifierProvider.notifier).login(
              LoginRequest(email: 'user@example.com', password: 'secret'),
            );
          },
          child: const Text('Login'),
        ),
      ],
    );
  }
}
</code></pre>
<p><code>ref.watch(loginNotifierProvider)</code> subscribes this widget to the notifier's state. Every time <code>state</code> changes inside the notifier, <code>build</code> is called again and the widget re-renders.</p>
<p><code>loginState.when</code> is how you handle each case of <code>AsyncValue</code>. When the state is <code>AsyncData</code>, it renders the login form. When it is <code>AsyncLoading</code>, it renders a loading indicator. When it is <code>AsyncError</code>, it renders the error view.</p>
<p>The widget knows nothing about tokens, navigation, or caching. It renders what it's told to render by the state. That's its entire job.</p>
<h3 id="heading-wiring-the-composition-root">Wiring the Composition Root</h3>
<p>All handler registrations happen once at app startup inside a Riverpod provider:</p>
<pre><code class="language-cpp">@riverpod
DomainEventBus eventBus(EventBusRef ref) {
  final bus = DomainEventBus();

  bus.register&lt;UserLoggedIn&gt;(
    TokenHandler(ref.read(secureStorageProvider)),
  );
  bus.register&lt;UserLoggedIn&gt;(
    UserCacheHandler(ref.read(userCacheProvider)),
  );
  bus.register&lt;UserLoggedIn&gt;(
    NavigationHandler(ref.read(navigationServiceProvider)),
  );
  bus.register&lt;UserLoggedIn&gt;(
    AnalyticsHandler(ref.read(analyticsServiceProvider)),
  );

  bus.register&lt;LoginFailed&gt;(
    AnalyticsFailureHandler(ref.read(analyticsServiceProvider)),
  );

  return bus;
}
</code></pre>
<p><code>eventBus</code> is a provider that creates the <code>DomainEventBus</code> and registers all handlers at the moment it is first read. Because Riverpod providers are lazy by default and cached after first creation, this runs once and the bus lives for the entire app session.</p>
<p>Every handler gets its dependencies injected via <code>ref.read</code>. Nothing is hardcoded. Everything is swappable in tests.</p>
<p>The <code>LoginUseCase</code> receives this event bus as a dependency through its own provider:</p>
<pre><code class="language-cpp">@riverpod
LoginUseCase loginUseCase(LoginUseCaseRef ref) {
  return LoginUseCase(
    repository: ref.read(authRepositoryProvider),
    eventBus: ref.read(eventBusProvider),
  );
}
</code></pre>
<p>This is the only place that connects the use case to the event bus. The notifier receives only the use case. The widget receives only the notifier's state. Each layer knows only about the layer directly below it and nothing else.</p>
<p>Adding a new side effect to login means creating a new handler class and adding one <code>bus.register</code> line in the composition root. The notifier, the use case logic, the widget, and every existing handler remain completely untouched.</p>
<h2 id="heading-testing-the-observer-architecture">Testing the Observer Architecture</h2>
<p>One of the most significant advantages of this architecture is how clearly it separates test concerns. Each layer has its own focused test scope.</p>
<h3 id="heading-testing-the-use-case">Testing the Use Case</h3>
<pre><code class="language-cpp">void main() {
  group('LoginUseCase', () {
    late LoginUseCase useCase;
    late MockAuthRepository mockRepository;
    late MockDomainEventBus mockEventBus;

    setUp(() {
      mockRepository = MockAuthRepository();
      mockEventBus = MockDomainEventBus();
      useCase = LoginUseCase(
        repository: mockRepository,
        eventBus: mockEventBus,
      );
    });

    test('publishes UserLoggedIn event on success', () async {
      final user = UserDto(id: '1', token: 'token123');
      when(() =&gt; mockRepository.login(any())).thenAnswer((_) async =&gt; user);

      await useCase.execute(LoginRequest(email: 'a@b.com', password: '123'));

      verify(() =&gt; mockEventBus.publish(any&lt;UserLoggedIn&gt;())).called(1);
    });

    test('publishes LoginFailed event on error', () async {
      when(() =&gt; mockRepository.login(any()))
          .thenThrow(AppException.unauthorized(message: 'Invalid credentials'));

      await useCase.execute(LoginRequest(email: 'a@b.com', password: 'wrong'));

      verify(() =&gt; mockEventBus.publish(any&lt;LoginFailed&gt;())).called(1);
    });
  });
}
</code></pre>
<p>The use case test mocks the repository and the event bus. It verifies that the correct event type was published for each outcome. It doesn't test what any handler does. That's not the use case's responsibility, so it's not the use case's test.</p>
<h3 id="heading-testing-each-handler">Testing Each Handler</h3>
<pre><code class="language-cpp">void main() {
  group('TokenHandler', () {
    late TokenHandler handler;
    late MockSecureStorageService mockStorage;

    setUp(() {
      mockStorage = MockSecureStorageService();
      handler = TokenHandler(mockStorage);
    });

    test('writes token to secure storage on UserLoggedIn', () {
      final event = UserLoggedIn(
        user: UserDto(id: '1', token: 'abc123'),
        occurredAt: DateTime.now(),
        eventId: 'event-1',
      );

      handler.handle(event);

      verify(
        () =&gt; mockStorage.write(key: 'auth_token', value: 'abc123'),
      ).called(1);
    });
  });
}
</code></pre>
<p>Each handler test is tiny. It creates the handler with a mocked dependency, fires the event, and verifies the exact side effect that handler is responsible for. No other handler is involved, no notifier is involved, and no widget is involved.</p>
<h3 id="heading-testing-the-notifier">Testing the Notifier</h3>
<pre><code class="language-cpp">void main() {
  group('LoginNotifier', () {
    test('transitions from loading to data on success', () async {
      final mockUseCase = MockLoginUseCase();
      final user = UserDto(id: '1', token: 'token123');

      when(() =&gt; mockUseCase.execute(any()))
          .thenAnswer((_) async =&gt; Result.success(user));

      final container = ProviderContainer(overrides: [
        loginUseCaseProvider.overrideWithValue(mockUseCase),
      ]);

      final notifier = container.read(loginNotifierProvider.notifier);

      await notifier.login(LoginRequest(email: 'a@b.com', password: '123'));

      expect(
        container.read(loginNotifierProvider),
        isA&lt;AsyncData&lt;UserDto?&gt;&gt;(),
      );
    });

    test('transitions from loading to error on failure', () async {
      final mockUseCase = MockLoginUseCase();
      final error = AppException.unauthorized(message: 'Invalid credentials');

      when(() =&gt; mockUseCase.execute(any()))
          .thenAnswer((_) async =&gt; Result.failure(error));

      final container = ProviderContainer(overrides: [
        loginUseCaseProvider.overrideWithValue(mockUseCase),
      ]);

      final notifier = container.read(loginNotifierProvider.notifier);

      await notifier.login(LoginRequest(email: 'a@b.com', password: 'wrong'));

      expect(
        container.read(loginNotifierProvider),
        isA&lt;AsyncError&gt;(),
      );
    });
  });
}
</code></pre>
<p>The notifier test only verifies state transitions. It doesn't need to mock the event bus because the notifier no longer touches the event bus. That's the use case's job, and the use case has its own test that verifies events are published correctly. Each layer is tested in complete isolation with no overlap.</p>
<h2 id="heading-when-to-use-the-observer-pattern">When to Use the Observer Pattern</h2>
<p>Use Observer when:</p>
<ul>
<li><p>One event needs to trigger multiple independent reactions</p>
</li>
<li><p>You want to add or remove reactions without modifying the event source</p>
</li>
<li><p>Side effects need to be decoupled from business logic</p>
</li>
<li><p>Each reaction should be independently testable</p>
</li>
<li><p>Multiple parts of the system need to react to the same state change</p>
</li>
<li><p>You are building a feature that will grow in number of side effects over time</p>
</li>
</ul>
<h2 id="heading-when-not-to-use-it">When Not to Use It</h2>
<p>Avoid Observer when:</p>
<ul>
<li><p>You have only one consumer and no realistic expectation of more</p>
</li>
<li><p>The relationship between producer and consumer is simple and direct</p>
</li>
<li><p>The pattern adds structural overhead without meaningful benefit</p>
</li>
<li><p>Streams, ChangeNotifier, or Riverpod's built-in reactivity already solve the problem naturally</p>
</li>
<li><p>Strict ordering of side effects is critical and fan-out makes that hard to guarantee</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The Observer Design Pattern is one of the most important tools in a software engineer's arsenal. This isn't because it's clever, but because it solves a problem every growing application faces: how do you let one event trigger many reactions without turning your codebase into a tightly coupled mess?</p>
<p>You started by understanding the pattern at its core. A Subject holds a list of Observers and notifies them when events occur. You saw it built step by step in Dart, with snapshot iteration to prevent concurrent modification errors, per-observer try/catch to prevent failure cascades, and dependency inversion to keep everything testable.</p>
<p>You discovered that the Observer pattern is already embedded in Flutter's Streams, ChangeNotifier, and BLoC. Understanding its foundations means you understand why those tools work the way they do.</p>
<p>You then took the pattern into Event-Driven Architecture, where events become immutable domain facts and the system is composed of producers and consumers with no direct coupling between them.</p>
<p>You applied it inside Domain-Driven Design, giving events a proper home in a pure Dart domain layer that is framework-independent, fully portable, and fully testable.</p>
<p>And you saw how it integrates with Riverpod through a hybrid architecture with a clear and enforced rule: handlers own side effects, the notifier owns UI state, and widgets own nothing.</p>
<p>The result is a codebase that scales gracefully. When a new side effect needs to be added, you create one handler and register it in one place. Nothing else changes. That's the promise of the Observer pattern. And as you've seen throughout this handbook, it's a promise it keeps.</p>
<p>Happy Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Saga Pattern in Node.js: How to Roll Back Distributed Transactions Across Microservices ]]>
                </title>
                <description>
                    <![CDATA[ Building reliable workflows across multiple microservices is challenging. In a monolith, a database transaction can ensure that multiple operations either succeed or fail together. But once data is sp ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-saga-pattern-in-node-js-roll-back-distributed-transactions-across-microservices/</link>
                <guid isPermaLink="false">6a2cfc9713c6ff659c6c31d1</guid>
                
                    <category>
                        <![CDATA[ Microservices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PostgreSQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ rollback ]]>
                    </category>
                
                    <category>
                        <![CDATA[ idempotence ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Md Tarikul Islam ]]>
                </dc:creator>
                <pubDate>Sat, 13 Jun 2026 06:45:43 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/b0e126ec-8b90-470a-b5c0-55e5e1673731.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building reliable workflows across multiple microservices is challenging. In a monolith, a database transaction can ensure that multiple operations either succeed or fail together. But once data is spread across different services and databases, that guarantee disappears.</p>
<p>This is where the Saga Pattern comes in. Instead of using distributed transactions, a saga coordinates a sequence of local transactions and runs compensation actions when something goes wrong.</p>
<p>In this article, we'll build an orchestrated Saga Pattern using NestJS, gRPC, PostgreSQL, and Sequelize. You'll learn how to coordinate work across services, implement compensation-based rollbacks, handle idempotency, and track workflow progress in a production-style microservice architecture.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-1-introduction">1. Introduction</a></p>
</li>
<li><p><a href="#heading-2-the-problem-in-one-picture">2. The Problem in One Picture</a></p>
</li>
<li><p><a href="#heading-3-why-you-need-a-saga">3. Why You Need a Saga</a></p>
</li>
<li><p><a href="#heading-4-choreography-vs-orchestration">4. Choreography vs Orchestration</a></p>
<ul>
<li><p><a href="#heading-choreography">Choreography</a></p>
</li>
<li><p><a href="#heading-orchestration">Orchestration</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-5-the-example-project">5. The Example Project</a></p>
</li>
<li><p><a href="#heading-6-architecture">6. Architecture</a></p>
</li>
<li><p><a href="#heading-7-the-saga-flow-step-by-step">7. The Saga Flow, Step by Step</a></p>
</li>
<li><p><a href="#heading-8-the-state-machine">8. The State Machine</a></p>
</li>
<li><p><a href="#heading-9-implementing-the-orchestrator">9. Implementing the Orchestrator</a></p>
<ul>
<li><p><a href="#heading-creating-the-saga-record">Creating the Saga Record</a></p>
</li>
<li><p><a href="#heading-the-main-loop">The Main Loop</a></p>
</li>
<li><p><a href="#heading-a-single-step-in-detail">A Single Step in Detail</a></p>
</li>
<li><p><a href="#heading-habits-worth-copying">Habits Worth Copying</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-10-implementing-the-participant">10. Implementing the Participant</a></p>
</li>
<li><p><a href="#heading-11-rollback-compensation">11. Rollback (Compensation)</a></p>
<ul>
<li><p><a href="#heading-on-the-orchestrator-side">On the Orchestrator Side</a></p>
</li>
<li><p><a href="#heading-on-the-participant-side">On the Participant Side</a></p>
</li>
<li><p><a href="#heading-rules-of-a-good-compensation">Rules of a Good Compensation</a></p>
</li>
<li><p><a href="#heading-what-happens-if-the-compensation-itself-fails">What Happens if the Compensation Itself Fails?</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-12-tracking-idempotency-and-observability">12. Tracking, Idempotency and Observability</a></p>
<ul>
<li><p><a href="#heading-orchestrator-side-agency_onboarding_sagas">Orchestrator Side — agency_onboarding_sagas</a></p>
</li>
<li><p><a href="#heading-participant-side-agency_provision_records">Participant Side — agency_provision_records</a></p>
</li>
<li><p><a href="#heading-observability-for-free">Observability for Free</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-13-testing-a-saga">13. Testing a Saga</a></p>
</li>
<li><p><a href="#heading-14-when-not-to-use-a-saga">14. When NOT to Use a Saga</a></p>
</li>
<li><p><a href="#heading-15-trade-offs-and-lessons-learned">15. Trade-offs and Lessons Learned</a></p>
</li>
<li><p><a href="#heading-16-conclusion">16. Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article assumes you're already familiar with some backend development concepts. You don't need prior experience with the Saga Pattern, but you should be comfortable with:</p>
<ul>
<li><p>JavaScript, TypeScript, Node.js</p>
</li>
<li><p>NestJS fundamentals (controllers, services, dependency injection)</p>
</li>
<li><p>Basic PostgreSQL concepts</p>
</li>
<li><p>Database transactions</p>
</li>
<li><p>Docker (recommended for local development)</p>
</li>
<li><p>Microservice architecture basics</p>
</li>
<li><p>gRPC fundamentals (helpful but not required)</p>
</li>
</ul>
<p>If you've already built a few backend services with NestJS and PostgreSQL, you'll have everything you need to follow this guide.</p>
<h2 id="heading-1-introduction">1. Introduction</h2>
<p>A <strong>saga</strong> is a sequence of local transactions across multiple services. Each step commits its own database transaction. If a later step fails, the saga runs <strong>compensating transactions</strong> to semantically undo the work already committed.</p>
<p>The pattern was first described by Hector Garcia-Molina and Kenneth Salem in 1987 for long-lived database transactions. It was rediscovered a decade ago when companies started splitting monoliths into microservices and realised that the database transaction — the single most powerful tool in a backend developer's belt — stops working at the service boundary.</p>
<p>This article walks through an orchestrated saga in Node.js (NestJS + gRPC) for onboarding an agency, where two services must agree on a single business outcome:</p>
<ul>
<li><p><code>agency-service</code> — owns the agency record.</p>
</li>
<li><p><code>auth-service</code> — owns the organization, user and role.</p>
</li>
</ul>
<p>If either side fails, the system must end up as if nothing ever happened. No half-created users, orphan organizations, or 3am Slack threads.</p>
<h2 id="heading-2-the-problem-in-one-picture">2. The Problem in One Picture</h2>
<p>Here's the bug a saga is built to prevent:</p>
<pre><code class="language-plaintext">Step 1: auth-service     ✅ creates Organization #42
Step 2: auth-service     ✅ creates User #99
Step 3: agency-service   ❌ fails (DB down, validation, network blip…)

Result without a saga:
   Organization #42 and User #99 still exist.
   There is no Agency row.
   The user can log in but has nothing to manage.
   Support gets a ticket. Engineer writes a one-off SQL cleanup.
   Repeat every week.
</code></pre>
<p>The saga's job is to detect that step 3 failed and <strong>explicitly delete Organization #42 and User #99</strong>, so the system is consistent again — even though those rows live in a different service's database.</p>
<h2 id="heading-3-why-you-need-a-saga">3. Why You Need a Saga</h2>
<p>In a monolith, you wrap everything in one DB transaction and let the database handle atomicity:</p>
<pre><code class="language-ts">await sequelize.transaction(async (tx) =&gt; {
  await Organization.create({...}, { transaction: tx });
  await User.create({...}, { transaction: tx });
  await Agency.create({...}, { transaction: tx });
});
</code></pre>
<p>In microservices, each service has its own database. You can't wrap two services in one ACID transaction. The classic alternatives all have problems:</p>
<table>
<thead>
<tr>
<th>Option</th>
<th>Problem</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Two-Phase Commit (2PC)</strong></td>
<td>Locks rows across services, coordinator is a single point of failure, and doesn't scale. Most modern databases don't support it well across HTTP/gRPC.</td>
</tr>
<tr>
<td><strong>"Just hope it works"</strong></td>
<td>Leaves orphan users / billing rows when half the flow fails. Real data corruption — and the longer the system runs, the more orphans accumulate.</td>
</tr>
<tr>
<td><strong>Manual cleanup scripts</strong></td>
<td>Works for a week. Bugs hide for months. New engineers don't know they exist.</td>
</tr>
<tr>
<td><strong>Eventual consistency without compensation</strong></td>
<td>Fine for some domains (analytics) but completely wrong for billing, identity, or anything with money.</td>
</tr>
<tr>
<td><strong>Saga pattern</strong></td>
<td>Each service commits locally. The orchestrator owns the workflow and runs explicit compensation on failure. It's auditable, restartable, and reasonable.</td>
</tr>
</tbody></table>
<p>The saga gives you eventual consistency with a clear, auditable rollback path — without distributed locks.</p>
<h2 id="heading-4-choreography-vs-orchestration">4. Choreography vs Orchestration</h2>
<p>There are two ways to implement a saga:</p>
<h3 id="heading-choreography">Choreography</h3>
<p>With Choreography, services emit events and other services subscribe and react.</p>
<pre><code class="language-plaintext">auth-service → emits "UserCreated"
agency-service → listens, creates agency, emits "AgencyCreated"
billing-service → listens, creates subscription…
</code></pre>
<p>It's simple at first, but brittle later. The workflow is scattered across N codebases. Nobody owns it. Debugging means tracing events across logs. Adding a step means changing several services.</p>
<h3 id="heading-orchestration">Orchestration</h3>
<p>With Orchestration, one service is the conductor. It calls the others in order.</p>
<pre><code class="language-plaintext">orchestrator:
   1. authClient.provisionAccount(...)
   2. agencyRepo.create(...)
   3. authClient.sendWelcomeEmail(...)
</code></pre>
<p>There's slightly more coupling here (the orchestrator imports clients), but the entire workflow lives in one file. Onboarding new engineers becomes a one-hour task. Adding a step is a single PR.</p>
<p><strong>Pick orchestration unless you have a strong reason not to.</strong> This article — and the reference implementation — uses orchestration.</p>
<h2 id="heading-5-the-example-project">5. The Example Project</h2>
<p>Our goal here is to create an Agency in the system. This is the moment a new B2B customer signs up.</p>
<p>It requires two services to agree on a single outcome:</p>
<p><code>auth-service</code> <strong>must create:</strong></p>
<ul>
<li><p>an <code>Organization</code> row (the tenant)</p>
</li>
<li><p>a <code>User</code> row (the agency admin who will log in)</p>
</li>
<li><p>a <code>UserRole</code> row linking the user to the <code>AGENCY_ADMIN</code> role</p>
</li>
</ul>
<p><code>agency-service</code> <strong>must create:</strong></p>
<ul>
<li>an <code>Agency</code> row containing business details (size, registration number, website, branches…), linked to the user/organization above</li>
</ul>
<p>These rows have foreign-key relationships <em>within</em> a service, but <em>not</em> across services — Postgres can't enforce that the user in auth's DB matches the <code>authUserId</code> in agency's DB. The application has to do it.</p>
<pre><code class="language-plaintext">auth-service DB                    agency-service DB
─────────────────                  ─────────────────
organizations  ◄────────┐
   │                    │
   │ (1:1)              │   foreign reference (no FK)
   ▼                    │           agencies
users  ──────► user_roles                     ─ authUserId
                                              └ authOrganizationId
</code></pre>
<p>If step 2 fails <em>after</em> step 1 succeeded, we end up with a user who can authenticate but has no agency — the exact bug from 2. That's what the saga prevents.</p>
<h2 id="heading-6-architecture">6. Architecture</h2>
<pre><code class="language-plaintext">                     ┌───────────────────────────────┐
                     │        API Gateway            │
                     └──────────────┬────────────────┘
                                    │ HTTP
                                    ▼
   ┌──────────────────────────────────────────────────┐
   │              agency-service                      │
   │   ┌─────────────────────────────────────────┐    │
   │   │   AgencyOnboardingOrchestrator (SAGA)   │    │
   │   └───────────────┬─────────────────────────┘    │
   │                   │ writes state                 │
   │                   ▼                              │
   │      agency_onboarding_sagas  (Postgres)         │
   └───────────────┬─────────────────┬────────────────┘
                   │ gRPC            │ gRPC
       provisionAgencyAccount   compensateAgencyAccount
                   │                 │
                   ▼                 ▼
   ┌──────────────────────────────────────────────────┐
   │              auth-service                        │
   │   AgencyProvisioningService  (Participant)       │
   │                                                  │
   │   organizations · users · user_roles             │
   │   agency_provision_records  ← idempotency log    │
   └──────────────────────────────────────────────────┘
</code></pre>
<p>Three components do all the work:</p>
<ol>
<li><p><code>AgencyOnboardingOrchestrator</code> in <code>agency-service</code> — drives the workflow.</p>
</li>
<li><p><code>agency_onboarding_sagas</code> table in <code>agency-service</code> — the durable log of the saga's progress.</p>
</li>
<li><p><code>AgencyProvisioningService</code> in <code>auth-service</code> — exposes a <code>do</code> operation (<code>provisionAgencyAccount</code>) and an <code>undo</code> operation (<code>compensateAgencyAccount</code>). It's backed by its own <code>agency_provision_records</code> idempotency table.</p>
</li>
</ol>
<p>The orchestrator never reaches into the auth database directly. The boundary is enforced by gRPC.</p>
<h2 id="heading-7-the-saga-flow-step-by-step">7. The Saga Flow, Step by Step</h2>
<p>This sequence diagram shows the complete lifecycle of the onboarding saga. The workflow begins when a client sends a request to create a new agency. The orchestrator first creates a saga record in its database and marks it as <code>STARTED</code>, giving it a durable record of the workflow before any business action takes place.</p>
<p>At a high level, the orchestrator begins by creating a saga record and then asks <code>auth-service</code> to provision the organization, user, and role. Once that succeeds, the orchestrator creates the agency record in its own database.</p>
<p>If every step succeeds, the saga reaches the <code>COMPLETED</code> state. If the agency creation fails after the auth resources have already been created, the orchestrator triggers a compensation step that instructs <code>auth-service</code> to remove everything it previously provisioned.</p>
<p>The key idea is that each service commits its own local transaction, while the saga coordinates the overall business workflow and ensures the system can return to a consistent state when failures occur.</p>
<pre><code class="language-mermaid">sequenceDiagram
    autonumber
    participant C as Client
    participant AS as agency-service&lt;br/&gt;Orchestrator
    participant DB1 as saga store
    participant AU as auth-service
    participant DB2 as auth DB

    C-&gt;&gt;AS: POST /agencies
    AS-&gt;&gt;DB1: INSERT saga (STARTED, payload)
    AS-&gt;&gt;AU: provisionAgencyAccount(sagaId, …)
    AU-&gt;&gt;DB2: BEGIN TX
    AU-&gt;&gt;DB2: create org + user + role + provision_record
    AU-&gt;&gt;DB2: COMMIT
    AU--&gt;&gt;AS: { userId, organizationId, roleId }
    AS-&gt;&gt;DB1: UPDATE saga (AUTH_PROVISIONED)
    AS-&gt;&gt;AS: create Agency row
    alt Agency row OK
        AS-&gt;&gt;DB1: UPDATE saga (AGENCY_CREATED → COMPLETED)
        AS-&gt;&gt;AU: sendAgencyWelcomeEmail (non-critical)
        AS--&gt;&gt;C: 200 OK + sagaId
    else Agency row fails
        AS-&gt;&gt;DB1: UPDATE saga (COMPENSATING)
        AS-&gt;&gt;AU: compensateAgencyAccount(sagaId)
        AU-&gt;&gt;DB2: BEGIN TX
        AU-&gt;&gt;DB2: delete role + token + user + org + record
        AU-&gt;&gt;DB2: COMMIT
        AS-&gt;&gt;DB1: UPDATE saga (COMPENSATED → FAILED)
        AS--&gt;&gt;C: 5xx + error code
    end
</code></pre>
<p>Read this once top to bottom and you'll understand the entire onboarding workflow. That's the value of orchestration — the sequence diagram <em>is</em> the architecture.</p>
<h2 id="heading-8-the-state-machine">8. The State Machine</h2>
<p>Every transition is written to <code>agency_onboarding_sagas</code> <strong>before</strong> the next step runs. That is what makes the saga observable and recoverable.</p>
<pre><code class="language-ts">export enum AgencyOnboardingSagaStatus {
  STARTED            = 'STARTED',            // Row exists, no side effects yet
  AUTH_PROVISIONED   = 'AUTH_PROVISIONED',   // Auth side committed
  AGENCY_CREATED     = 'AGENCY_CREATED',     // Agency row committed
  COMPLETED          = 'COMPLETED',          // Happy-path terminal state
  COMPENSATING       = 'COMPENSATING',       // Rollback in progress
  COMPENSATED        = 'COMPENSATED',        // Rollback finished
  FAILED             = 'FAILED',             // Terminal failure (with or without compensation)
}
</code></pre>
<p>Why so many states? Because <em>"what went wrong here?"</em> is a question someone will ask at 2am. A saga that only stores <code>success | failure</code> is useless for forensics.</p>
<pre><code class="language-plaintext">                ┌── auth fails ──────────► FAILED  (nothing to compensate)
                │
STARTED ──► AUTH_PROVISIONED ──► AGENCY_CREATED ──► COMPLETED  (happy path)
                                       │
                       agency fails ───┘
                                       ▼
                                COMPENSATING
                                       │
                                       ▼
                                COMPENSATED ──► FAILED  (consistent again)
</code></pre>
<p>The “point of no return” is <code>AUTH_PROVISIONED</code>. Before it, we can fail fast — there's nothing to undo. After it, every failure path <em>must</em> go through compensation.</p>
<h2 id="heading-9-implementing-the-orchestrator">9. Implementing the Orchestrator</h2>
<p>The orchestrator is the <em>only</em> place that knows the workflow. Each step is a private method, and each step persists its result before returning.</p>
<h3 id="heading-creating-the-saga-record">Creating the Saga Record</h3>
<pre><code class="language-ts">// agency-onboarding.saga.repository.ts
async createSaga(payload: CreateAgencyOrchestrationInput) {
  return this.sagaModel.create({
    sagaId: randomUUID(),                          // correlation id for everything
    status: AgencyOnboardingSagaStatus.STARTED,
    currentStep: 'STARTED',
    payload,                                       // full input snapshot for replay
  });
}
</code></pre>
<p>The <code>sagaId</code> is a UUID generated once and <strong>propagated to every downstream call</strong>. It's the single identifier that ties the saga log on the orchestrator side to the provision record on the participant side.</p>
<h3 id="heading-the-main-loop">The Main Loop</h3>
<pre><code class="language-ts">// agency-onboarding.orchestrator.ts (trimmed for the article)
async execute(input: CreateAgencyOrchestrationInput) {
  const saga = await this.sagaRepository.createSaga(input); // STARTED

  try {
    // Step 1 — auth-service work
    const authStep = await this.provisionAuth(saga, input);
    if (!authStep.ok) {
      await this.markFailed(saga, authStep.failure); // nothing to compensate
      return authStep.failure;
    }

    // Step 2 — agency-service work
    let activeSaga = authStep.saga; // status: AUTH_PROVISIONED
    try {
      activeSaga = await this.createAgencyRow(activeSaga, input, authStep.authIds);
    } catch (err) {
      // The expensive case: undo what auth-service did
      await this.compensateAuth(activeSaga, 'SAGA_FAILED');
      const failure = mapSagaFailure(err.message, 'SAGA_FAILED', 'CREATE_AGENCY');
      await this.markFailed(activeSaga, failure);
      return failure;
    }

    // Step 3 — mark done and run non-critical side effects
    activeSaga = await this.sagaRepository.updateSaga(activeSaga, {
      status: AgencyOnboardingSagaStatus.COMPLETED,
    });
    await this.sendWelcomeEmail(input, activeSaga); // best-effort

    return mapSagaSuccess(activeSaga, await this.agencyModel.findByPk(activeSaga.agencyId!));
  } catch (error) {
    // Defensive catch-all (lost DB connection, unexpected throw)
    await this.compensateAuth(saga, 'SAGA_FAILED');
    const failure = mapSagaFailure(error.message, 'SAGA_FAILED', 'SAGA');
    await this.markFailed(saga, failure);
    return failure;
  }
}
</code></pre>
<h3 id="heading-a-single-step-in-detail">A Single Step in Detail</h3>
<pre><code class="language-ts">private async provisionAuth(saga: AgencyOnboardingSaga, input: ...) {
  this.logger.log(`[${saga.sagaId}] PROVISION_AUTH`);

  const auth = await firstValueFrom(
    this.authClient.provisionAgencyAccount({
      sagaId: saga.sagaId,                  // &lt;-- correlation
      organizationName: input.agencyName.trim(),
      email: input.email.trim().toLowerCase(),
      // …
    }),
  );

  if (!auth.status || !auth.data) {
    return { ok: false, failure: mapAuthProvisionFailure(auth) };
  }

  // Persist the IDs we will need if we have to compensate later
  const updated = await this.sagaRepository.updateSaga(saga, {
    authOrganizationId: Number(auth.data.organizationId),
    authUserId: Number(auth.data.userId),
    authUserRoleId: Number(auth.data.userRoleId),
    status: AgencyOnboardingSagaStatus.AUTH_PROVISIONED,
  });

  return { ok: true, saga: updated, authIds: auth.data };
}
</code></pre>
<p>The line that does most of the work is the <code>updateSaga</code> call. It stores the foreign IDs returned by <code>auth-service</code> on the saga row, so even if the orchestrator process crashes and restarts, a recovery job can read that row and still know what to compensate.</p>
<h3 id="heading-habits-worth-copying">Habits Worth Copying</h3>
<ul>
<li><p><strong>Persist after every successful step</strong>, including the IDs you'll need to undo it.</p>
</li>
<li><p><strong>Distinguish critical vs non-critical steps.</strong> Welcome emails, audit logs and analytics events are <em>not</em> worth rolling a saga back for. They're best-effort.</p>
</li>
<li><p><strong>One log line per transition</strong>, prefixed with <code>[${sagaId}]</code>. Grep is your debugger.</p>
</li>
</ul>
<h2 id="heading-10-implementing-the-participant">10. Implementing the Participant</h2>
<p>The participant (<code>auth-service</code>) wraps all of its own work in a local DB transaction. Inside that boundary it's still ACID — the saga only handles the cross-service problem.</p>
<pre><code class="language-ts">// agency-provisioning.service.ts (trimmed)
async provisionAgencyAccount(req: ProvisionAgencyAccountInput) {

  // 1. Idempotency — return the previous result if this sagaId already provisioned.
  const existing = await this.provisionRecordModel.findOne({
    where: { sagaId: req.sagaId },
  });
  if (existing) {
    return serviceSuccess('Agency admin already onboarded', {
      userId: Number(existing.userId),
      organizationId: Number(existing.organizationId),
      userRoleId: Number(existing.roleId),
    });
  }

  // 2. Domain validation BEFORE the transaction (fail fast).
  if (await this.emailExists(req.email)) {
    return serviceFailure('Email already exists', { code: 'EMAIL_EXISTS' });
  }
  if (await this.organizationExists(req.organizationName)) {
    return serviceFailure('Organization already exists', { code: 'ORGANIZATION_EXISTS' });
  }

  // 3. The actual work — atomic at the auth-service boundary.
  return withSequelizeTransaction(this.sequelize, async (tx) =&gt; {
    const org = await this.organizationModel.create({ ... }, { transaction: tx });
    const user = await this.userModel.create({ ..., organizationId: org.id }, { transaction: tx });
    await this.userRoleModel.create({ userId: user.id, roleId: agencyAdminRole.id }, { transaction: tx });

    // The audit record that makes compensation possible later.
    await this.provisionRecordModel.create(
      { sagaId: req.sagaId, organizationId: org.id, userId: user.id, roleId: agencyAdminRole.id },
      { transaction: tx },
    );

    return serviceSuccess('Provisioned', {
      userId: user.id, organizationId: org.id, userRoleId: agencyAdminRole.id,
    });
  });
}
</code></pre>
<p>Three things make this method "saga-safe":</p>
<ol>
<li><p><strong>Idempotency check first:</strong> If the orchestrator retries (network blip, gRPC timeout), the second call is a no-op that returns the same IDs. No duplicate users.</p>
</li>
<li><p><strong>Validation outside the transaction:</strong> Cheap reads first, expensive writes second.</p>
</li>
<li><p><strong>One transaction wraps every write:</strong> If any insert fails, the whole thing rolls back automatically. The orchestrator sees a clean failure response and knows nothing was persisted.</p>
</li>
</ol>
<p>The <code>agency_provision_records</code> table is the single most important piece of the participant. It's <strong>both</strong> the idempotency key <em>and</em> the compensation lookup — keyed by the same <code>sagaId</code> the orchestrator uses.</p>
<h2 id="heading-11-rollback-compensation">11. Rollback (Compensation)</h2>
<p>Compensation is just another gRPC call. The orchestrator sends the <code>sagaId</code> and the IDs it remembers. The participant deletes everything it created, <strong>in reverse dependency order</strong>, inside its own DB transaction.</p>
<h3 id="heading-on-the-orchestrator-side">On the Orchestrator Side</h3>
<pre><code class="language-ts">private async compensateAuth(saga: AgencyOnboardingSaga, errorCode?: string) {
  if (!saga.authUserId &amp;&amp; !saga.authOrganizationId) {
    // Nothing was provisioned — nothing to compensate.
    return;
  }

  // Mark the saga as compensating BEFORE the call, so the row is consistent
  // even if the compensating RPC times out.
  await this.sagaRepository.updateSaga(saga, {
    status: AgencyOnboardingSagaStatus.COMPENSATING,
    currentStep: 'COMPENSATING',
    errorCode,
  });

  try {
    const rollback = await firstValueFrom(this.authClient.compensateAgencyAccount({
      sagaId: saga.sagaId,
      organizationId: saga.authOrganizationId,
      userId: saga.authUserId,
    }));
    if (!rollback.status) {
      this.logger.error(`[\({saga.sagaId}] Auth compensation returned failure: \){rollback.message}`);
    }
  } catch (err) {
    this.logger.error(`[\({saga.sagaId}] Auth compensation RPC failed: \){err.message}`);
  }

  await this.sagaRepository.updateSaga(saga, {
    status: AgencyOnboardingSagaStatus.COMPENSATED,
    currentStep: 'COMPENSATED',
  });
}
</code></pre>
<h3 id="heading-on-the-participant-side">On the Participant Side</h3>
<pre><code class="language-ts">private async rollbackProvisionedAuth(req, sagaId: string, tx: Transaction) {
  // Use the saga log as the source of truth — even if the caller forgot IDs.
  const record = await this.provisionRecordModel.findOne({
    where: { sagaId }, transaction: tx,
  });
  const userId         = req.userId         ?? record?.userId;
  const organizationId = req.organizationId ?? record?.organizationId;

  if (userId) {
    const user = await this.userModel.findByPk(userId, { transaction: tx, attributes: ['email'] });
    await this.userRoleModel.destroy({ where: { userId }, transaction: tx });
    if (user?.email) {
      await this.passwordResetTokenModel.destroy({ where: { email: user.email }, transaction: tx });
    }
    await this.userModel.destroy({ where: { id: userId }, transaction: tx });
  }
  if (organizationId) {
    await this.organizationModel.destroy({ where: { id: organizationId }, transaction: tx });
  }
  if (record) {
    await record.destroy({ transaction: tx });
  }
}
</code></pre>
<h3 id="heading-rules-of-a-good-compensation">Rules of a Good Compensation</h3>
<ol>
<li><p><strong>Reverse the order of creation:</strong> Children first (user_roles, tokens), then parents (users, organizations). The same rule you follow for <code>DROP TABLE</code> statements.</p>
</li>
<li><p><strong>Be idempotent:</strong> Receiving the same <code>sagaId</code> twice must be safe — every <code>destroy</code> is a no-op if the row is already gone.</p>
</li>
<li><p><strong>Use the saga log, not just the request:</strong> If the caller forgets an ID or sends a partial payload, look it up by <code>sagaId</code>. Defence in depth.</p>
</li>
<li><p><strong>Wrap it in a local transaction:</strong> The rollback must itself be atomic — half-undone is worse than not-undone.</p>
</li>
<li><p><strong>Always close the loop on the orchestrator side:</strong> Mark <code>COMPENSATED</code> even if the RPC failed. The failure should also be surfaced (log, metric, alert). A stuck <code>COMPENSATING</code> row is an operational landmine.</p>
</li>
</ol>
<h3 id="heading-what-happens-if-the-compensation-itself-fails">What Happens if the Compensation Itself Fails?</h3>
<p>This is the worst case in any saga design. There are three reasonable strategies:</p>
<p>First, you can retry with exponential backoff. This works for transient failures (network, deadlocks).</p>
<p>Second, you can dead-letter the saga — write it to a "needs human attention" queue and alert.</p>
<p>Third, you can expose a manual rollback endpoint. This reference implementation does that via <code>RollbackAgencyOnboarding</code> gRPC, so an operator can replay compensation with the same <code>sagaId</code>.</p>
<p>A production system should combine all three. The pattern doesn't decide for you. <em>You</em> decide based on your business risk.</p>
<h2 id="heading-12-tracking-idempotency-and-observability">12. Tracking, Idempotency and Observability</h2>
<p>Two tables, both keyed by the same UUID <code>sagaId</code>, give you full traceability across services.</p>
<h3 id="heading-orchestrator-side-agencyonboardingsagas">Orchestrator Side — <code>agency_onboarding_sagas</code></h3>
<table>
<thead>
<tr>
<th>column</th>
<th>purpose</th>
</tr>
</thead>
<tbody><tr>
<td><code>sagaId</code> (UUID, unique)</td>
<td>Propagated to every RPC. The join key across services.</td>
</tr>
<tr>
<td><code>status</code></td>
<td>Current state in the state machine.</td>
</tr>
<tr>
<td><code>currentStep</code></td>
<td>Human-readable label for dashboards (<code>PROVISION_AUTH</code>, <code>CREATE_AGENCY</code>…).</td>
</tr>
<tr>
<td><code>payload</code> (JSONB)</td>
<td>Snapshot of the input — used for replay, debug, support.</td>
</tr>
<tr>
<td><code>authOrganizationId</code>, <code>authUserId</code>, <code>authUserRoleId</code></td>
<td>Foreign IDs needed for compensation.</td>
</tr>
<tr>
<td><code>agencyId</code></td>
<td>Set once the agency row exists.</td>
</tr>
<tr>
<td><code>errorCode</code>, <code>errorMessage</code></td>
<td>Filled on failure.</td>
</tr>
<tr>
<td><code>createdAt</code>, <code>updatedAt</code></td>
<td>Timeline for the saga.</td>
</tr>
</tbody></table>
<p>A real row in <code>COMPLETED</code> state looks roughly like this:</p>
<pre><code class="language-json">{
  "sagaId": "0a4f3e2c-7b11-4f8d-9a2c-90b6f5f5b8a1",
  "status": "COMPLETED",
  "currentStep": "COMPLETED",
  "agencyId": 17,
  "authOrganizationId": 42,
  "authUserId": 99,
  "authUserRoleId": 3,
  "errorCode": null,
  "errorMessage": null,
  "payload": { "agencyName": "Acme Education", "email": "admin@acme.com", "...": "..." },
  "createdAt": "2026-05-22T10:14:32.118Z",
  "updatedAt": "2026-05-22T10:14:33.412Z"
}
</code></pre>
<h3 id="heading-participant-side-agencyprovisionrecords">Participant Side — <code>agency_provision_records</code></h3>
<table>
<thead>
<tr>
<th>column</th>
<th>purpose</th>
</tr>
</thead>
<tbody><tr>
<td><code>sagaId</code> (unique)</td>
<td>Idempotency key. The same <code>sagaId</code> from the orchestrator.</td>
</tr>
<tr>
<td><code>userId</code>, <code>organizationId</code>, <code>roleId</code></td>
<td>What to delete on compensation.</td>
</tr>
<tr>
<td><code>createdAt</code>, <code>updatedAt</code></td>
<td>Audit timestamps.</td>
</tr>
</tbody></table>
<h3 id="heading-observability-for-free">Observability for Free</h3>
<p>Because every log line is prefixed with <code>[${sagaId}]</code>, a single grep across both services gives the full timeline:</p>
<pre><code class="language-plaintext">[0a4f3e2c…] PROVISION_AUTH                  agency-service
[0a4f3e2c…] provisionAgencyAccount: ok      auth-service
[0a4f3e2c…] CREATE_AGENCY                   agency-service
[0a4f3e2c…] Agency step failed: ...         agency-service
[0a4f3e2c…] Auth compensation completed     auth-service
</code></pre>
<p>In a structured-logging setup (Loki, Elasticsearch, Datadog) this becomes a one-click filter. <strong>The</strong> <code>sagaId</code> <strong>is your distributed trace.</strong></p>
<h2 id="heading-13-testing-a-saga">13. Testing a Saga</h2>
<p>A saga is just a state machine, so the test matrix is finite and small. Cover at least these cases:</p>
<table>
<thead>
<tr>
<th>#</th>
<th>Scenario</th>
<th>Expected end state</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Happy path</td>
<td><code>COMPLETED</code>, agency exists, user exists</td>
</tr>
<tr>
<td>2</td>
<td>Auth step fails (e.g. email exists)</td>
<td><code>FAILED</code>, no rows on either side</td>
</tr>
<tr>
<td>3</td>
<td>Agency step fails</td>
<td><code>COMPENSATED</code>, auth rows gone, no agency</td>
</tr>
<tr>
<td>4</td>
<td>Compensation RPC times out</td>
<td><code>COMPENSATING</code> → operator-driven recovery</td>
</tr>
<tr>
<td>5</td>
<td>Caller retries with the same <code>sagaId</code></td>
<td>Second call returns the first call's result; no duplicate rows</td>
</tr>
<tr>
<td>6</td>
<td>Welcome email fails</td>
<td><code>COMPLETED</code> still — non-critical step did not cascade</td>
</tr>
</tbody></table>
<p>Two practical tips for testing:</p>
<p>First, mock the gRPC client at the orchestrator level, not the network. You want to assert that <code>compensateAgencyAccount</code> <em>was called with the right</em> <code>sagaId</code>, not that bytes hit a socket.</p>
<p>Second, spin up a real Postgres in integration tests (Testcontainers, or a Docker Compose <code>postgres</code> service). The saga state machine is too easy to "test" against a mock and too easy to break against a real DB.</p>
<h2 id="heading-14-when-not-to-use-a-saga">14. When NOT to Use a Saga</h2>
<p>Sagas are not free. Skip them when:</p>
<ul>
<li><p><strong>One service does all the writes.</strong> Use a regular DB transaction. Don't reinvent the wheel.</p>
</li>
<li><p><strong>The workflow is read-only or analytical.</strong> No rollback semantics exist for a SELECT.</p>
</li>
<li><p><strong>The "rollback" is impossible.</strong> You sent a real email. You charged a credit card and the gateway doesn't support refunds. In those cases, design forward: send an apology email, queue a manual refund. Sagas can't unsend physical actions.</p>
</li>
<li><p><strong>You don't actually have multiple services yet.</strong> A saga in a monolith is over-engineering. Wait until the service boundary is real.</p>
</li>
</ul>
<p>A saga adds a state table, a compensation method per step, and an operational habit of grepping by <code>sagaId</code>. That cost is worth paying when the alternative is orphaned data — and not before.</p>
<h2 id="heading-15-trade-offs-and-lessons-learned">15. Trade-offs and Lessons Learned</h2>
<p>Things that worked well in this design:</p>
<ul>
<li><p>Synchronous orchestration is easier to debug than choreography. A new engineer reads one file and understands the whole flow.</p>
</li>
<li><p>Idempotency at the participant is non-negotiable. Retries from the orchestrator must be safe. Build it in from day one — retro-fitting is painful.</p>
</li>
<li><p>The saga table replaces tribal knowledge. Ops can answer <em>"what happened to this signup?"</em> with a single SQL query. The payload JSONB is gold during incidents.</p>
</li>
<li><p><code>sagaId</code> as the trace key plays nicely with OpenTelemetry / Datadog / Loki — no extra infra to set up.</p>
</li>
</ul>
<p>Things to know before copying this pattern:</p>
<ul>
<li><p>A failing compensation is the worst case. If <code>compensateAgencyAccount</code> itself errors, you have inconsistent state. Plan for retries + dead-letter + a manual rollback endpoint from the start.</p>
</li>
<li><p>Non-critical steps must be marked explicitly. Here, the welcome email is allowed to fail without rolling back the agency. Don't accidentally compensate over a flaky SMTP provider.</p>
</li>
<li><p>Sagas aren't a replacement for local transactions. Inside each service, still use a real DB transaction. The saga only handles the cross-service seam.</p>
</li>
<li><p>Synchronous gRPC is simple but couples availability. If <code>auth-service</code> is down, agency creation fails. Swap the gRPC calls for a durable message bus (RabbitMQ / Kafka) and treat each step as a command + reply when you need higher resilience.</p>
</li>
<li><p>The orchestrator becomes a critical service. Treat its uptime accordingly — monitor saga durations, alert on stuck <code>COMPENSATING</code> rows, and run more than one replica.</p>
</li>
</ul>
<h2 id="heading-16-conclusion">16. Conclusion</h2>
<p>The saga pattern isn't magic. It's a disciplined version of what experienced engineers already do by hand: <em>commit locally, record what you did, and know how to undo it.</em></p>
<p>In Node.js with NestJS, you only need three ingredients:</p>
<ol>
<li><p><strong>A state table</strong> to track the saga.</p>
</li>
<li><p><strong>An orchestrator</strong> that drives the workflow and writes that state.</p>
</li>
<li><p><strong>A participant</strong> that exposes a <code>do</code> and an <code>undo</code> operation, both idempotent and keyed by <code>sagaId</code>.</p>
</li>
</ol>
<p>Get those three right and your microservices can offer the same "all-or-nothing" feel as a monolithic transaction — without the operational pain of distributed locks.</p>
<p>Start simple, use orchestration, make every step idempotent, persist before you call, and always know how to undo. That's the whole pattern.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the Command Pattern in Python ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever used an undo button in an app or scheduled tasks to run later? Both of these rely on the same idea: turning actions into objects. That's the command pattern. Instead of calling a method  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-command-pattern-in-python/</link>
                <guid isPermaLink="false">69c1abb330a9b81e3aa82e36</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Mon, 23 Mar 2026 21:08:03 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/85170982-e7e8-453a-9fd4-a7f2f4f7edb3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever used an undo button in an app or scheduled tasks to run later? Both of these rely on the same idea: <strong>turning actions into objects</strong>.</p>
<p>That's the command pattern. Instead of calling a method directly, you package the call – the action, its target, and any arguments – into an object. That object can be stored, passed around, executed later, or undone.</p>
<p>In this tutorial, you'll learn what the command pattern is and how to implement it in Python with a practical text editor example that supports undo.</p>
<p>You can find the code for this tutorial <a href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/command">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Basic understanding of Python classes and methods</p>
</li>
<li><p>Familiarity with object-oriented programming (OOP) concepts</p>
</li>
</ul>
<p>Let's get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-the-command-pattern">What Is the Command Pattern?</a></p>
</li>
<li><p><a href="#heading-setting-up-the-receiver">Setting Up the Receiver</a></p>
</li>
<li><p><a href="#heading-defining-commands">Defining Commands</a></p>
</li>
<li><p><a href="#heading-the-invoker-running-and-undoing-commands">The Invoker: Running and Undoing Commands</a></p>
</li>
<li><p><a href="#heading-putting-it-all-together">Putting It All Together</a></p>
</li>
<li><p><a href="#heading-when-to-use-the-command-pattern">When to Use the Command Pattern</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-command-pattern">What Is the Command Pattern?</h2>
<p>The <strong>command pattern</strong> is a behavioral design pattern that encapsulates a request as an object. This lets you:</p>
<ul>
<li><p><strong>Parameterize</strong> callers with different operations</p>
</li>
<li><p><strong>Queue or schedule</strong> operations for later execution</p>
</li>
<li><p><strong>Support undo/redo</strong> by keeping a history of executed commands</p>
</li>
</ul>
<p>The pattern has four key participants:</p>
<ul>
<li><p><strong>Command</strong>: an interface with an <code>execute()</code> method (and optionally <code>undo()</code>)</p>
</li>
<li><p><strong>Concrete Command</strong>: implements <code>execute()</code> and <code>undo()</code> for a specific action</p>
</li>
<li><p><strong>Receiver</strong>: the object that actually does the work (for example, a document)</p>
</li>
<li><p><strong>Invoker</strong>: triggers commands and manages history</p>
</li>
</ul>
<p>Think of a restaurant. The customer (client) tells the waiter (invoker) what they want. The waiter writes it on a ticket (command) and hands it to the kitchen (receiver). The waiter doesn't cook – they only manage tickets. If you change your mind, the waiter can cancel the ticket before it reaches the kitchen.</p>
<h2 id="heading-setting-up-the-receiver">Setting Up the Receiver</h2>
<p>We'll build a simple document editor. The <strong>receiver</strong> here is the <code>Document</code> class. It knows how to insert and delete text, but it has no idea who's calling it or why.</p>
<pre><code class="language-python">class Document:
    def __init__(self):
        self.content = ""

    def insert(self, text: str, position: int) -&gt; None:
        self.content = (
            self.content[:position] + text + self.content[position:]
        )

    def delete(self, position: int, length: int) -&gt; None:
        self.content = (
            self.content[:position] + self.content[position + length:]
        )

    def show(self) -&gt; None:
        print(f'Document: "{self.content}"')
</code></pre>
<p><code>insert</code> places text at a given position. <code>delete</code> removes <code>length</code> characters from a given position. Both are plain methods with no history or awareness of commands. And that's intentional.</p>
<h2 id="heading-defining-commands">Defining Commands</h2>
<p>Now let's define a base <code>Command</code> interface using an abstract class:</p>
<pre><code class="language-python">from abc import ABC, abstractmethod

class Command(ABC):
    @abstractmethod
    def execute(self) -&gt; None:
        pass

    @abstractmethod
    def undo(self) -&gt; None:
        pass
</code></pre>
<p>Any concrete command must implement both <code>execute</code> and <code>undo</code>. This is what makes a full history possible.</p>
<h3 id="heading-insertcommand"><code>InsertCommand</code></h3>
<p><code>InsertCommand</code> stores the text and position at creation time:</p>
<pre><code class="language-python">class InsertCommand(Command):
    def __init__(self, document: Document, text: str, position: int):
        self.document = document
        self.text = text
        self.position = position

    def execute(self) -&gt; None:
        self.document.insert(self.text, self.position)

    def undo(self) -&gt; None:
        self.document.delete(self.position, len(self.text))
</code></pre>
<p>When <code>execute()</code> is called, it inserts the text. When <code>undo()</code> is called, it deletes exactly what was inserted. Notice that <code>undo</code> is the inverse of <code>execute</code> – this is the key design requirement.</p>
<h3 id="heading-deletecommand"><code>DeleteCommand</code></h3>
<p>Now let's code the <code>DeleteCommand</code>:</p>
<pre><code class="language-python">class DeleteCommand(Command):
    def __init__(self, document: Document, position: int, length: int):
        self.document = document
        self.position = position
        self.length = length
        self._deleted_text = ""  # stored on execute, used on undo

    def execute(self) -&gt; None:
        self._deleted_text = self.document.content[
            self.position : self.position + self.length
        ]
        self.document.delete(self.position, self.length)

    def undo(self) -&gt; None:
        self.document.insert(self._deleted_text, self.position)
</code></pre>
<p><code>DeleteCommand</code> has one important detail: it captures the deleted text <em>during</em> <code>execute()</code>, not at creation time. This is because we don't know what text is at that position until the command actually runs. Without this, <code>undo()</code> wouldn't know what to restore.</p>
<h2 id="heading-the-invoker-running-and-undoing-commands">The Invoker: Running and Undoing Commands</h2>
<p>The <strong>invoker</strong> is the object that executes commands and keeps a history stack. It has no idea what a document is or how text editing works. It just manages command objects.</p>
<pre><code class="language-python">class EditorInvoker:
    def __init__(self):
        self._history: list[Command] = []

    def run(self, command: Command) -&gt; None:
        command.execute()
        self._history.append(command)

    def undo(self) -&gt; None:
        if not self._history:
            print("Nothing to undo.")
            return
        command = self._history.pop()
        command.undo()
        print("Undo successful.")
</code></pre>
<p><code>run()</code> executes the command and pushes it onto the history stack. <code>undo()</code> pops the last command and calls its <code>undo()</code> method. The stack naturally gives you the right order: last in, first undone.</p>
<h2 id="heading-putting-it-all-together">Putting It All Together</h2>
<p>Let's put it all together and walk through a real editing session:</p>
<pre><code class="language-python">doc = Document()
editor = EditorInvoker()

# Type a title
editor.run(InsertCommand(doc, "Quarterly Report", 0))
doc.show()

# Add a subtitle
editor.run(InsertCommand(doc, " - Finance", 16))
doc.show()

# Oops, wrong subtitle — undo it
editor.undo()
doc.show()

# Delete "Quarterly" and replace with "Annual"
editor.run(DeleteCommand(doc, 0, 9))
doc.show()

editor.run(InsertCommand(doc, "Annual", 0))
doc.show()

# Undo the insert
editor.undo()
doc.show()

# Undo the delete (restores "Quarterly")
editor.undo()
doc.show()
</code></pre>
<p>This outputs:</p>
<pre><code class="language-plaintext">Document: "Quarterly Report"
Document: "Quarterly Report - Finance"
Undo successful.
Document: "Quarterly Report"
Document: " Report"
Document: "Annual Report"
Undo successful.
Document: " Report"
Undo successful.
Document: "Quarterly Report"
</code></pre>
<p>Here's the step-by-step breakdown of how (and why) this works:</p>
<ul>
<li><p>Each <code>InsertCommand</code> and <code>DeleteCommand</code> carries its own instructions for both doing and undoing.</p>
</li>
<li><p><code>EditorInvoker</code> never looks inside a command. It only calls <code>execute()</code> and <code>undo()</code>.</p>
</li>
<li><p>The document (<code>Document</code>) never thinks about history. It mutates its content when told to.</p>
</li>
</ul>
<p>Each participant has a single, clear responsibility.</p>
<h2 id="heading-extending-with-macros">Extending with Macros</h2>
<p>One of the lesser-known benefits of the command pattern is that commands are just objects. So you can group them. Here's a <code>MacroCommand</code> that batches several commands and undoes them as a unit:</p>
<pre><code class="language-python">class MacroCommand(Command):
    def __init__(self, commands: list[Command]):
        self.commands = commands

    def execute(self) -&gt; None:
        for cmd in self.commands:
            cmd.execute()

    def undo(self) -&gt; None:
        for cmd in reversed(self.commands):
            cmd.undo()

# Apply a heading format in one shot: clear content, insert formatted title
macro = MacroCommand([
    DeleteCommand(doc, 0, len(doc.content)),
    InsertCommand(doc, "== Annual Report ==", 0),
])

editor.run(macro)
doc.show()

editor.undo()
doc.show()
</code></pre>
<p>This gives the following output:</p>
<pre><code class="language-plaintext">Document: "== Annual Report =="
Undo successful.
Document: "Quarterly Report"
</code></pre>
<p>The macro undoes its commands in reverse order. This is correct since the last thing done should be the first thing undone.</p>
<h2 id="heading-when-to-use-the-command-pattern">When to Use the Command Pattern</h2>
<p>The command pattern is a good fit when:</p>
<ul>
<li><p><strong>You need undo/redo</strong>: the pattern is practically made for this. Store executed commands in a stack and reverse them.</p>
</li>
<li><p><strong>You need to queue or schedule operations</strong>: commands are objects, so you can put them in a queue, serialize them, or delay execution.</p>
</li>
<li><p><strong>You want to decouple the caller from the action</strong>: the invoker doesn't need to know what the command does. It just runs it.</p>
</li>
<li><p><strong>You need to support macros or batched operations</strong>: group commands into a composite and run them together, as shown above.</p>
</li>
</ul>
<p>Avoid it when:</p>
<ul>
<li><p>The operations are simple and will never need undo or queuing. The pattern adds classes and indirection that may not be worth it for a simple CRUD action.</p>
</li>
<li><p>Commands would need to share so much state that the "encapsulate the request" idea breaks down.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you found this tutorial useful. To summarize, the command pattern turns actions into objects. And that single idea unlocks a lot: undo/redo, queuing, macros, and clean separation between who triggers an action and what the action does.</p>
<p>We built a document editor from scratch using <code>InsertCommand</code>, <code>DeleteCommand</code>, an <code>EditorInvoker</code> with a history stack, and a <code>MacroCommand</code> for batched edits. Each class knew exactly one thing and did it well.</p>
<p>As a next step, try extending the editor with a <code>RedoCommand</code>. You'll need a second stack alongside the history to bring back undone commands.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement the Strategy Pattern in Python ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever opened a food delivery app and chosen between "fastest route", "cheapest option", or "fewest stops"? Or picked a payment method at checkout like credit card, PayPal, or wallet balance? B ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-the-strategy-pattern-in-python/</link>
                <guid isPermaLink="false">69b1d33d6c896b0519c3abdc</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Wed, 11 Mar 2026 20:40:29 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/8298ed99-c958-4b98-821e-ae43496b85af.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever opened a food delivery app and chosen between "fastest route", "cheapest option", or "fewest stops"? Or picked a payment method at checkout like credit card, PayPal, or wallet balance? Behind both of these, there's a good chance the <strong>strategy pattern</strong> is at work.</p>
<p>The strategy pattern lets you define a family of algorithms, put each one in its own class, and make them interchangeable at runtime. Instead of writing a giant <code>if/elif</code> chain every time behavior needs to change, you swap in the right strategy for the job.</p>
<p>In this tutorial, you'll learn what the strategy pattern is, why it's useful, and how to implement it in Python with practical examples.</p>
<p>You can get the code <a href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/strategy">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Basic understanding of Python classes and methods</p>
</li>
<li><p>Familiarity with object-oriented programming (OOP) concepts</p>
</li>
</ul>
<p>Let's get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-the-strategy-pattern">What Is the Strategy Pattern?</a></p>
</li>
<li><p><a href="#heading-a-simple-strategy-pattern-example">A Simple Strategy Pattern Example</a></p>
</li>
<li><p><a href="#heading-swapping-strategies-at-runtime">Swapping Strategies at Runtime</a></p>
</li>
<li><p><a href="#heading-using-abstract-base-classes">Using Abstract Base Classes</a></p>
</li>
<li><p><a href="#heading-when-to-use-the-strategy-pattern">When to Use the Strategy Pattern</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-strategy-pattern">What Is the Strategy Pattern?</h2>
<p>The <strong>strategy pattern</strong> defines a way to encapsulate a group of related algorithms so they can be used interchangeably. The object that uses the algorithm, called the <strong>context</strong>, doesn't need to know how it works. It just delegates the work to whichever strategy is currently set.</p>
<p>Think of it like a GPS app. The destination is the same, but you can switch between "avoid highways", "shortest distance", or "least traffic" without changing the destination or the app itself. Each routing option is a separate strategy.</p>
<p>The pattern is useful when:</p>
<ul>
<li><p>You have multiple variations of an algorithm or behavior</p>
</li>
<li><p>You want to eliminate long <code>if/elif</code> conditionals based on type</p>
</li>
<li><p>You want to swap behavior at runtime without changing the context class</p>
</li>
<li><p>Different parts of your app need different variations of the same operation</p>
</li>
</ul>
<p>Now let's look at examples to understand this better.</p>
<h2 id="heading-a-simple-strategy-pattern-example">A Simple Strategy Pattern Example</h2>
<p>Let's build a simple e-commerce order system where different discount strategies can be applied at checkout.</p>
<p>First, let's create the three discount strategies:</p>
<pre><code class="language-python">class RegularDiscount:
    def apply(self, price):
        return price * 0.95  # 5% off

class SeasonalDiscount:
    def apply(self, price):
        return price * 0.80  # 20% off

class NoDiscount:
    def apply(self, price):
        return price  # no change
</code></pre>
<p>Each class has a single <code>apply</code> method that takes a price and returns the discounted price. They <strong>share the same interface but implement different logic</strong>: that's the key concept in the strategy pattern.</p>
<p>Now let's create the <code>Order</code> class that uses one of these strategies:</p>
<pre><code class="language-python">class Order:
    def __init__(self, product, price, discount_strategy):
        self.product = product
        self.price = price
        self.discount_strategy = discount_strategy

    def final_price(self):
        return self.discount_strategy.apply(self.price)

    def summary(self):
        print(f"Product : {self.product}")
        print(f"Original: ${self.price:.2f}")
        print(f"Final   : ${self.final_price():.2f}")
        print("-" * 30)
</code></pre>
<p>The <code>Order</code> class is our <strong>context</strong>. It doesn't contain any discount logic itself – it delegates that entirely to <code>discount_strategy.apply()</code>. Whichever strategy object you pass in, that's the one that runs.</p>
<p>Now let's place some orders:</p>
<pre><code class="language-python">order1 = Order("Mechanical Keyboard", 120.00, NoDiscount())
order2 = Order("Laptop Stand", 45.00, RegularDiscount())
order3 = Order("USB-C Hub", 35.00, SeasonalDiscount())

order1.summary()
order2.summary()
order3.summary()
</code></pre>
<p>Running the above code should give you the following output:</p>
<pre><code class="language-plaintext">Product : Mechanical Keyboard
Original: $120.00
Final   : $120.00
------------------------------
Product : Laptop Stand
Original: $45.00
Final   : $42.75
------------------------------
Product : USB-C Hub
Original: $35.00
Final   : $28.00
------------------------------
</code></pre>
<p>Notice how <code>Order</code> never checks <code>if discount_type == "seasonal"</code>. It just calls <code>apply()</code> and trusts the strategy to handle it. Adding a new discount type in the future means creating one new class and nothing else changes.</p>
<h2 id="heading-swapping-strategies-at-runtime">Swapping Strategies at Runtime</h2>
<p>One of the biggest advantages of the strategy pattern is that you can change the strategy while the program is running. Let's say a user upgrades to a premium membership mid-session:</p>
<pre><code class="language-python">class ShoppingCart:
    def __init__(self):
        self.items = []
        self.discount_strategy = NoDiscount()  # default

    def add_item(self, name, price):
        self.items.append({"name": name, "price": price})

    def set_discount(self, strategy):
        self.discount_strategy = strategy
        print(f"Discount updated to: {strategy.__class__.__name__}")

    def checkout(self):
        print("\n--- Checkout Summary ---")
        total = 0
        for item in self.items:
            discounted = self.discount_strategy.apply(item["price"])
            print(f"{item['name']}: ${discounted:.2f}")
            total += discounted
        print(f"Total: ${total:.2f}\n")
</code></pre>
<p>The <code>set_discount</code> method lets us replace the strategy at any point. Let's see it in action:</p>
<pre><code class="language-python">cart = ShoppingCart()
cart.add_item("Notebook", 15.00)
cart.add_item("Desk Lamp", 40.00)
cart.add_item("Monitor Riser", 25.00)

# Checkout as a regular customer
cart.checkout()

# User upgrades to seasonal sale membership
cart.set_discount(SeasonalDiscount())
cart.checkout()
</code></pre>
<p>This outputs:</p>
<pre><code class="language-plaintext">--- Checkout Summary ---
Notebook: $15.00
Desk Lamp: $40.00
Monitor Riser: $25.00
Total: $80.00

Discount updated to: SeasonalDiscount

--- Checkout Summary ---
Notebook: $12.00
Desk Lamp: $32.00
Monitor Riser: $20.00
Total: $64.00
</code></pre>
<p>The cart itself didn't change – only the strategy did. This is the advantage of keeping <em>behavior</em> separate from the <em>context</em> that uses it.</p>
<h2 id="heading-using-abstract-base-classes">Using Abstract Base Classes</h2>
<p>So far, nothing enforces that every strategy has an <code>apply</code> method. If someone creates a strategy and forgets it, they'll get a cryptic <code>AttributeError</code> at runtime. We can prevent that using <a href="https://docs.python.org/3/library/abc.html">Python's Abstract Base Classes</a>.</p>
<pre><code class="language-python">from abc import ABC, abstractmethod

class DiscountStrategy(ABC):
    @abstractmethod
    def apply(self, price: float) -&gt; float:
        pass
</code></pre>
<p>Now let's rewrite our strategies to inherit from it:</p>
<pre><code class="language-python">class RegularDiscount(DiscountStrategy):
    def apply(self, price):
        return price * 0.95

class SeasonalDiscount(DiscountStrategy):
    def apply(self, price):
        return price * 0.80

class NoDiscount(DiscountStrategy):
    def apply(self, price):
        return price
</code></pre>
<p>Now if someone creates a broken strategy without <code>apply</code>, Python will raise a <code>TypeError</code> immediately when they try to instantiate it — before any code runs. That's a much cleaner failure.</p>
<pre><code class="language-python">class BrokenStrategy(DiscountStrategy):
    pass  # forgot to implement apply()

s = BrokenStrategy()  # raises TypeError right here
</code></pre>
<p>Using ABCs is especially helpful on larger teams or in shared codebases, where you want to make the contract explicit: every strategy <em>must</em> implement <code>apply</code>. Else, you run into an error as shown.</p>
<pre><code class="language-plaintext">      2     pass  # forgot to implement apply()
      3 
----&gt; 4 s = BrokenStrategy()  # raises TypeError right here

TypeError: Can't instantiate abstract class BrokenStrategy without an implementation for abstract method 'apply'
</code></pre>
<h2 id="heading-when-to-use-the-strategy-pattern">When to Use the Strategy Pattern</h2>
<p>The Strategy pattern is a good fit when:</p>
<ul>
<li><p>You have branching logic based on type — long <code>if/elif</code> blocks that check a "mode" or "type" variable are a signal that Strategy might help.</p>
</li>
<li><p>Behavior needs to change at runtime — when users or config values should be able to switch algorithms without restarting.</p>
</li>
<li><p>You're building extensible systems — new behavior can be added as a new class without touching existing code.</p>
</li>
<li><p>You want to test algorithms independently — each strategy is its own class, making unit tests straightforward.</p>
</li>
</ul>
<p>Avoid it when:</p>
<ul>
<li><p>You only have two variations that will never grow — a simple <code>if/else</code> is perfectly fine there.</p>
</li>
<li><p>The strategies share so much state that separating them into classes adds complexity without benefit.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you found this tutorial useful. To sum up, the strategy pattern gives you a clean way to manage varying behavior without polluting your classes with conditional logic. The context stays simple and stable and the strategies handle the complexity.</p>
<p>We covered the basic pattern, runtime strategy swapping, and enforcing contracts with abstract base classes. As with most design patterns, start simple: even without ABCs, separating your algorithms into their own classes immediately makes your code easier to read, test, and extend.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement the Observer Pattern in Python ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever wondered how YouTube notifies you when your favorite channel uploads a new video? Or how your email client alerts you when new messages arrive? These are perfect examples of the observer pattern in action. The observer pattern is a desi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-the-observer-pattern-in-python/</link>
                <guid isPermaLink="false">6994c4a494993ba9dd1ad6ad</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Tue, 17 Feb 2026 19:42:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771357332246/45dc3900-04d9-474e-91a8-bac2fec86c2c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever wondered how YouTube notifies you when your favorite channel uploads a new video? Or how your email client alerts you when new messages arrive? These are perfect examples of the observer pattern in action.</p>
<p>The observer pattern is a design pattern where an object (called the subject) maintains a list of dependents (called observers) and notifies them automatically when its state changes. It's like having a newsletter subscription: when new content is published, all subscribers get notified.</p>
<p>In this tutorial, you'll learn what the observer pattern is, why it's useful, and how to implement it in Python with practical examples.</p>
<p>You can find the code <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/observer">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Understanding of how Python classes and methods work</p>
</li>
<li><p>Familiarity with object-oriented programming (OOP) concepts</p>
</li>
</ul>
<p>Let's get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-observer-pattern">What Is the Observer Pattern?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-simple-observer-pattern-example">A Simple Observer Pattern Example</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-handling-unsubscribes">Handling Unsubscribes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-different-types-of-observers">Different Types of Observers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-abstract-base-classes">Using Abstract Base Classes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-the-observer-pattern">When to Use the Observer Pattern</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-observer-pattern">What Is the Observer Pattern?</h2>
<p>The observer pattern defines a <a target="_blank" href="https://en.wikipedia.org/wiki/One-to-many_\(data_model\)">one-to-many relationship</a> between objects. <strong>When one object changes state, all its dependents are notified and updated automatically</strong>.</p>
<p>Think of it like a news agency and reporters. When breaking news happens (the subject), the agency notifies all subscribed reporters (observers) immediately. Each reporter can then handle the news in their own way – some might tweet it, others might write articles, and some might broadcast it on TV.</p>
<p>The pattern is useful when:</p>
<ul>
<li><p>You need to notify multiple objects about state changes</p>
</li>
<li><p>You want loose coupling between objects</p>
</li>
<li><p>You don't know how many objects need to be notified in advance</p>
</li>
<li><p>Objects should be able to subscribe and unsubscribe dynamically</p>
</li>
</ul>
<h2 id="heading-a-simple-observer-pattern-example">A Simple Observer Pattern Example</h2>
<p>Let's start with a basic example: a blog that notifies readers when a new article is published.</p>
<p>We'll create a blog (subject) and email subscribers (observers) who get notified automatically when new content is posted.</p>
<p>First, let's build the <code>Blog</code> class that will manage subscribers and send notifications:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Blog</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name</span>):</span>
        self.name = name
        self._subscribers = []
        self._latest_post = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">subscribe</span>(<span class="hljs-params">self, subscriber</span>):</span>
        <span class="hljs-string">"""Add a subscriber to the blog"""</span>
        <span class="hljs-keyword">if</span> subscriber <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self._subscribers:
            self._subscribers.append(subscriber)
            print(<span class="hljs-string">f"✓ <span class="hljs-subst">{subscriber.email}</span> subscribed to <span class="hljs-subst">{self.name}</span>"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">unsubscribe</span>(<span class="hljs-params">self, subscriber</span>):</span>
        <span class="hljs-string">"""Remove a subscriber from the blog"""</span>
        <span class="hljs-keyword">if</span> subscriber <span class="hljs-keyword">in</span> self._subscribers:
            self._subscribers.remove(subscriber)
            print(<span class="hljs-string">f"✗ <span class="hljs-subst">{subscriber.email}</span> unsubscribed from <span class="hljs-subst">{self.name}</span>"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">notify_all</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Send notifications to all subscribers"""</span>
        print(<span class="hljs-string">f"\nNotifying <span class="hljs-subst">{len(self._subscribers)}</span> subscribers..."</span>)
        <span class="hljs-keyword">for</span> subscriber <span class="hljs-keyword">in</span> self._subscribers:
            subscriber.receive_notification(self.name, self._latest_post)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">publish_post</span>(<span class="hljs-params">self, title</span>):</span>
        <span class="hljs-string">"""Publish a new post and notify subscribers"""</span>
        print(<span class="hljs-string">f"\n📝 <span class="hljs-subst">{self.name}</span> published: '<span class="hljs-subst">{title}</span>'"</span>)
        self._latest_post = title
        self.notify_all()
</code></pre>
<p>The <code>Blog</code> class is our subject. It maintains a list of subscribers in <code>_subscribers</code> and stores the latest post title in <code>_latest_post</code>. The <code>subscribe</code> method adds subscribers to the list, checking for duplicates. The <code>notify_all</code> method loops through all subscribers and calls their <code>receive_notification</code> method. When we call <code>publish_post</code>, it updates the latest post and automatically notifies all subscribers.</p>
<p>Now let's create the observer class that receives notifications:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailSubscriber</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, email</span>):</span>
        self.email = email

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">receive_notification</span>(<span class="hljs-params">self, blog_name, post_title</span>):</span>
        print(<span class="hljs-string">f"📧 Email sent to <span class="hljs-subst">{self.email}</span>: New post on <span class="hljs-subst">{blog_name}</span> - '<span class="hljs-subst">{post_title}</span>'"</span>)
</code></pre>
<p>The <code>EmailSubscriber</code> class is our observer. It has one method, <code>receive_notification</code>, which handles incoming notifications from the blog.</p>
<p>Now let's use these classes together:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a blog</span>
tech_blog = Blog(<span class="hljs-string">"DevDaily"</span>)

<span class="hljs-comment"># Create subscribers</span>
reader1 = EmailSubscriber(<span class="hljs-string">"anna@example.com"</span>)
reader2 = EmailSubscriber(<span class="hljs-string">"betty@example.com"</span>)
reader3 = EmailSubscriber(<span class="hljs-string">"cathy@example.com"</span>)

<span class="hljs-comment"># Subscribe to the blog</span>
tech_blog.subscribe(reader1)
tech_blog.subscribe(reader2)
tech_blog.subscribe(reader3)

<span class="hljs-comment"># Publish posts</span>
tech_blog.publish_post(<span class="hljs-string">"10 Python Tips for Beginners"</span>)
tech_blog.publish_post(<span class="hljs-string">"Understanding Design Patterns"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">✓ anna@example.com subscribed to DevDaily
✓ betty@example.com subscribed to DevDaily
✓ cathy@example.com subscribed to DevDaily

📝 DevDaily published: '10 Python Tips for Beginners'

Notifying 3 subscribers...
📧 Email sent to anna@example.com: New post on DevDaily - '10 Python Tips for Beginners'
📧 Email sent to betty@example.com: New post on DevDaily - '10 Python Tips for Beginners'
📧 Email sent to cathy@example.com: New post on DevDaily - '10 Python Tips for Beginners'

📝 DevDaily published: 'Understanding Design Patterns'

Notifying 3 subscribers...
📧 Email sent to anna@example.com: New post on DevDaily - 'Understanding Design Patterns'
📧 Email sent to betty@example.com: New post on DevDaily - 'Understanding Design Patterns'
📧 Email sent to cathy@example.com: New post on DevDaily - 'Understanding Design Patterns'
</code></pre>
<p>Notice how the <code>Blog</code> class doesn't need to know the details of how each subscriber handles the notification. It just calls their <code>receive_notification</code> method.</p>
<p><strong>Note</strong>: Think of all the examples here as placeholder functions that explain how the observer pattern works. In your projects, you’ll have functions that connect to email and other services.</p>
<h2 id="heading-handling-unsubscribes">Handling Unsubscribes</h2>
<p>In real applications, users need to be able to unsubscribe. Here's how that works:</p>
<pre><code class="lang-python">blog = Blog(<span class="hljs-string">"CodeMaster"</span>)

user1 = EmailSubscriber(<span class="hljs-string">"john@example.com"</span>)
user2 = EmailSubscriber(<span class="hljs-string">"jane@example.com"</span>)

<span class="hljs-comment"># Subscribe users</span>
blog.subscribe(user1)
blog.subscribe(user2)

<span class="hljs-comment"># Publish a post</span>
blog.publish_post(<span class="hljs-string">"Getting Started with Python"</span>)

<span class="hljs-comment"># User1 unsubscribes</span>
blog.unsubscribe(user1)

<span class="hljs-comment"># Publish another post - only user2 gets notified</span>
blog.publish_post(<span class="hljs-string">"Advanced Python Techniques"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">✓ john@example.com subscribed to CodeMaster
✓ jane@example.com subscribed to CodeMaster

📝 CodeMaster published: 'Getting Started with Python'

Notifying 2 subscribers...
📧 Email sent to john@example.com: New post on CodeMaster - 'Getting Started with Python'
📧 Email sent to jane@example.com: New post on CodeMaster - 'Getting Started with Python'
✗ john@example.com unsubscribed from CodeMaster

📝 CodeMaster published: 'Advanced Python Techniques'

Notifying 1 subscribers...
📧 Email sent to jane@example.com: New post on CodeMaster - 'Advanced Python Techniques'
</code></pre>
<p>After <code>user1</code> unsubscribes, only <code>user2</code> receives the notification for the second post. The observer pattern makes it easy to add and remove observers dynamically.</p>
<h2 id="heading-different-types-of-observers">Different Types of Observers</h2>
<p>One super useful aspect of the observer pattern is that different observers can react differently to the same event. Let's create a stock price tracker where multiple observer types respond to price changes.</p>
<p>First, let's create the <code>Stock</code> class that will notify observers when the price changes:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Stock</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, symbol, price</span>):</span>
        self.symbol = symbol
        self._price = price
        self._observers = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_observer</span>(<span class="hljs-params">self, observer</span>):</span>
        self._observers.append(observer)
        print(<span class="hljs-string">f"Observer added: <span class="hljs-subst">{observer.__class__.__name__}</span>"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">remove_observer</span>(<span class="hljs-params">self, observer</span>):</span>
        self._observers.remove(observer)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">notify_observers</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">for</span> observer <span class="hljs-keyword">in</span> self._observers:
            observer.update(self.symbol, self._price)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">set_price</span>(<span class="hljs-params">self, price</span>):</span>
        print(<span class="hljs-string">f"\n <span class="hljs-subst">{self.symbol}</span> price changed: $<span class="hljs-subst">{self._price}</span> → $<span class="hljs-subst">{price}</span>"</span>)
        self._price = price
        self.notify_observers()
</code></pre>
<p>The <code>Stock</code> class maintains the current price and notifies all observers whenever <code>set_price</code> is called.</p>
<p>Now let's create three different observer types that respond differently to price updates:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailAlert</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, email</span>):</span>
        self.email = email

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, symbol, price</span>):</span>
        print(<span class="hljs-string">f"📧 Sending email to <span class="hljs-subst">{self.email}</span>: <span class="hljs-subst">{symbol}</span> is now $<span class="hljs-subst">{price}</span>"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SMSAlert</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, phone</span>):</span>
        self.phone = phone

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, symbol, price</span>):</span>
        print(<span class="hljs-string">f"📱 Sending SMS to <span class="hljs-subst">{self.phone}</span>: <span class="hljs-subst">{symbol}</span> price update $<span class="hljs-subst">{price}</span>"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Logger</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, symbol, price</span>):</span>
        print(<span class="hljs-string">f"📝 Logging: <span class="hljs-subst">{symbol}</span> = $<span class="hljs-subst">{price}</span> at system time"</span>)
</code></pre>
<p>Each observer has a different implementation of the update method. <code>EmailAlert</code> sends emails, <code>SMSAlert</code> sends text messages, and <code>Logger</code> records the change.</p>
<p>Now let's use them together:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a stock</span>
apple_stock = Stock(<span class="hljs-string">"AAPL"</span>, <span class="hljs-number">150.00</span>)

<span class="hljs-comment"># Create different types of observers</span>
email_notifier = EmailAlert(<span class="hljs-string">"investor@example.com"</span>)
sms_notifier = SMSAlert(<span class="hljs-string">"+1234567890"</span>)
price_logger = Logger()

<span class="hljs-comment"># Add all observers</span>
apple_stock.add_observer(email_notifier)
apple_stock.add_observer(sms_notifier)
apple_stock.add_observer(price_logger)

<span class="hljs-comment"># Update the stock price</span>
apple_stock.set_price(<span class="hljs-number">155.50</span>)
apple_stock.set_price(<span class="hljs-number">152.25</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Observer added: EmailAlert
Observer added: SMSAlert
Observer added: Logger

 AAPL price changed: $150.0 → $155.5
📧 Sending email to investor@example.com: AAPL is now $155.5
📱 Sending SMS to +1234567890: AAPL price update $155.5
📝 Logging: AAPL = $155.5 at system time

 AAPL price changed: $155.5 → $152.25
📧 Sending email to investor@example.com: AAPL is now $152.25
📱 Sending SMS to +1234567890: AAPL price update $152.25
📝 Logging: AAPL = $152.25 at system time
</code></pre>
<p>The <code>Stock</code> class doesn't care what each observer does. It simply calls <code>update</code> on each one and passes the necessary data. You can mix and match observers however you want.</p>
<h2 id="heading-using-abstract-base-classes">Using Abstract Base Classes</h2>
<p>To enforce a consistent interface across all observers, we can use Python's <a target="_blank" href="https://docs.python.org/3/library/abc.html">Abstract Base Classes</a>. This guarantees type safety.</p>
<p>First, let's create the base classes that define our interface:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> abc <span class="hljs-keyword">import</span> ABC, abstractmethod

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Subject</span>(<span class="hljs-params">ABC</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self._observers = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">attach</span>(<span class="hljs-params">self, observer</span>):</span>
        <span class="hljs-keyword">if</span> observer <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self._observers:
            self._observers.append(observer)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">detach</span>(<span class="hljs-params">self, observer</span>):</span>
        self._observers.remove(observer)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">notify</span>(<span class="hljs-params">self, data</span>):</span>
        <span class="hljs-keyword">for</span> observer <span class="hljs-keyword">in</span> self._observers:
            observer.update(data)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Observer</span>(<span class="hljs-params">ABC</span>):</span>
<span class="hljs-meta">    @abstractmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, data</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>The <code>Subject</code> class provides standard observer management methods. The <code>Observer</code> class defines the interface with the <code>@abstractmethod</code> decorator ensuring all observers implement update.</p>
<p>Now let's create an order system that uses these base classes:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderSystem</span>(<span class="hljs-params">Subject</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__()
        self._order_id = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">place_order</span>(<span class="hljs-params">self, order_id, items</span>):</span>
        print(<span class="hljs-string">f"\n🛒 Order #<span class="hljs-subst">{order_id}</span> placed with <span class="hljs-subst">{len(items)}</span> items"</span>)
        self._order_id = order_id
        self.notify({<span class="hljs-string">"order_id"</span>: order_id, <span class="hljs-string">"items"</span>: items})
</code></pre>
<p>The <code>OrderSystem</code> inherits from <code>Subject</code> and can manage observers without implementing that logic itself.</p>
<p>Next, let's create concrete observers for different departments:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InventoryObserver</span>(<span class="hljs-params">Observer</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, data</span>):</span>
        print(<span class="hljs-string">f"📦 Inventory: Updating stock for order #<span class="hljs-subst">{data[<span class="hljs-string">'order_id'</span>]}</span>"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShippingObserver</span>(<span class="hljs-params">Observer</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, data</span>):</span>
        print(<span class="hljs-string">f"🚚 Shipping: Preparing shipment for order #<span class="hljs-subst">{data[<span class="hljs-string">'order_id'</span>]}</span>"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BillingObserver</span>(<span class="hljs-params">Observer</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, data</span>):</span>
        print(<span class="hljs-string">f"💳 Billing: Processing payment for order #<span class="hljs-subst">{data[<span class="hljs-string">'order_id'</span>]}</span>"</span>)
</code></pre>
<p>Each observer <em>must</em> implement the <code>update</code> method. Now let's put it all together:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create the order system</span>
order_system = OrderSystem()

<span class="hljs-comment"># Create observers</span>
inventory = InventoryObserver()
shipping = ShippingObserver()
billing = BillingObserver()

<span class="hljs-comment"># Attach observers</span>
order_system.attach(inventory)
order_system.attach(shipping)
order_system.attach(billing)

<span class="hljs-comment"># Place an order</span>
order_system.place_order(<span class="hljs-string">"ORD-12345"</span>, [<span class="hljs-string">"Laptop"</span>, <span class="hljs-string">"Mouse"</span>, <span class="hljs-string">"Keyboard"</span>])
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">🛒 Order #ORD-12345 placed with 3 items
📦 Inventory: Updating stock for order #ORD-12345
🚚 Shipping: Preparing shipment for order #ORD-12345
💳 Billing: Processing payment for order #ORD-12345
</code></pre>
<p>Using abstract base classes provides type safety and ensures all observers follow the same interface.</p>
<h2 id="heading-when-to-use-the-observer-pattern">When to Use the Observer Pattern</h2>
<p>The observer pattern is suiatble for:</p>
<ul>
<li><p>Event-driven systems – GUI frameworks, game engines, or any system where actions trigger updates elsewhere.</p>
</li>
<li><p>Real-time notifications – Chat apps, social media feeds, stock tickers, or push notification systems.</p>
</li>
<li><p>Decoupled architecture – When you want the subject independent of its observers for flexibility.</p>
</li>
<li><p>Multiple listeners – When multiple objects need to react to the same event differently.</p>
</li>
</ul>
<p>Avoid the Observer Pattern when you have simple one-to-one relationships, or when performance is critical with many observers (because notification overhead can be significant).</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The observer pattern creates a clean separation between objects that produce events and objects that respond to them. It promotes loose coupling – the subject doesn't need to know anything about its observers except that they have an update method.</p>
<p>We've covered the basic implementation, handling subscriptions, using different observer types, and abstract base classes. Start simple with the basic subject-observer relationship and add complexity only when needed.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the Factory Pattern in Python - A Practical Guide ]]>
                </title>
                <description>
                    <![CDATA[ Design patterns are proven solutions to common problems in software development. If you've ever found yourself writing repetitive object creation code or struggling to manage different types of objects, the factory pattern might be exactly what you n... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-factory-pattern-in-python-a-practical-guide/</link>
                <guid isPermaLink="false">6989f75b7982b0d48a3c2c48</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Mon, 09 Feb 2026 15:03:55 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770649418899/f26d3d70-a909-4d8f-92f5-7f263c64f9fe.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Design patterns are proven solutions to common problems in software development. If you've ever found yourself writing repetitive object creation code or struggling to manage different types of objects, the <strong>factory pattern</strong> might be exactly what you need.</p>
<p>In this tutorial, you'll learn what the factory pattern is, why it's useful, and how to implement it in Python. We'll build practical examples that show you when and how to use this pattern in real-world applications.</p>
<p>You can find the code <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/factory">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Understanding of Python classes and methods</p>
</li>
<li><p>Familiarity with <a target="_blank" href="https://www.youtube.com/watch?v=Ej_02ICOIgs">object-oriented programming</a> (OOP) concepts</p>
</li>
</ul>
<p>Let’s get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-the-factory-pattern">What Is the Factory Pattern?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-simple-factory-example">A Simple Factory Example</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-a-dictionary-for-cleaner-code">Using a Dictionary for Cleaner Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-factory-pattern-with-parameters">Factory Pattern with Parameters</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-abstract-base-classes">Using Abstract Base Classes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-more-helpful-example-database-connection-factory">A More Helpful Example: Database Connection Factory</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-the-factory-pattern">When to Use the Factory Pattern</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-factory-pattern">What Is the Factory Pattern?</h2>
<p>The factory pattern is a creational design pattern that <strong>provides an interface for creating objects without specifying their exact classes</strong>. Instead of calling a constructor directly, you call a factory method that decides which class to instantiate.</p>
<p>Think of it like ordering food at a restaurant. You don't go into the kitchen and make the food yourself. You tell the waiter what you want, and the kitchen (the factory) creates it for you. You get your meal without worrying about the recipe or cooking process.</p>
<p>The factory pattern is useful when:</p>
<ul>
<li><p>You have multiple related classes and need to decide which one to instantiate at runtime</p>
</li>
<li><p>Object creation logic is complex and you want to encapsulate it</p>
</li>
<li><p>You want to make your code more maintainable and testable</p>
</li>
</ul>
<h2 id="heading-a-simple-factory-example">A Simple Factory Example</h2>
<p>Let's start with a basic example. Say you're building a notification system that can send messages via email, SMS, or push notifications.</p>
<p>Without a factory, you might write code like this everywhere in your application:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Bad approach - tight coupling</span>
<span class="hljs-keyword">if</span> notification_type == <span class="hljs-string">"email"</span>:
    notifier = EmailNotifier()
<span class="hljs-keyword">elif</span> notification_type == <span class="hljs-string">"sms"</span>:
    notifier = SMSNotifier()
<span class="hljs-keyword">elif</span> notification_type == <span class="hljs-string">"push"</span>:
    notifier = PushNotifier()
</code></pre>
<p>This gets messy quickly. Let's use a factory instead:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailNotifier</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send</span>(<span class="hljs-params">self, message</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Sending email: <span class="hljs-subst">{message}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SMSNotifier</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send</span>(<span class="hljs-params">self, message</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Sending SMS: <span class="hljs-subst">{message}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PushNotifier</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send</span>(<span class="hljs-params">self, message</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Sending push notification: <span class="hljs-subst">{message}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NotificationFactory</span>:</span>
<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_notifier</span>(<span class="hljs-params">notifier_type</span>):</span>
        <span class="hljs-keyword">if</span> notifier_type == <span class="hljs-string">"email"</span>:
            <span class="hljs-keyword">return</span> EmailNotifier()
        <span class="hljs-keyword">elif</span> notifier_type == <span class="hljs-string">"sms"</span>:
            <span class="hljs-keyword">return</span> SMSNotifier()
        <span class="hljs-keyword">elif</span> notifier_type == <span class="hljs-string">"push"</span>:
            <span class="hljs-keyword">return</span> PushNotifier()
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown notifier type: <span class="hljs-subst">{notifier_type}</span>"</span>)
</code></pre>
<p>In this code, we define three notifier classes, each with a send method.</p>
<p><strong>Note</strong>: In a real application, these would have different implementations for sending notifications.</p>
<p>The <code>NotificationFactory</code> class has a <a target="_blank" href="https://docs.python.org/3/library/functions.html#staticmethod">static method</a> called <code>create_notifier</code>. This is our factory method. It takes a string parameter and returns the appropriate notifier object.</p>
<p>The <code>@staticmethod</code> decorator means we can call this method without creating an instance of the factory. We just use <code>NotificationFactory.create_notifier()</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Using the factory</span>
notifier = NotificationFactory.create_notifier(<span class="hljs-string">"email"</span>)
result = notifier.send(<span class="hljs-string">"Hello, World!"</span>)
</code></pre>
<p>Now, whenever we need a notifier, we call the factory instead of instantiating classes directly. This centralizes our object creation logic in one place.</p>
<h2 id="heading-using-a-dictionary-for-cleaner-code">Using a Dictionary for Cleaner Code</h2>
<p>The if-elif chain in our factory can get unwieldy as we add more notifier types. Let's refactor using a dictionary:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NotificationFactory</span>:</span>
    notifier_types = {
        <span class="hljs-string">"email"</span>: EmailNotifier,
        <span class="hljs-string">"sms"</span>: SMSNotifier,
        <span class="hljs-string">"push"</span>: PushNotifier
    }

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_notifier</span>(<span class="hljs-params">notifier_type</span>):</span>
        notifier_class = NotificationFactory.notifier_types.get(notifier_type)
        <span class="hljs-keyword">if</span> notifier_class:
            <span class="hljs-keyword">return</span> notifier_class()
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown notifier type: <span class="hljs-subst">{notifier_type}</span>"</span>)
</code></pre>
<p>This approach is much cleaner. We store a dictionary that maps strings to class objects and <em>not</em> instances. The keys are notifier type names, and the values are the actual class references.</p>
<p>The <code>get</code> method retrieves the class from the dictionary. If the key doesn't exist, it returns <code>None</code>. We then instantiate the class by calling it with parentheses: <code>notifier_class()</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Test with different types</span>
email_notifier = NotificationFactory.create_notifier(<span class="hljs-string">"email"</span>)
sms_notifier = NotificationFactory.create_notifier(<span class="hljs-string">"sms"</span>)
push_notifier = NotificationFactory.create_notifier(<span class="hljs-string">"push"</span>)
</code></pre>
<p>This makes adding new notifier types easier. You just add another entry to the dictionary.</p>
<h2 id="heading-factory-pattern-with-parameters">Factory Pattern with Parameters</h2>
<p>Real-world objects often need configuration. Let's extend our factory to handle notifiers that require initialization parameters.</p>
<p>We'll create a document generator that produces different file formats with custom settings:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PDFDocument</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, title, author</span>):</span>
        self.title = title
        self.author = author
        self.format = <span class="hljs-string">"PDF"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Generating <span class="hljs-subst">{self.format}</span>: '<span class="hljs-subst">{self.title}</span>' by <span class="hljs-subst">{self.author}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WordDocument</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, title, author</span>):</span>
        self.title = title
        self.author = author
        self.format = <span class="hljs-string">"DOCX"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Generating <span class="hljs-subst">{self.format}</span>: '<span class="hljs-subst">{self.title}</span>' by <span class="hljs-subst">{self.author}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MarkdownDocument</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, title, author</span>):</span>
        self.title = title
        self.author = author
        self.format = <span class="hljs-string">"MD"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Generating <span class="hljs-subst">{self.format}</span>: '<span class="hljs-subst">{self.title}</span>' by <span class="hljs-subst">{self.author}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DocumentFactory</span>:</span>
    document_types = {
        <span class="hljs-string">"pdf"</span>: PDFDocument,
        <span class="hljs-string">"word"</span>: WordDocument,
        <span class="hljs-string">"markdown"</span>: MarkdownDocument
    }

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_document</span>(<span class="hljs-params">doc_type, title, author</span>):</span>
        document_class = DocumentFactory.document_types.get(doc_type)
        <span class="hljs-keyword">if</span> document_class:
            <span class="hljs-keyword">return</span> document_class(title, author)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown document type: <span class="hljs-subst">{doc_type}</span>"</span>)
</code></pre>
<p>The key difference here is that our factory method now accepts additional parameters.</p>
<p>The <code>create_document</code> method takes <code>doc_type</code>, <code>title</code>, and <code>author</code> as arguments. When we instantiate the class, we pass the <code>title</code> and <code>author</code> to the <code>create_document</code> constructor: <code>document_class(title, author)</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create different documents with parameters</span>
pdf = DocumentFactory.create_document(<span class="hljs-string">"pdf"</span>, <span class="hljs-string">"Python Guide"</span>, <span class="hljs-string">"Tutorial Team"</span>)
word = DocumentFactory.create_document(<span class="hljs-string">"word"</span>, <span class="hljs-string">"Meeting Notes"</span>, <span class="hljs-string">"Grace Dev"</span>)
markdown = DocumentFactory.create_document(<span class="hljs-string">"markdown"</span>, <span class="hljs-string">"README"</span>, <span class="hljs-string">"DevTeam"</span>)
</code></pre>
<p>This lets us create fully configured objects through the factory while keeping the creation logic centralized.</p>
<h2 id="heading-using-abstract-base-classes">Using Abstract Base Classes</h2>
<p>To make our factory more robust, we can use Python's <a target="_blank" href="https://docs.python.org/3/library/abc.html">Abstract Base Classes (ABC)</a> to enforce a common interface.</p>
<p>Let's create a super simple payment processing system:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> abc <span class="hljs-keyword">import</span> ABC, abstractmethod

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentProcessor</span>(<span class="hljs-params">ABC</span>):</span>
<span class="hljs-meta">    @abstractmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_payment</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">pass</span>

<span class="hljs-meta">    @abstractmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refund</span>(<span class="hljs-params">self, transaction_id</span>):</span>
        <span class="hljs-keyword">pass</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreditCardProcessor</span>(<span class="hljs-params">PaymentProcessor</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_payment</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Processing $<span class="hljs-subst">{amount}</span> via Credit Card"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refund</span>(<span class="hljs-params">self, transaction_id</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Refunding credit card transaction <span class="hljs-subst">{transaction_id}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PayPalProcessor</span>(<span class="hljs-params">PaymentProcessor</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_payment</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Processing $<span class="hljs-subst">{amount}</span> via PayPal"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refund</span>(<span class="hljs-params">self, transaction_id</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Refunding PayPal transaction <span class="hljs-subst">{transaction_id}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentFactory</span>:</span>
    processors = {
        <span class="hljs-string">"credit_card"</span>: CreditCardProcessor,
        <span class="hljs-string">"paypal"</span>: PayPalProcessor
    }

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_processor</span>(<span class="hljs-params">processor_type</span>):</span>
        processor_class = PaymentFactory.processors.get(processor_type)
        <span class="hljs-keyword">if</span> processor_class:
            <span class="hljs-keyword">return</span> processor_class()
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown processor type: <span class="hljs-subst">{processor_type}</span>"</span>)
</code></pre>
<p>Here, the <code>PaymentProcessor</code> class defines an interface that <em>all</em> payment processors must implement. The <code>@abstractmethod</code> decorator marks methods that subclasses must override.</p>
<p>You cannot instantiate <code>PaymentProcessor</code> directly. It only serves as a blueprint. All concrete processors (<code>CreditCardProcessor</code>, <code>PayPalProcessor</code>) must implement both <code>process_payment</code> and <code>refund</code> methods. If they don't, Python will raise an error. This guarantees that any object created by our factory will have the expected methods, making our code more predictable and safer.</p>
<p>You can use the factory like so:</p>
<pre><code class="lang-python">processor = PaymentFactory.create_processor(<span class="hljs-string">"paypal"</span>)
</code></pre>
<h2 id="heading-a-more-helpful-example-database-connection-factory">A More Helpful Example: Database Connection Factory</h2>
<p>Let's build something practical: a factory that creates different database connection objects based on configuration.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MySQLConnection</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, host, database</span>):</span>
        self.host = host
        self.database = database
        self.connection_type = <span class="hljs-string">"MySQL"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">connect</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Connected to <span class="hljs-subst">{self.connection_type}</span> at <span class="hljs-subst">{self.host}</span>/<span class="hljs-subst">{self.database}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute_query</span>(<span class="hljs-params">self, query</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Executing on MySQL: <span class="hljs-subst">{query}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostgreSQLConnection</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, host, database</span>):</span>
        self.host = host
        self.database = database
        self.connection_type = <span class="hljs-string">"PostgreSQL"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">connect</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Connected to <span class="hljs-subst">{self.connection_type}</span> at <span class="hljs-subst">{self.host}</span>/<span class="hljs-subst">{self.database}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute_query</span>(<span class="hljs-params">self, query</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Executing on PostgreSQL: <span class="hljs-subst">{query}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SQLiteConnection</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, host, database</span>):</span>
        self.host = host
        self.database = database
        self.connection_type = <span class="hljs-string">"SQLite"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">connect</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Connected to <span class="hljs-subst">{self.connection_type}</span> at <span class="hljs-subst">{self.host}</span>/<span class="hljs-subst">{self.database}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute_query</span>(<span class="hljs-params">self, query</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Executing on SQLite: <span class="hljs-subst">{query}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseFactory</span>:</span>
    db_types = {
        <span class="hljs-string">"mysql"</span>: MySQLConnection,
        <span class="hljs-string">"postgresql"</span>: PostgreSQLConnection,
        <span class="hljs-string">"sqlite"</span>: SQLiteConnection
    }

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_connection</span>(<span class="hljs-params">db_type, host, database</span>):</span>
        db_class = DatabaseFactory.db_types.get(db_type)
        <span class="hljs-keyword">if</span> db_class:
            <span class="hljs-keyword">return</span> db_class(host, database)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown database type: <span class="hljs-subst">{db_type}</span>"</span>)

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_from_config</span>(<span class="hljs-params">config</span>):</span>
        <span class="hljs-string">"""Create a database connection from a configuration dictionary"""</span>
        <span class="hljs-keyword">return</span> DatabaseFactory.create_connection(
            config[<span class="hljs-string">"type"</span>],
            config[<span class="hljs-string">"host"</span>],
            config[<span class="hljs-string">"database"</span>]
        )
</code></pre>
<p>This example shows a more realistic use case. We have multiple database connection classes, each with the same interface but different implementations.</p>
<p>The factory has two creation methods: <code>create_connection</code> for direct parameters and <code>create_from_config</code> for configuration dictionaries.</p>
<p>The <code>create_from_config</code> method is particularly useful because it lets you load database settings from a config file or environment variables and create the appropriate connection object.</p>
<p>This pattern makes it easy to switch between different databases without changing your application code. You just change the configuration as shown:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Use with direct parameters</span>
db1 = DatabaseFactory.create_connection(<span class="hljs-string">"mysql"</span>, <span class="hljs-string">"localhost"</span>, <span class="hljs-string">"myapp_db"</span>)
print(db1.connect())
print(db1.execute_query(<span class="hljs-string">"SELECT * FROM users"</span>))

<span class="hljs-comment"># Use with configuration dictionary</span>
config = {
    <span class="hljs-string">"type"</span>: <span class="hljs-string">"postgresql"</span>,
    <span class="hljs-string">"host"</span>: <span class="hljs-string">"db.example.com"</span>,
    <span class="hljs-string">"database"</span>: <span class="hljs-string">"production_db"</span>
}
db2 = DatabaseFactory.create_from_config(config)
</code></pre>
<h2 id="heading-when-to-use-the-factory-pattern">When to Use the Factory Pattern</h2>
<p>The factory pattern is useful when you have the following:</p>
<ol>
<li><p><strong>Multiple related classes</strong>: When you have several classes that share a common interface but have different implementations (like the payment processors or database connections we had in the examples).</p>
</li>
<li><p><strong>Runtime decisions</strong>: When you need to decide which class to instantiate based on user input, configuration, or other runtime conditions.</p>
</li>
<li><p><strong>Complex object creation</strong>: When creating an object involves multiple steps or requires specific logic that you want to encapsulate.</p>
</li>
</ol>
<p>However, don't use the factory pattern when:</p>
<ul>
<li><p>You only have one or two simple classes</p>
</li>
<li><p>Object creation is straightforward with no special logic</p>
</li>
<li><p>The added abstraction makes your code harder to understand</p>
</li>
</ul>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>The factory pattern is a useful tool for managing object creation in Python. It helps you write cleaner, more maintainable code by centralizing creation logic and decoupling your code from specific class implementations. We've covered:</p>
<ul>
<li><p>Basic factory implementation with simple examples</p>
</li>
<li><p>Using dictionaries for cleaner factory code</p>
</li>
<li><p>Passing parameters to factory-created objects</p>
</li>
<li><p>Using abstract base classes for cleaner interfaces</p>
</li>
</ul>
<p>The key takeaway is this: <strong>whenever you find yourself writing repetitive object creation code or need to decide which class to instantiate at runtime, consider using the factory pattern</strong>. Start simple and add complexity only when needed. The basic dictionary-based factory is often all you need for most applications.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How the Factory and Abstract Factory Design Patterns Work in Flutter ]]>
                </title>
                <description>
                    <![CDATA[ In software development, particularly object-oriented programming and design, object creation is a common task. And how you manage this process can impact your app's flexibility, scalability, and maintainability. Creational design patterns govern how... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-the-factory-and-abstract-factory-design-patterns-work-in-flutter/</link>
                <guid isPermaLink="false">6978f477116625d0304ed264</guid>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Factory Design Pattern ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mobile Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mobile apps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer ]]>
                    </category>
                
                    <category>
                        <![CDATA[ OOPS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Object Oriented Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design principles ]]>
                    </category>
                
                    <category>
                        <![CDATA[ object oriented design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Abstract Factory Patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwaseyi Fatunmole ]]>
                </dc:creator>
                <pubDate>Tue, 27 Jan 2026 17:23:03 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769533734673/8b5ad88a-13d2-4fec-969b-55fd854df5c1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In software development, particularly object-oriented programming and design, object creation is a common task. And how you manage this process can impact your app's flexibility, scalability, and maintainability.</p>
<p>Creational design patterns govern how classes and objects are created in a systematic and scalable way. They provide blueprints for creating objects so you don't repeat code. They also keep your system consistent and makes your app easy to extend.</p>
<p>There are five major Creational Design patterns:</p>
<ol>
<li><p><strong>Singleton:</strong> Ensures a class has only one instance and provides a global point of access to it.</p>
</li>
<li><p><strong>Factory Method</strong>: Provides an interface for creating objects but lets subclasses decide which class to instantiate.</p>
</li>
<li><p><strong>Abstract Factory</strong>: Creates families of related objects without specifying their concrete classes.</p>
</li>
<li><p><strong>Builder</strong>: Allows you to construct complex objects step by step, separating construction from representation.</p>
</li>
<li><p><strong>Prototype</strong>: Creates new objects by cloning existing ones, rather than creating from scratch.</p>
</li>
</ol>
<p>Each of these patterns solves specific problems around object creation, depending on the complexity and scale of your application.</p>
<p>In this tutorial, I'll explain what Creational Design Patterns are and how they work. We'll focus on two primary patterns: the Factory and Abstract Factory patterns.</p>
<p>Many people mix these two up, so here we'll explore:</p>
<ol>
<li><p>How each pattern works</p>
</li>
<li><p>Practical examples in Flutter</p>
</li>
<li><p>Applications, best practices, and usage</p>
</li>
</ol>
<p>By the end, you'll understand when to use Factory, when to switch to Abstract Factory, and how to structure your Flutter apps for scalability and maintainability.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-how-the-factory-pattern-works-in-flutter">How the Factory Pattern Works in Flutter</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-define-the-product-and-abstract-creator">Step 1: Define the Product and Abstract Creator</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-implement-concrete-products">Step 2: Implement Concrete Products</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-create-the-factory">Step 3: Create the Factory</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-use-the-factory">Step 4: Use the Factory</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-factory-pattern-for-security-checks">Factory Pattern for Security Checks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-the-abstract-factory-pattern-works-in-flutter">How the Abstract Factory Pattern Works in Flutter</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-define-abstract-product-interfaces">Step 1: Define Abstract Product Interfaces</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-implement-platform-specific-products">Step 2: Implement Platform-Specific Products</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-define-the-abstract-factory-interface">Step 3: Define the Abstract Factory Interface</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-implement-platform-specific-factories">Step 4: Implement Platform Specific Factories</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-client-code-using-abstract-factory">Step 5: Client Code Using Abstract Factory</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before diving into this tutorial, you should have:</p>
<ul>
<li><p>a basic understanding of the Dart programming language</p>
</li>
<li><p>familiarity with Object-Oriented Programming (OOP) concepts (particularly classes, inheritance, and abstract classes)</p>
</li>
<li><p>basic knowledge of Flutter development (helpful but not required)</p>
</li>
<li><p>an understanding of interfaces and polymorphism</p>
</li>
<li><p>and experience creating and instantiating classes in Dart.</p>
</li>
</ul>
<h2 id="heading-how-the-factory-pattern-works-in-flutter">How the Factory Pattern Works in Flutter</h2>
<p>You'll typically use the Factory Pattern when you want to manage data sets that might be related, but only for a single type of object.</p>
<p>Let's say you want to manage themes for Android and iOS. Using the Factory Pattern allows you to encapsulate object creation and keep your app modular. We'll build this step by step so you can see how the pattern works.</p>
<h3 id="heading-step-1-define-the-product-and-abstract-creator">Step 1: Define the Product and Abstract Creator</h3>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppTheme</span> </span>{
  <span class="hljs-built_in">String?</span> data;
  AppTheme({<span class="hljs-keyword">this</span>.data});
}

<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApplicationThemeData</span> </span>{
  Future&lt;AppTheme&gt; getApplicationTheme();
}
</code></pre>
<p>Here, <code>AppTheme</code> is a simple data class that holds theme information. This represents the product our factory will create. <code>ApplicationThemeData</code> serves as an abstract base class. This abstraction is crucial because it defines a contract that all concrete theme implementations must follow.</p>
<p>By requiring a <code>getApplicationTheme()</code> method, we ensure consistency across different platforms.</p>
<h3 id="heading-step-2-implement-concrete-products">Step 2: Implement Concrete Products</h3>
<p>Now we create platform-specific implementations that provide actual theme data.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AndroidAppTheme</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ApplicationThemeData</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;AppTheme&gt; getApplicationTheme() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> AppTheme(data: <span class="hljs-string">"Here is android theme"</span>);
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IOSThemeData</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ApplicationThemeData</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;AppTheme&gt; getApplicationTheme() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> AppTheme(data: <span class="hljs-string">"This is IOS theme data"</span>);
  }
}
</code></pre>
<p>The concrete implementations, <code>AndroidAppTheme</code> and <code>IOSThemeData</code>, extend the abstract class and provide platform-specific theme data. Each returns an <code>AppTheme</code> object with content tailored to its respective platform.</p>
<h3 id="heading-step-3-create-the-factory">Step 3: Create the Factory</h3>
<p>The factory encapsulates the object creation logic, so client code doesn't need to know which specific theme class it's working with.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThemeFactory</span> </span>{
  ThemeFactory({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.theme});
  ApplicationThemeData theme;

  loadTheme() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> theme.getApplicationTheme();
  }
}
</code></pre>
<p><code>ThemeFactory</code> acts as the factory itself. It accepts any <code>ApplicationThemeData</code> implementation and provides a unified <code>loadTheme()</code> method. This encapsulates the object creation logic cleanly.</p>
<h3 id="heading-step-4-use-the-factory">Step 4: Use the Factory</h3>
<p>Finally, we use the factory in our application code.</p>
<pre><code class="lang-dart">ThemeFactory(
  theme: Platform.isAndroid ? AndroidAppTheme() : IOSThemeData()
).loadTheme();
</code></pre>
<p>Here, you choose a theme (Android or iOS) and get the corresponding <code>AppTheme</code>. This approach is simple and effective when you only care about one functionality, like loading a theme.</p>
<p>The beauty of this pattern is that the client code remains clean and doesn't need to change if you add new platforms later.</p>
<h2 id="heading-factory-pattern-for-security-checks">Factory Pattern for Security Checks</h2>
<p>Another excellent use case for the Factory Pattern is when implementing security checks during your application bootstrap.</p>
<p>For instance, Android and iOS require different logic for internal security. Android might check for developer mode or rooted devices, while iOS checks for jailbroken devices. This scenario is a perfect example of when to apply the Factory Pattern, as it allows you to encapsulate platform-specific security logic cleanly and maintainably. Let's implement this step by step.</p>
<h3 id="heading-step-1-define-security-check-result-and-abstract-checker">Step 1: Define Security Check Result and Abstract Checker</h3>
<p>First, we need a standardized way to communicate security check outcomes and a contract for performing security checks.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Base security check result class</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecurityCheckResult</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> isSecure;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> message;

  SecurityCheckResult({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.isSecure, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.message});
}

<span class="hljs-comment">// Abstract security checker</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecurityChecker</span> </span>{
  Future&lt;SecurityCheckResult&gt; performSecurityCheck();
}
</code></pre>
<p>The <code>SecurityCheckResult</code> class provides a standardized way to communicate security check outcomes across platforms.</p>
<p>It contains a boolean flag indicating security status and a descriptive message for the user. The abstract <code>SecurityChecker</code> class defines the contract that all platform-specific security implementations must follow.</p>
<p>This ensures that, regardless of the platform, we can always call <code>performSecurityCheck()</code> and receive a consistent result type.</p>
<h3 id="heading-step-2-implement-platform-specific-security-checkers">Step 2: Implement Platform-Specific Security Checkers</h3>
<p>Now we create the actual security checking implementations for each platform.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Android-specific security implementation</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AndroidSecurityChecker</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">SecurityChecker</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;SecurityCheckResult&gt; performSecurityCheck() <span class="hljs-keyword">async</span> {
    <span class="hljs-built_in">bool</span> isRooted = <span class="hljs-keyword">await</span> checkIfDeviceIsRooted();
    <span class="hljs-keyword">if</span> (isRooted) {
      <span class="hljs-keyword">return</span> SecurityCheckResult(
        isSecure: <span class="hljs-keyword">false</span>,
        message: <span class="hljs-string">"Device is rooted. App cannot run on rooted devices."</span>
      );
    }

    <span class="hljs-built_in">bool</span> isDeveloperMode = <span class="hljs-keyword">await</span> checkDeveloperMode();
    <span class="hljs-keyword">if</span> (isDeveloperMode) {
      <span class="hljs-keyword">return</span> SecurityCheckResult(
        isSecure: <span class="hljs-keyword">false</span>,
        message: <span class="hljs-string">"Developer mode is enabled. Please disable it to continue."</span>
      );
    }

    <span class="hljs-keyword">return</span> SecurityCheckResult(
      isSecure: <span class="hljs-keyword">true</span>,
      message: <span class="hljs-string">"Device security check passed."</span>
    );
  }

  Future&lt;<span class="hljs-built_in">bool</span>&gt; checkIfDeviceIsRooted() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; 
  }

  Future&lt;<span class="hljs-built_in">bool</span>&gt; checkDeveloperMode() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; <span class="hljs-comment">// Placeholder</span>
  }
}

<span class="hljs-comment">// iOS-specific security implementation</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IOSSecurityChecker</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">SecurityChecker</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;SecurityCheckResult&gt; performSecurityCheck() <span class="hljs-keyword">async</span> {
    <span class="hljs-built_in">bool</span> isJailbroken = <span class="hljs-keyword">await</span> checkIfDeviceIsJailbroken();

    <span class="hljs-keyword">if</span> (isJailbroken) {
      <span class="hljs-keyword">return</span> SecurityCheckResult(
        isSecure: <span class="hljs-keyword">false</span>,
        message: <span class="hljs-string">"Device is jailbroken. App cannot run on jailbroken devices."</span>
      );
    }

    <span class="hljs-keyword">return</span> SecurityCheckResult(
      isSecure: <span class="hljs-keyword">true</span>,
      message: <span class="hljs-string">"Device security check passed."</span>
    );
  }

  Future&lt;<span class="hljs-built_in">bool</span>&gt; checkIfDeviceIsJailbroken() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; 
  }
}
</code></pre>
<p>The Android implementation focuses on detecting rooted devices and developer mode, which are common security concerns on Android.</p>
<p>A rooted device has elevated permissions that could allow malicious apps to access sensitive data, while developer mode can expose debugging interfaces.</p>
<p>The iOS implementation checks for jailbroken devices, which is the iOS equivalent of rooting. Jailbroken devices bypass Apple's security restrictions and can pose similar security risks.</p>
<h3 id="heading-step-3-create-the-security-factory">Step 3: Create the Security Factory</h3>
<p>The factory wraps the chosen security checker and provides a clean interface for running checks.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Security Factory</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecurityCheckFactory</span> </span>{
  SecurityCheckFactory({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.checker});
  SecurityChecker checker;

  Future&lt;SecurityCheckResult&gt; runSecurityCheck() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> checker.performSecurityCheck();
  }
}
</code></pre>
<p>The <code>SecurityCheckFactory</code> provides a simple interface that accepts any <code>SecurityChecker</code> implementation. This means your app initialization code doesn't need to know about platform-specific security details – it just calls <code>runSecurityCheck()</code> and handles the result.</p>
<h3 id="heading-step-4-use-the-security-factory-in-app-bootstrap">Step 4: Use the Security Factory in App Bootstrap</h3>
<p>Finally, we integrate the security factory into our app's initialization process.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// In your app's bootstrap/initialization</span>
Future&lt;<span class="hljs-keyword">void</span>&gt; initializeApp() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">final</span> securityFactory = SecurityCheckFactory(
    checker: Platform.isAndroid 
      ? AndroidSecurityChecker() 
      : IOSSecurityChecker()
  );

  <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> securityFactory.runSecurityCheck();

  <span class="hljs-keyword">if</span> (!result.isSecure) {
    <span class="hljs-comment">// Show error dialog and prevent app from continuing</span>
    showSecurityErrorDialog(result.message);
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-comment">// Continue with normal app initialization</span>
  runApp(MyApp());
}
</code></pre>
<p>This usage example demonstrates how the Factory Pattern makes your app initialization code clean and maintainable.</p>
<p>The platform detection happens in one place, the factory handles the creation of the appropriate checker, and your code simply deals with the standardized result.</p>
<p><strong>Key takeaway:</strong> Factory is great when you need one type of object, but you want to abstract away the creation logic.</p>
<h2 id="heading-how-the-abstract-factory-pattern-works-in-flutter">How the Abstract Factory Pattern Works in Flutter</h2>
<p>The Abstract Factory Pattern comes into play when you have more than two data sets for comparison, and each set includes multiple functionalities.</p>
<p>For example, imagine you now want to manage themes, widgets, and architecture for Android, iOS, and Linux. Managing this with just a Factory becomes messy, so Abstract Factory provides a structured way to handle multiple related objects for different platforms.</p>
<p>So let's see how you can handle this using the abstract factory method.</p>
<h3 id="heading-step-1-define-abstract-product-interfaces">Step 1: Define Abstract Product Interfaces</h3>
<p>Before we dive into this implementation, it's important to understand what abstract product interfaces are. An abstract product interface is essentially a contract that defines what methods a product must implement, without specifying how they're implemented.</p>
<p>Think of it as a blueprint that ensures all related products share a common structure. In our case, we're defining three core functionalities that every platform must provide:</p>
<ol>
<li><p>Theme management</p>
</li>
<li><p>Widget handling</p>
</li>
<li><p>Architecture configuration.</p>
</li>
</ol>
<p>By creating these abstract interfaces first, we establish a consistent API that all platform-specific implementations will follow.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThemeManager</span> </span>{
  Future&lt;<span class="hljs-built_in">String</span>&gt; getTheme();
}

<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WidgetHandler</span> </span>{
  Future&lt;<span class="hljs-built_in">bool</span>&gt; getWidget();
}

<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArchitechtureHandler</span> </span>{
  Future&lt;<span class="hljs-built_in">String</span>&gt; getArchitechture();
}
</code></pre>
<p>Here, we’re defining three base functionalities that every platform will implement: theme, widgets, and architecture.</p>
<p>Each interface declares a single method that returns platform-specific information.</p>
<p>The <code>ThemeManager</code> retrieves theme data, <code>WidgetHandler</code> determines widget compatibility, and <code>ArchitechtureHandler</code> provides architecture details.</p>
<h3 id="heading-step-2-implement-platform-specific-products">Step 2: Implement Platform-Specific Products</h3>
<p>Now that we have our abstract interfaces defined, we need to create concrete implementations for each platform. This step is where we provide the actual, platform-specific behavior for each product type. Think of this as filling in the blueprint with real details.</p>
<p>While the abstract interfaces told us what methods we need, these concrete classes tell us how those methods behave on each specific platform. Each platform (Android, iOS, Linux) will have its own unique implementation of themes, widgets, and architecture.</p>
<h4 id="heading-android">Android:</h4>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AndroidThemeManager</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ThemeManager</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">String</span>&gt; getTheme() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Android Theme"</span>;
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AndroidWidgetHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">WidgetHandler</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">bool</span>&gt; getWidget() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AndroidArchitechtureHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ArchitechtureHandler</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">String</span>&gt; getArchitechture() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Android Architecture"</span>;
  }
}
</code></pre>
<p>For Android, we're creating three specific product classes. The <code>AndroidThemeManager</code> returns Material Design theme data, the <code>AndroidWidgetHandler</code> returns true to indicate that Android supports home screen widgets, and the <code>AndroidArchitechtureHandler</code> provides information about Android's architecture (which could include details about ARM, x86, or other processor architectures).</p>
<h4 id="heading-ios">iOS:</h4>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IOSThemeManager</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ThemeManager</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">String</span>&gt; getTheme() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"IOS Theme"</span>;
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IOSWidgetHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">WidgetHandler</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">bool</span>&gt; getWidget() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IOSArchitechtureHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ArchitechtureHandler</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">String</span>&gt; getArchitechture() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"iOS Architecture"</span>;
  }
}
</code></pre>
<p>The iOS implementations follow the same structure but provide iOS-specific values. Notice that <code>IOSWidgetHandler</code> returns false, this could represent a scenario where certain widget features aren't available or behave differently on iOS compared to Android.</p>
<h4 id="heading-linux">Linux:</h4>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinuxThemeManager</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ThemeManager</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">String</span>&gt; getTheme() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Linux Theme"</span>;
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinuxWidgetHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">WidgetHandler</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">bool</span>&gt; getWidget() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinuxArchitechtureHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ArchitechtureHandler</span> </span>{
  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">String</span>&gt; getArchitechture() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Linux Architecture"</span>;
  }
}
</code></pre>
<p>Similarly, Linux gets its own set of implementations, providing Linux-specific theme data and architecture information.</p>
<h3 id="heading-step-3-define-the-abstract-factory-interface">Step 3: Define the Abstract Factory Interface</h3>
<p>With our product classes ready, we now need to create the factory that will produce them.</p>
<p>The abstract factory interface is the master blueprint that declares which products our factory must be able to create. This interface doesn't create anything itself, it simply declares that any concrete factory must provide methods to create all three product types (theme, widget, and architecture handlers). This ensures that, regardless of which platform factory we use, we can always access all three functionalities.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppFactory</span> </span>{
  ThemeManager themeManager();
  WidgetHandler widgetManager();
  ArchitechtureHandler architechtureHandler();
}
</code></pre>
<p>Here, we define a factory blueprint. Any platform specific factory will have to implement all three functionalities. This guarantees consistency: every platform will have all three capabilities available.</p>
<h3 id="heading-step-4-implement-platform-specific-factories">Step 4: Implement Platform Specific Factories</h3>
<p>This is where everything comes together. We're now creating the actual factories that will produce the platform-specific products we defined earlier. Each factory is responsible for creating all the related products for its platform. The key advantage here is encapsulation: the factory knows how to create all the related objects for a platform, and it ensures they're compatible with each other. For example, <code>AndroidFactory</code> creates Android-specific theme managers, widget handlers, and architecture handlers that all work together seamlessly.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AndroidFactory</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AppFactory</span> </span>{
  <span class="hljs-meta">@override</span>
  ThemeManager themeManager() =&gt; AndroidThemeManager();

  <span class="hljs-meta">@override</span>
  WidgetHandler widgetManager() =&gt; AndroidWidgetHandler();

  <span class="hljs-meta">@override</span>
  ArchitechtureHandler architechtureHandler() =&gt; AndroidArchitechtureHandler();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IOSFactory</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AppFactory</span> </span>{
  <span class="hljs-meta">@override</span>
  ThemeManager themeManager() =&gt; IOSThemeManager();

  <span class="hljs-meta">@override</span>
  WidgetHandler widgetManager() =&gt; IOSWidgetHandler();

  <span class="hljs-meta">@override</span>
  ArchitechtureHandler architechtureHandler() =&gt; IOSArchitechtureHandler();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LinuxFactory</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AppFactory</span> </span>{
  <span class="hljs-meta">@override</span>
  ThemeManager themeManager() =&gt; LinuxThemeManager();

  <span class="hljs-meta">@override</span>
  WidgetHandler widgetManager() =&gt; LinuxWidgetHandler();

  <span class="hljs-meta">@override</span>
  ArchitechtureHandler architechtureHandler() =&gt; LinuxArchitechtureHandler();
}
</code></pre>
<p>Each concrete factory (AndroidFactory, IOSFactory, LinuxFactory) implements all three methods from the <code>AppFactory</code> interface. When you call <code>themeManager()</code> on <code>AndroidFactory</code>, you get an <code>AndroidThemeManager</code>. When you call it on <code>IOSFactory</code>, you get an <code>IOSThemeManager</code>. The same pattern applies to all products.</p>
<h3 id="heading-step-5-client-code-using-abstract-factory">Step 5: Client Code Using Abstract Factory</h3>
<p>Finally, we create the client code that uses our abstract factory. This is the layer that your application will actually interact with. The beauty of this pattern is that the client code doesn't need to know anything about the specific platform implementations, it just works with the abstract factory interface.</p>
<p>The <code>AppBaseFactory</code> class accepts any factory that implements <code>AppFactory</code> and provides a simple method to initialize all platform settings. The <code>CheckDevice</code> class determines which factory to use based on the current platform, completely abstracting this decision away from the rest of your application.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppBaseFactory</span> </span>{
  AppBaseFactory({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.<span class="hljs-keyword">factory</span>});
  AppFactory <span class="hljs-keyword">factory</span>;

  getAppSettings() {
    <span class="hljs-keyword">factory</span>
      ..architechtureHandler()
      ..themeManager()
      ..widgetManager();
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CheckDevice</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">get</span>() {
    <span class="hljs-keyword">if</span> (Platform.isAndroid) <span class="hljs-keyword">return</span> AndroidFactory();
    <span class="hljs-keyword">if</span> (Platform.isIOS) <span class="hljs-keyword">return</span> IOSFactory();
    <span class="hljs-keyword">if</span> (Platform.isLinux) <span class="hljs-keyword">return</span> LinuxFactory();
    <span class="hljs-keyword">throw</span> UnsupportedError(<span class="hljs-string">"Platform not supported"</span>);
  }
}

<span class="hljs-comment">// Usage</span>
AppBaseFactory(<span class="hljs-keyword">factory</span>: CheckDevice.<span class="hljs-keyword">get</span>()).getAppSettings();
</code></pre>
<p>Here's what's happening in this code:</p>
<p>The <code>AppBaseFactory</code> class acts as a wrapper around any <code>AppFactory</code> implementation. It provides a convenient <code>getAppSettings()</code> method that initializes all three components (architecture handler, theme manager, and widget manager) using Dart's cascade notation.</p>
<p>The <code>CheckDevice</code> class contains the platform detection logic. Its static <code>get()</code> method checks the current platform and returns the appropriate factory. This centralizes all platform detection in one place. When you call <code>AppBaseFactory(factory: CheckDevice.get()).getAppSettings()</code>, the code automatically detects your platform, creates the right factory, and initializes all platform-specific components, all without the calling code needing to know any platform-specific details.</p>
<p>Each platform factory produces all related products. The client only interacts with <code>AppBaseFactory</code>, remaining unaware of the internal implementation. This ensures your code is scalable, maintainable, and consistent.</p>
<h2 id="heading-real-world-application-payment-provider-management">Real-World Application: Payment Provider Management</h2>
<p>Another good use case for abstract factory is when you need to switch between multiple payment providers in your application and you only want to expose the necessary functionality to the client (presentation layer).</p>
<p>The abstract factory design pattern properly helps you manage this scenario in terms of concrete implementation, encapsulation, clean code, separation of concerns, and proper code structure and management. For example, you might support Stripe, PayPal, and Flutterwave in your application.</p>
<p>Each provider requires different initialization, transaction processing, and webhook handling. By using the Abstract Factory pattern, you can create a consistent interface for all payment operations while keeping provider-specific details encapsulated within their respective factory implementations.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You should now feel more comfortable deciding when to use the Factory design pattern vs the Abstract Factory design pattern.</p>
<p>Understanding the factory and abstract factory patterns and their usages properly will help with object creation based on the particular use case you are trying to implement.</p>
<p>The Factory Pattern is ideal when you need one product and want to encapsulate creation logic while the Abstract Factory Pattern works well when you have multiple related products across platforms, need consistency, and want scalability. Using these patterns will help you write clean, maintainable, and scalable Flutter apps.</p>
<p>They give you a systematic approach to object creation and prevent messy, hard-to-maintain code as your app grows.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the Singleton Design Pattern in Flutter: Lazy, Eager, and Factory Variations ]]>
                </title>
                <description>
                    <![CDATA[ In software engineering, sometimes you need only one instance of a class across your entire application. Creating multiple instances in such cases can lead to inconsistent behavior, wasted memory, or resource conflicts. The Singleton Design Pattern i... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-singleton-design-pattern-in-flutter-lazy-eager-and-factory-variations/</link>
                <guid isPermaLink="false">69740b7bc3e68b8de44a179f</guid>
                
                    <category>
                        <![CDATA[ Singleton Design Pattern ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Object Oriented Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ood ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ flutter development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Factory Design Pattern ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mobile Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mobile app development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwaseyi Fatunmole ]]>
                </dc:creator>
                <pubDate>Fri, 23 Jan 2026 23:59:55 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769212761076/11d41d2a-8efa-4ddb-9ee2-218f5be00d9f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In software engineering, sometimes you need only one instance of a class across your entire application. Creating multiple instances in such cases can lead to inconsistent behavior, wasted memory, or resource conflicts.</p>
<p>The Singleton Design Pattern is a creational design pattern that solves this problem by ensuring that a class has exactly one instance and provides a global point of access to it.</p>
<p>This pattern is widely used in mobile apps, backend systems, and Flutter applications for managing shared resources such as:</p>
<ul>
<li><p>Database connections</p>
</li>
<li><p>API clients</p>
</li>
<li><p>Logging services</p>
</li>
<li><p>Application configuration</p>
</li>
<li><p>Security checks during app bootstrap</p>
</li>
</ul>
<p>In this article, we'll explore what the Singleton pattern is, how to implement it in Flutter/Dart, its variations (eager, lazy, and factory), and physical examples. By the end, you'll understand the proper way to use this pattern effectively and avoid common pitfalls.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-singleton-pattern">What is the Singleton Pattern?</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-when-to-use-the-singleton-pattern">When to Use the Singleton Pattern</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-singleton-class">How to Create a Singleton Class</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-eager-singleton">Eager Singleton</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lazy-singleton">Lazy Singleton</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-choosing-between-eager-and-lazy">Choosing Between Eager and Lazy</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-factory-constructors-in-the-singleton-pattern">Factory Constructors in the Singleton Pattern</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-are-factory-constructors">What Are Factory Constructors?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-implementing-singleton-with-factory-constructor">Implementing Singleton with Factory Constructor</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-when-not-to-use-a-singleton">When Not to Use a Singleton</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-singletons-can-be-problematic">Why Singletons Can Be Problematic</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-scenarios-where-you-should-avoid-singletons">Scenarios Where You Should Avoid Singletons</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-general-guidelines">General Guidelines</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before diving into this tutorial, you should have:</p>
<ol>
<li><p>Basic understanding of the Dart programming language</p>
</li>
<li><p>Familiarity with Object-Oriented Programming (OOP) concepts, particularly classes and constructors</p>
</li>
<li><p>Basic knowledge of Flutter development (helpful but not required)</p>
</li>
<li><p>Understanding of static variables and methods in Dart</p>
</li>
<li><p>Familiarity with the concept of class instantiation</p>
</li>
</ol>
<h2 id="heading-what-is-the-singleton-pattern">What is the Singleton Pattern?</h2>
<p>The Singleton pattern is a creational design pattern that ensures a class has only one instance and that there is a global point of access to the instance.</p>
<p>Again, this is especially powerful when managing shared resources across an application.</p>
<h3 id="heading-when-to-use-the-singleton-pattern">When to Use the Singleton Pattern</h3>
<p>You should use a Singleton when you are designing parts of your system that must exist once, such as:</p>
<ol>
<li><p>Global app state (user session, auth token, app config)</p>
</li>
<li><p>Shared services (logger, API client, database connection)</p>
</li>
<li><p>Resource heavy logic (encryption handlers, ML models, cache manager)</p>
</li>
<li><p>Application boot security (run platform-specific root/jailbreak checks)</p>
</li>
</ol>
<p>For example, in a Flutter app, Android may check developer mode or root status, while iOS checks jailbroken device state. A Singleton security class is a perfect way to enforce that these checks run once globally during app startup.</p>
<h2 id="heading-how-to-create-a-singleton-class">How to Create a Singleton Class</h2>
<p>We have two major ways of creating a singleton class:</p>
<ol>
<li><p>Eager Instantiation</p>
</li>
<li><p>Lazy Instantiation</p>
</li>
</ol>
<h3 id="heading-eager-singleton">Eager Singleton</h3>
<p>This is where the Singleton is created at load time, whether it's used or not.</p>
<p>In this case, the instance of the singleton class as well as any initialization logic runs at load time, regardless of when this class is actually needed or used. Here's how it works:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EagerSingleton</span> </span>{
  EagerSingleton._internal();
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> EagerSingleton _instance = EagerSingleton._internal();

  <span class="hljs-keyword">static</span> EagerSingleton <span class="hljs-keyword">get</span> instance =&gt; _instance;

  <span class="hljs-keyword">void</span> sayHello() =&gt; <span class="hljs-built_in">print</span>(<span class="hljs-string">"Hello from Eager Singleton"</span>);
}

<span class="hljs-comment">//usage</span>
<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Accessing the singleton globally</span>
  EagerSingleton.instance.sayHello();
}
</code></pre>
<h4 id="heading-how-the-eager-singleton-works">How the Eager Singleton Works</h4>
<p>Let's break down what's happening in this implementation:</p>
<p>First, <code>EagerSingleton._internal()</code> is a private named constructor (notice the underscore prefix). This prevents external code from creating new instances using <code>EagerSingleton()</code>. The only way to get an instance is through the controlled mechanism we're about to define.</p>
<p>Next, <code>static final EagerSingleton _instance = EagerSingleton._internal();</code> is the key line. This creates the single instance immediately when the class is first loaded into memory. Because it's <code>static final</code>, it belongs to the class itself (not any particular instance) and can only be assigned once. The instance is created right here, at declaration time.</p>
<p>The <code>static EagerSingleton get instance =&gt; _instance;</code> getter provides global access to that single instance. Whenever you call <code>EagerSingleton.instance</code> anywhere in your code, you're getting the exact same object that was created when the class loaded.</p>
<p>Finally, <code>sayHello()</code> is just a regular method to demonstrate that the singleton works. You could replace this with any business logic your singleton needs to perform.</p>
<p>When you run the code in <code>main()</code>, the class loads, the instance is created immediately, and <code>EagerSingleton.instance.sayHello()</code> accesses that pre-created instance to call the method.</p>
<h4 id="heading-pros">Pros:</h4>
<ol>
<li><p>This is simple and thread safe, meaning it's not affected by concurrency, especially when your app runs on multithreads.</p>
</li>
<li><p>It's ideal if the instance is lightweight and may be accessed frequently.</p>
</li>
</ol>
<h4 id="heading-cons">Cons:</h4>
<ol>
<li>If this instance is never used through the runtime, it results in wasted memory and could impact application performance.</li>
</ol>
<h3 id="heading-lazy-singleton">Lazy Singleton</h3>
<p>In this case, the singleton instance is only created when the class is called or needed in runtime. Here, a trigger needs to happen before the instance is created. Let's see an example:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LazySingleton</span> </span>{
  LazySingleton._internal(); 
  <span class="hljs-keyword">static</span> LazySingleton? _instance;

  <span class="hljs-keyword">static</span> LazySingleton <span class="hljs-keyword">get</span> instance {
    _instance ??= LazySingleton._internal();
    <span class="hljs-keyword">return</span> _instance!;
  }

  <span class="hljs-keyword">void</span> sayHello() =&gt; <span class="hljs-built_in">print</span>(<span class="hljs-string">"Hello from LazySingleton"</span>);
}

<span class="hljs-comment">//usage </span>
<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Accessing the singleton globally</span>
  LazySingleton.instance.sayHello();
}
</code></pre>
<h4 id="heading-how-the-lazy-singleton-works">How the Lazy Singleton Works</h4>
<p>The lazy implementation differs from eager in one crucial way: timing.</p>
<p>Again, <code>LazySingleton._internal()</code> is a private constructor that prevents external instantiation.</p>
<p>But notice that <code>static LazySingleton? _instance;</code> is declared as nullable and not initialized. Unlike the eager version, no instance is created at load time. The variable simply exists as <code>null</code> until it's needed.</p>
<p>The magic happens in the getter: <code>_instance ??= LazySingleton._internal();</code> uses Dart's null-aware assignment operator. This line says "if <code>_instance</code> is null, create a new instance and assign it. Otherwise, keep the existing one." This is the lazy initialization: the instance is only created the first time someone accesses it.</p>
<p>The first time you call <code>LazySingleton.instance</code>, <code>_instance</code> is null, so a new instance is created. Every subsequent call finds that <code>_instance</code> already exists, so it just returns that same instance.</p>
<p>The <code>return _instance!;</code> uses the null assertion operator because we know <code>_instance</code> will never be null at this point (we just ensured it's not null in the previous line).</p>
<p>This approach saves memory because if you never call <code>LazySingleton.instance</code> in your app, the instance never gets created.</p>
<h4 id="heading-pros-1">Pros:</h4>
<ol>
<li><p>Saves application memory, as it only creates what is needed in runtime.</p>
</li>
<li><p>Avoids memory leaks.</p>
</li>
<li><p>Is ideal for resource heavy objects while considering application performance.</p>
</li>
</ol>
<h4 id="heading-cons-1">Cons:</h4>
<ol>
<li>Could be difficult to manage in multithreaded environments, as you have to ensure thread safety while following this pattern.</li>
</ol>
<h3 id="heading-choosing-between-eager-and-lazy">Choosing Between Eager and Lazy</h3>
<p>Now that we've broken down these two major types of singleton instantiation, it's worthy of note that you'll need to be intentional while deciding whether to create a singleton the eager or lazy way. Your use case/context should help you determine what singleton pattern you need to apply during object creation.</p>
<p>As an engineer, you need to ask yourself these questions when using a singleton for object creation:</p>
<ol>
<li><p>Do I need this class instantiated when the app loads?</p>
</li>
<li><p>Based on the user journey, will this class always be needed during every session?</p>
</li>
<li><p>Can a user journey be completed without needing to call any logic in this class?</p>
</li>
</ol>
<p>These three questions will determine what pattern (eager or lazy) you should use to fulfill best practices while maintaining scalability and high performance in your application.</p>
<h2 id="heading-factory-constructors-in-the-singleton-pattern">Factory Constructors in the Singleton Pattern</h2>
<p>Applying factory constructors in the Singleton pattern can be powerful if you use them properly. But first, let's understand what factory constructors are.</p>
<h3 id="heading-what-are-factory-constructors">What Are Factory Constructors?</h3>
<p>A factory constructor in Dart is a special type of constructor that doesn't always create a new instance of its class. Unlike regular constructors that must return a new instance, factory constructors can:</p>
<ol>
<li><p>Return an existing instance (perfect for singletons)</p>
</li>
<li><p>Return a subclass instance</p>
</li>
<li><p>Apply logic before deciding what to return</p>
</li>
<li><p>Perform validation or initialization before returning an object</p>
</li>
</ol>
<p>The <code>factory</code> keyword tells Dart that this constructor has the flexibility to return any instance of the class (or its subtypes), not necessarily a fresh one.</p>
<h3 id="heading-implementing-singleton-with-factory-constructor">Implementing Singleton with Factory Constructor</h3>
<p>This allows you to apply initialization logic while your class instance is being created before returning the instance.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FactoryLazySingleton</span> </span>{
  FactoryLazySingleton._internal();
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> FactoryLazySingleton _instance = FactoryLazySingleton._internal();

  <span class="hljs-keyword">static</span> FactoryLazySingleton <span class="hljs-keyword">get</span> instance =&gt; _instance;

  <span class="hljs-keyword">factory</span> FactoryLazySingleton() {
    <span class="hljs-comment">// Your logic runs here</span>
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Factory constructor called"</span>);
    <span class="hljs-keyword">return</span> _instance;
  }
}
</code></pre>
<h4 id="heading-how-the-factory-constructor-singleton-works">How the Factory Constructor Singleton Works</h4>
<p>This implementation combines aspects of both eager and lazy patterns with additional control.</p>
<p>The <code>FactoryLazySingleton._internal()</code> private constructor and <code>static final _instance</code> create an eager singleton. The instance is created immediately when the class loads.</p>
<p>The <code>static get instance</code> provides the traditional singleton access pattern we've seen before.</p>
<p>But the interesting part is the <code>factory FactoryLazySingleton()</code> constructor. This is a public constructor that looks like a normal constructor call, but behaves differently. When you call <code>FactoryLazySingleton()</code>, instead of creating a new instance, it runs whatever logic you've placed inside (in this case, a print statement), then returns the existing <code>_instance</code>.</p>
<p>This pattern is powerful because:</p>
<ol>
<li><p>You can log when someone tries to create an instance</p>
</li>
<li><p>You can validate conditions before returning the instance</p>
</li>
<li><p>You can apply configuration based on parameters passed to the factory</p>
</li>
<li><p>You can choose to return different singleton instances based on conditions</p>
</li>
</ol>
<p>For example, you might have different configuration singletons for development vs production:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">factory</span> FactoryLazySingleton({<span class="hljs-built_in">bool</span> isProduction = <span class="hljs-keyword">false</span>}) {
  <span class="hljs-keyword">if</span> (isProduction) {
    <span class="hljs-comment">// Apply production configuration</span>
    _instance.configure(productionSettings);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Apply development configuration</span>
    _instance.configure(devSettings);
  }
  <span class="hljs-keyword">return</span> _instance;
}
</code></pre>
<h4 id="heading-pros-2">Pros</h4>
<ol>
<li><p>You can add logic before returning an instance</p>
</li>
<li><p>You can cache or reuse the same object</p>
</li>
<li><p>You can dynamically return a subtype if needed</p>
</li>
<li><p>You avoid unnecessary instantiation</p>
</li>
<li><p>You can inject configuration or environment logic</p>
</li>
</ol>
<h4 id="heading-cons-2">Cons</h4>
<ol>
<li><p>Adds slight complexity compared to simple getter access</p>
</li>
<li><p>The factory constructor syntax might confuse developers unfamiliar with the pattern</p>
</li>
<li><p>If overused with complex logic, it can make debugging harder</p>
</li>
<li><p>Can create misleading code where <code>FactoryLazySingleton()</code> looks like it creates a new instance but doesn't</p>
</li>
</ol>
<h2 id="heading-when-not-to-use-a-singleton">When Not to Use a Singleton</h2>
<p>While singletons are powerful, they're not always the right solution. Understanding when to avoid them is just as important as knowing when to use them.</p>
<h3 id="heading-why-singletons-can-be-problematic">Why Singletons Can Be Problematic</h3>
<p>Singletons create global state, which can make your application harder to reason about and test. They introduce tight coupling between components that shouldn't necessarily know about each other, and they can make it difficult to isolate components for unit testing.</p>
<h3 id="heading-scenarios-where-you-should-avoid-singletons">Scenarios Where You Should Avoid Singletons</h3>
<p>Avoid using the Singleton pattern if:</p>
<h4 id="heading-you-need-multiple-independent-instances">You need multiple independent instances</h4>
<p>If different parts of your app need their own separate configurations or states, singletons force you into a one-size-fits-all approach.</p>
<p>For example, if you're building a multi-tenant application where each tenant needs isolated data, a singleton would cause data to bleed between tenants.</p>
<p><strong>Alternative</strong>: Use dependency injection to pass different instances to different parts of your app. Each component receives the specific instance it needs through its constructor or a service locator.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Instead of singleton</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserRepository</span> </span>{
  <span class="hljs-keyword">final</span> DatabaseConnection db;
  UserRepository(<span class="hljs-keyword">this</span>.db); 
}

<span class="hljs-comment">// Usage</span>
<span class="hljs-keyword">final</span> dbForTenantA = DatabaseConnection(tenantId: <span class="hljs-string">'A'</span>);
<span class="hljs-keyword">final</span> dbForTenantB = DatabaseConnection(tenantId: <span class="hljs-string">'B'</span>);
<span class="hljs-keyword">final</span> repoA = UserRepository(dbForTenantA);
<span class="hljs-keyword">final</span> repoB = UserRepository(dbForTenantB);
</code></pre>
<h4 id="heading-your-architecture-avoids-shared-global-state">Your architecture avoids shared global state</h4>
<p>Modern architectural patterns like BLoC, Provider, or Riverpod in Flutter specifically aim to avoid global mutable state. Singletons work against these patterns by reintroducing global state.</p>
<p><strong>Alternative</strong>: Use state management solutions designed for Flutter. Provider, Riverpod, BLoC, or GetX offer better ways to share data across your app while maintaining testability and avoiding tight coupling.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Using Provider instead of singleton</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppConfig</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> apiUrl;
  AppConfig(<span class="hljs-keyword">this</span>.apiUrl);
}

<span class="hljs-comment">// Provide it at the top level</span>
<span class="hljs-keyword">void</span> main() {
  runApp(
    Provider&lt;AppConfig&gt;(
      create: (_) =&gt; AppConfig(<span class="hljs-string">'https://api.example.com'</span>),
      child: MyApp(),
    ),
  );
}

<span class="hljs-comment">// Access it anywhere in the widget tree</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyWidget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">final</span> config = Provider.of&lt;AppConfig&gt;(context);

  }
}
</code></pre>
<h4 id="heading-it-forces-tight-coupling-between-unrelated-classes">It forces tight coupling between unrelated classes</h4>
<p>When multiple unrelated classes depend on the same singleton, they become indirectly coupled. Changes to the singleton affect all these classes, making the codebase fragile and hard to refactor.</p>
<p><strong>Alternative</strong>: Use interfaces and dependency injection. Define what behavior you need through an interface, then inject implementations. This way, classes depend on abstractions, not concrete singletons.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Define an interface</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Logger</span> </span>{
  <span class="hljs-keyword">void</span> log(<span class="hljs-built_in">String</span> message);
}

<span class="hljs-comment">// Implementation</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConsoleLogger</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Logger</span> </span>{
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> log(<span class="hljs-built_in">String</span> message) =&gt; <span class="hljs-built_in">print</span>(message);
}

<span class="hljs-comment">// Classes depend on the interface, not a singleton</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentService</span> </span>{
  <span class="hljs-keyword">final</span> Logger logger;
  PaymentService(<span class="hljs-keyword">this</span>.logger);

  <span class="hljs-keyword">void</span> processPayment() {
    logger.log(<span class="hljs-string">'Processing payment'</span>);
  }
}

<span class="hljs-comment">// Easy to test with mock</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MockLogger</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Logger</span> </span>{
  <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt; logs = [];
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> log(<span class="hljs-built_in">String</span> message) =&gt; logs.add(message);
}
</code></pre>
<h4 id="heading-you-need-clean-isolated-testing">You need clean, isolated testing</h4>
<p>Singletons maintain state between tests, causing test pollution where one test affects another. This makes tests unreliable and order-dependent.</p>
<p><strong>Alternative</strong>: Use dependency injection and create fresh instances for each test. Most testing frameworks support this pattern, allowing you to inject mocks or fakes easily.</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Testable code</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderService</span> </span>{
  <span class="hljs-keyword">final</span> PaymentProcessor processor;
  OrderService(<span class="hljs-keyword">this</span>.processor);
}

<span class="hljs-comment">// In tests</span>
<span class="hljs-keyword">void</span> main() {
  test(<span class="hljs-string">'processes order successfully'</span>, () {
    <span class="hljs-keyword">final</span> mockProcessor = MockPaymentProcessor();
    <span class="hljs-keyword">final</span> service = OrderService(mockProcessor); 

  });
}
</code></pre>
<h3 id="heading-general-guidelines">General Guidelines</h3>
<p>Use singletons sparingly and only when you truly need exactly one instance of something for the entire application lifecycle. Good candidates include logging systems, application-level configuration, and hardware interface managers.</p>
<p>For most other cases, prefer dependency injection, state management solutions, or simply passing instances where needed. These approaches make your code more flexible, testable, and maintainable.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The Singleton pattern is a powerful creational tool, but like every tool, you should use it strategically.</p>
<p>Overusing singletons can make apps tightly coupled, hard to test, and less maintainable.</p>
<p>But when used correctly, the Singleton pattern helps you save memory, enforce consistency, and control object lifecycle beautifully.</p>
<p>The key is understanding your specific use case and choosing the right implementation approach – whether eager, lazy, or factory-based – that best serves your application's needs while maintaining clean, testable code.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the Optimistic UI Pattern with the useOptimistic() Hook in React ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever clicked a Like icon on a social media app and noticed the count jumps instantly? The colour of the icon changes at the same time, even before the server finishes the action. Now imagine you hit the same Like button, but it takes its swe... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-optimistic-ui-pattern-with-the-useoptimistic-hook-in-react/</link>
                <guid isPermaLink="false">693c5d28a2bfa1537f407a65</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tapas Adhikary ]]>
                </dc:creator>
                <pubDate>Fri, 12 Dec 2025 18:21:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765561440350/c3546e6c-8b23-476a-86d4-b63fd2cb9f6c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever clicked a <code>Like</code> icon on a social media app and noticed the count jumps instantly? The colour of the icon changes at the same time, even before the server finishes the action.</p>
<p>Now imagine you hit the same Like button, but it takes its sweet time in making the server call, performing the DB updates, and getting you the response back to update the state of the Like button.</p>
<p>Which experience would you like the most? You are most likely to select the first scenario. We all love “instant feedback”. The magic of instant feedback is powered by a pattern called the <code>Optimistic UI Pattern</code>.</p>
<p>In this article, we will uncover:</p>
<ul>
<li><p>What does Optimistic UI really mean?</p>
</li>
<li><p>Why does it massively improve the user experience?</p>
</li>
<li><p>How does React 19’s new useOptimistic() hook make it easier than ever?</p>
</li>
<li><p>How to implement a real-world scenario using the Optimistic Pattern</p>
</li>
<li><p>A bunch of use cases where you will be able to use this pattern.</p>
</li>
</ul>
<p>By the end, you will be proactively thinking of using this design pattern to improve the UX of your project.</p>
<p>This article is also available as a video tutorial as part of the <a target="_blank" href="https://www.youtube.com/playlist?list=PLIJrr73KDmRyQVT__uFZvaVfWPdfyMFHC">15 Days of React Design Patterns</a> <a target="_blank" href="https://www.youtube.com/playlist?list=PLIJrr73KDmRyQVT__uFZvaVfWPdfyMFHC">initiative</a>. Please check it out.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/x03yX-yNxas" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-optimistic-ui">What is Optimistic UI</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-it-work-under-the-hood">How Does it Work Under the Hood?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-an-optimistic-like-button">How to Build an Optimistic Like Button</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-pitfalls-and-anti-patterns">The Pitfalls and Anti-Patterns</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-15-days-of-react-design-patterns">15 Days of React Design Patterns</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-before-we-end">Before We End...</a></p>
</li>
</ol>
<h2 id="heading-what-is-optimistic-ui">What is Optimistic UI?</h2>
<p><code>Optimistic UI</code> (also known as optimistic updates) is a pattern that helps you update the UI immediately, assuming the server operation will succeed, and if it later fails, you roll back the UI to the correct state.</p>
<p>Instead of waiting for the round-trip of the client request, database write, server response, and then the UI render, the UI just updates instantly. This dramatically increases what’s called the <code>perceived speed</code>. The user of the application perceives the UI update as instant – but the actual operation may take place in the background.</p>
<h3 id="heading-without-an-optimistic-update">Without an Optimistic Update:</h3>
<p>If you’re not using the optimistic pattern, it’s just a traditional client-server mechanism, where:</p>
<ul>
<li><p>At the client side, a user interacts with a UI element.</p>
</li>
<li><p>An <a target="_blank" href="https://www.youtube.com/watch?v=WQdCffdPPKI">async call</a> (request) is made to the server.</p>
</li>
<li><p>The server processes the request and may make DB updates.</p>
</li>
<li><p>On a successful case, the server sends back the response to the client.</p>
</li>
<li><p>The client updates the relevant UI.</p>
</li>
<li><p>In an error case, the server sends back the error response to the client.</p>
</li>
<li><p>The client informs the user about the error.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765334108586/aabd3f16-b175-4b1d-ae33-94f33e1b894a.png" alt="Without an Optimistic Update" class="image--center mx-auto" width="1240" height="704" loading="lazy"></p>
<p>In this case, the user has to wait for the success/failure of the request to perceive any change after their interaction. This wait is neither uniform nor optimal. It may vary based on the network speed, network latency, and deployment strategies of the application.</p>
<h3 id="heading-with-an-optimistic-update">With an Optimistic Update:</h3>
<p>When you’re using an optimistic update, here’s how things go:</p>
<ul>
<li><p>At the client side, a user interacts with a UI element.</p>
</li>
<li><p>The UI gets updated instantly, and the user perceives the feedback immediately.</p>
</li>
<li><p>In parallel, in the background, the client initiates the server call.</p>
</li>
<li><p>The server processes the request and may make DB updates.</p>
</li>
<li><p>On a successful case, the server doesn’t do anything else, as the UI has been updated already, assuming this call will succeed.</p>
</li>
<li><p>In an error case, the server sends back the error response to the client.</p>
</li>
<li><p>The client rolls back the eager, optimistic UI update it made.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765334174203/e8bef9ba-28b6-45e0-8f22-0fc1468e3219.png" alt="With an Optimistic Update" class="image--center mx-auto" width="1189" height="892" loading="lazy"></p>
<p>In this case, the user doesn’t wait for the server round-trip to complete before the UI is updated. It’s much faster, assuming that, in most cases, the parallel server call will succeed.</p>
<p>With this comparison, we can now understand why Optimistic Updates matter in modern UI.</p>
<ul>
<li><p>It improves perceived speed</p>
</li>
<li><p>It keeps users engaged</p>
</li>
<li><p>It eliminates the awkward feelings like “Did my click work?”</p>
</li>
</ul>
<p>And so on. Optimistic updates are critical for real-time feeling features like chat messages, likes, comments, cart updates, poll votes, collaborative editing, and more. Even AI-driven apps that take time to respond benefit from optimistic placeholders like “Thinking…”, “Sending…” and so on.</p>
<h2 id="heading-how-does-it-work-under-the-hood">How Does it Work Under the Hood?</h2>
<p>Under the hood, there are actually two states:</p>
<ol>
<li><p>The Actual State: This is the actual source of truth. This data should be in sync with the server.</p>
</li>
<li><p>The Optimistic State: This is temporary and instantly shown to the user.</p>
</li>
</ol>
<p>When the server request succeeds, do nothing. Your optimistic state is now correct. If the server request fails, perform a rollback, and return UI the actual state.</p>
<p>React 19 introduced a built-in hook to help with this pattern called <code>useOptimistic()</code> . In the next section, we will take a deep dive into it with code and working internals.</p>
<h3 id="heading-the-useoptimistic-hook-in-react-19">The <code>useOptimistic()</code> Hook in React 19</h3>
<p><code>useOptimistic()</code> is a React hook introduced in React 19 to help with optimistic updates. The syntax and usage of the hook go like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
</code></pre>
<p>When an async action is underway, the <code>useOptimistic()</code> hook allows you to show different states.</p>
<p>It accepts:</p>
<ol>
<li><p><strong>currentState</strong>: your real source of truth (useState, Redux, server state, and so on)</p>
</li>
<li><p><strong>updateFn</strong>: a pure function that says how to compute the optimistic value</p>
</li>
</ol>
<p>It returns:</p>
<ol>
<li><p><strong>optimisticState</strong>: the temporary UI state</p>
</li>
<li><p><strong>addOptimisticUpdate(input)</strong>: function you call to apply immediate updates</p>
</li>
</ol>
<p>Take a look at the picture below. It shows the relationship between the current state and the optimistic state clearly:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765434835916/249e71eb-bba6-4b98-951a-feb397dc36e2.png" alt="Anatomy" class="image--center mx-auto" width="1744" height="781" loading="lazy"></p>
<p>Here’s what’s going on there:</p>
<ol>
<li><p>We pass the current state and an updater function to the <code>useOptimistic</code> hook.</p>
</li>
<li><p>The updater function takes the current state and a user input to compute and return the next optimistic state.</p>
</li>
<li><p>The input to the updater function is supplied using the <code>addOptimistic(input)</code> function.</p>
</li>
<li><p>Finally, the optimistic state value is used in the component.</p>
</li>
</ol>
<p>Let’s now build something exciting using this hook to understand its internals better.</p>
<h2 id="heading-how-to-build-an-optimistic-like-button">How to Build an Optimistic Like Button</h2>
<p>We will be building the Like button functionality optimistically. The flow will be like this:</p>
<ul>
<li><p>The user clicks on the Like button.</p>
</li>
<li><p>We update the Like button’s state immediately and optimistically.</p>
</li>
<li><p>In parallel, we send the server call to persist the value into the DB (we will simulate it)</p>
</li>
<li><p>Then we handle any error scenarios.</p>
</li>
</ul>
<p>First, let’s simulate a network call to the server using JavaScript’s Promise object and the <code>setTimeout()</code> web API:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// simulate a network call to the Server</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendLikeToServer</span>(<span class="hljs-params">postId</span>) </span>{
    <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> <span class="hljs-built_in">setTimeout</span>(r, <span class="hljs-number">700</span>));

    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() &lt; <span class="hljs-number">0.2</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Network failed"</span>);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Sent a like for the post id <span class="hljs-subst">${postId}</span>`</span>);
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span> };
}
</code></pre>
<p>The <code>sendLikeToServer</code> function takes a post ID as a parameter and simulates a fake network call using a Promise and a delay of 700 ms. It pretends to submit a request to the server to persist a post’s likes value.</p>
<p>To make it a bit more realistic, we have created a random error. The function will throw an error randomly so that we can understand the rollback scenario as well.</p>
<p>Next, we will create the real source of truth, the actual state for the Like count:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [likes, setLikes] = useState(initialLikes);
</code></pre>
<p>Then, create the optimistic state value with the <code>useOptimistic()</code> hook:</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> [optimisticLikes, addOptimisticLike] = useOptimistic(
        likes, <span class="hljs-function">(<span class="hljs-params">currentLikes, delta</span>) =&gt;</span> currentLikes + delta);
</code></pre>
<p>Let’s understand this declaration well:</p>
<ul>
<li><p>We have passed the actual state value (likes) and the updater function to the <code>useOptimistic()</code> hook.</p>
</li>
<li><p>Take a look at the updater function, <code>(currentLikes, delta) =&gt; currentLikes + delta</code>. It’s an arrow function that gets the current like value and a delta. It returns the sum of the current like value and the delta. The return value logic is your own business logic. For incrementing the like count, it makes sense to increase the current like value by a delta value (of 1).</p>
</li>
<li><p>Now, the question is, how do we get this delta value? Who passes it? That’s where the return values of <code>useOptimistic()</code> come in handy. The <code>addOptimisticLike</code> is a function through which we can pass that delta value. How? Let’s take a look.</p>
</li>
</ul>
<p>When someone clicks on the Like button, we need to handle the click event and increase the like count value. So here is a <code>handleLike()</code> function which does that:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> handleLike = <span class="hljs-keyword">async</span> () =&gt; {
        addOptimisticLike(<span class="hljs-number">1</span>);
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">await</span> sendLikeToServer(postId);
            setLikes(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> prev + <span class="hljs-number">1</span>);
        } <span class="hljs-keyword">catch</span> (err) {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Like failed:"</span>, err);
            setLikes(<span class="hljs-function">(<span class="hljs-params">s</span>) =&gt;</span> s); 
        }
};
</code></pre>
<p>A lot is happening here:</p>
<ul>
<li><p>We call the <code>addOptimisticLike()</code> function with a delta value of 1. This call will ensure that the updater function <code>(currentLikes, delta) =&gt; currentLikes + delta</code> of the <code>useOptimistic()</code> will be called. The return value will be set to the optimistic state, that is, <code>optimisticLikes</code>.</p>
</li>
<li><p>This optimistic state value we use in the JSX. So we can see the increased like count immediately.</p>
</li>
<li><p>Then we make the fake server call, and also update the actual state, provided the server call was successful.</p>
</li>
<li><p>In case of an error, the control goes into the catch-block, where we roll back the likes value to the previous one. This will also sync the optimistic state’s value with a rollback.</p>
</li>
</ul>
<p>Here is the complete code of the <code>LikeButton</code> component:</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">import</span> { startTransition, useOptimistic, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-comment">// simulate a network call to the Server</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendLikeToServer</span>(<span class="hljs-params">postId</span>) </span>{
    <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> <span class="hljs-built_in">setTimeout</span>(r, <span class="hljs-number">700</span>));

    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.random() &lt; <span class="hljs-number">0.2</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Network failed"</span>);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Sent a like for the post id <span class="hljs-subst">${postId}</span>`</span>);
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span> };
}

<span class="hljs-comment">// The Like Button Component</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">LikeButton</span>(<span class="hljs-params">{ postId, initialLikes = <span class="hljs-number">0</span> }</span>) </span>{
    <span class="hljs-comment">// the "real" source of truth for likes (committed)</span>
    <span class="hljs-keyword">const</span> [likes, setLikes] = useState(initialLikes);
    <span class="hljs-comment">// optimistic state and updater function</span>
    <span class="hljs-keyword">const</span> [optimisticLikes, addOptimisticLike] = useOptimistic(
        likes,
        <span class="hljs-function">(<span class="hljs-params">currentLikes, delta</span>) =&gt;</span> currentLikes + delta
    );

    <span class="hljs-keyword">const</span> handleLike = <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-comment">// 1) Apply optimistic change *immediately*</span>
        addOptimisticLike(<span class="hljs-number">1</span>);

        <span class="hljs-comment">// 2) Start server call in low priority to avoid blocking UI</span>

        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">await</span> sendLikeToServer(postId);
            <span class="hljs-comment">// On success, commit the real state update:</span>
            <span class="hljs-comment">// IMPORTANT: update the real state so optimistic snapshot eventually matches</span>
            setLikes(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> prev + <span class="hljs-number">1</span>);
        } <span class="hljs-keyword">catch</span> (err) {
            <span class="hljs-comment">// On error, rollback the real state (or trigger a refetch)</span>
            <span class="hljs-comment">// Because we never incremented likes (real), just leave likes unchanged</span>
            <span class="hljs-comment">// But we should show an error to user:</span>
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Like failed:"</span>, err);
            <span class="hljs-comment">// Optionally: show toast or set an error state</span>
            <span class="hljs-comment">// And — to force the optimistic view to refresh and reflect real state,</span>
            <span class="hljs-comment">// call setLikes to current value</span>
            setLikes(<span class="hljs-function">(<span class="hljs-params">s</span>) =&gt;</span> s); <span class="hljs-comment">// no-op but will cause optimistic to reflect the</span>
                                <span class="hljs-comment">// committed value Or you can trigger a re-fetch of the </span>
                                <span class="hljs-comment">// post state</span>
        }
    };

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleLike}</span>&gt;</span>❤️ {optimisticLikes}<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> startTransition(async () =&gt; handleLike())}&gt;
                ❤️ {optimisticLikes}
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>Have you noticed that we have wrapped the <code>handleLike()</code> call with the <code>startTransition</code>?</p>
<p>Without this, React gives us a warning:</p>
<blockquote>
<p>“An optimistic state update occurred outside a transition or action.”</p>
</blockquote>
<p>This is because optimistic updates are <strong>low-priority visual updates</strong>, not critical ones.</p>
<p>Using <code>startTransition()</code> ensures that:</p>
<ul>
<li><p>React doesn’t block rendering</p>
</li>
<li><p>We do not get the warning</p>
</li>
<li><p>We get a smooth, optimistic experience</p>
</li>
</ul>
<p>The transitions are part of React’s concurrency model that helps us improve the performance of React applications. If you are interested in learning various performance optimisation techniques, <a target="_blank" href="https://www.youtube.com/watch?v=G8Mk6lsSOcw">here is a two-part guide for you</a>.</p>
<h2 id="heading-the-pitfalls-and-anti-patterns">The Pitfalls and Anti-Patterns</h2>
<p>With any design pattern, we need to be aware of possible pitfalls, misuses, and anti-patterns. Here are a few things you should be aware of:</p>
<ul>
<li><p>Don’t assume that the server call will be successful. Network failure will happen, and you need to have a way to roll back. Rollback is the heart of optimistic UI. Omitting the rollback logic will cause adverse consequences.</p>
</li>
<li><p>Don’t try hiding the bad UX behind optimistic updates. The Optimistic UI is not a fix or replacement for poor designs.</p>
</li>
<li><p>Don’t perform any expensive work in optimistic updates. Keep the optimistic updater function lean, pure, and fast.</p>
</li>
</ul>
<h2 id="heading-15-days-of-react-design-patterns"><strong>15 Days of React Design Patterns</strong></h2>
<p>I have some great news for you: after my <em>40 days of the JavaScript</em> initiative, I have now started a brand new initiative called <a target="_blank" href="https://www.youtube.com/playlist?list=PLIJrr73KDmRyQVT__uFZvaVfWPdfyMFHC">15 Days of React Design Patterns</a>.</p>
<p>If you enjoyed learning from this article, I am sure you will love this series, featuring the 15+ most important React design patterns. Check it out and join for FREE:</p>
<p><a target="_blank" href="https://www.youtube.com/playlist?list=PLIJrr73KDmRyQVT__uFZvaVfWPdfyMFHC"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765439781697/751c2051-5dc2-4a88-bcc2-037f6ce0e91e.png" alt="https://www.youtube.com/playlist?list=PLIJrr73KDmRyQVT__uFZvaVfWPdfyMFHC" class="image--center mx-auto" width="1612" height="850" loading="lazy"></a></p>
<h2 id="heading-before-we-end"><strong>Before We End...</strong></h2>
<p>That’s all! I hope you found this article insightful. You can find all the source code used in this tutorial on the <a target="_blank" href="https://github.com/tapascript/15-days-of-react-design-patterns/tree/main/day-08">tapaScript GitHub</a>.</p>
<p><a target="_blank" href="https://github.com/tapascript/15-days-of-react-design-patterns/tree/main/day-03/compound-components-patterns">Let’s connect:</a></p>
<ul>
<li><p>Subscribe to my <a target="_blank" href="https://www.youtube.com/tapasadhikary?sub_confirmation=1">YouTube Channel</a>.</p>
</li>
<li><p>Grab the <a target="_blank" href="https://www.tapascript.io/books/react-hooks-cheatsheet">React Hooks Cheatsheet</a>.</p>
</li>
<li><p>Follow on <a target="_blank" href="https://www.linkedin.com/in/tapasadhikary/">LinkedIn</a> if you don't want to miss the daily dose of up-skilling tips.</p>
</li>
<li><p>Join my <a target="_blank" href="https://discord.gg/zHHXx4vc2H">Discord Server</a>, and let’s learn together.</p>
</li>
<li><p>Subscribe to my fortnightly newsletter, <a target="_blank" href="https://tapascript.substack.com/subscribe?utm_medium=fcc">The Commit Log</a>.</p>
</li>
</ul>
<p>See you soon with my next article. Until then, please take care of yourself and keep learning.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ System Design Patterns in Android Bluetooth [Full Handbook] ]]>
                </title>
                <description>
                    <![CDATA[ If you’ve ever opened the Android Bluetooth source code, you might know this feeling. You go in with the calm confidence of a developer who just wants to understand how things work. You open BluetoothAdapter.java and think, “Ah, this looks clean.” Th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/system-design-patterns-in-android-bluetooth-full-handbook/</link>
                <guid isPermaLink="false">6915f7d8453f11c904fade0c</guid>
                
                    <category>
                        <![CDATA[ aosp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bluetooth ]]>
                    </category>
                
                    <category>
                        <![CDATA[ System Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nikheel Vishwas Savant ]]>
                </dc:creator>
                <pubDate>Thu, 13 Nov 2025 15:23:04 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763047349934/78e1861c-62d3-44c8-adc3-971d6b63a7cc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you’ve ever opened the Android Bluetooth source code, you might know this feeling.</p>
<p>You go in with the calm confidence of a developer who just wants to understand how things work. You open <code>BluetoothAdapter.java</code> and think, “Ah, this looks clean.” Then you click through a few methods. Suddenly, you’re in <code>AdapterService.java</code>, then <code>StateMachine.java</code>, and before you realize it, you’re staring at a JNI bridge leading straight into native C++ code that talks to daemons with names like <code>bluetoothd</code>.</p>
<p>Somewhere between the Binder calls, message queues, and “Unexpected state” logs, your curiosity quietly turns into existential dread.</p>
<p>That, my friend, is the Android Bluetooth experience.</p>
<p>But here’s the twist: it’s not chaos. It’s choreography. Every message, callback, and native call exists for a reason. Android Bluetooth has been built, rebuilt, and evolved over more than a decade to support everything from old-school car kits to cutting-edge LE Audio.</p>
<p>Underneath that ever-expanding complexity lies a remarkably disciplined foundation built on <strong>system design patterns</strong>. These patterns are the reason Bluetooth can still work across thousands of devices, dozens of chip vendors, and millions of random user interactions that happen every second.</p>
<p>What’s fascinating is how the Bluetooth stack mirrors Android’s entire design philosophy: isolate complexity, define clear roles, and let components communicate through predictable contracts.</p>
<p>The app layer talks to managers. The managers talk to services. The services talk to native daemons. And the daemons finally talk to the hardware. Each layer speaks its own language but follows a shared rhythm –like musicians who have never met but somehow stay in tune.</p>
<p><img src="https://www.androidauthority.com/wp-content/uploads/2018/03/Bluetooth-Icon-Settings-Menu.jpg" alt="What is Bluetooth and how does it work? - Android Authority" width="1920" height="1080" loading="lazy"></p>
<p>Without these patterns, the system would collapse under its own ambition. Imagine writing logic for pairing, bonding, discovery, connection, streaming, and low-energy data transfer without structure. Every change would be a minefield.</p>
<p>Design patterns bring sanity to this chaos.</p>
<ul>
<li><p>The <strong>Manager-Service split</strong> ensures clear boundaries.</p>
</li>
<li><p>The <strong>State Machine</strong> keeps connection lifecycles predictable.</p>
</li>
<li><p>The <strong>Handler-Looper mechanism</strong> turns concurrency into an orderly queue.</p>
</li>
<li><p>The <strong>Facade</strong> hides native messiness behind friendly APIs.</p>
</li>
<li><p>And the <strong>Observer</strong> pattern lets everyone stay updated without tripping over each other.</p>
</li>
</ul>
<p>This article is about peeling back those layers and seeing the design ideas that quietly keep Android Bluetooth alive. We won’t just list patterns like a textbook. Instead, we’ll explore how each one appears in real AOSP code, why it exists, and how you can apply the same ideas to your own projects.</p>
<p>If you’ve ever wondered how something as temperamental as Bluetooth manages to stay mostly reliable, this is your backstage pass.</p>
<p>So grab your debugger, open a terminal window, and get ready to look at Bluetooth not as a mysterious black box, but as one of Android’s most elegant examples of long-term system design done right.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-the-manager-service-pattern-divide-and-delegate">The Manager–Service Pattern: Divide and Delegate</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-facade-pattern-making-complexity-look-simple">The Facade Pattern: Making Complexity Look Simple</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-state-machine-pattern-keeping-bluetooth-sane">The State Machine Pattern: Keeping Bluetooth Sane</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-handler-looper-pattern-message-driven-concurrency">The Handler–Looper Pattern: Message-Driven Concurrency</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-observer-pattern-when-bluetooth-talks-back">The Observer Pattern: When Bluetooth Talks Back</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-builder-pattern-making-gatt-bearable">The Builder Pattern: Making GATT Bearable</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-strategy-pattern-adapting-to-different-devices">The Strategy Pattern: Adapting to Different Devices</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-template-method-pattern-common-flows-custom-details">The Template Method Pattern: Common Flows, Custom Details</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-service-locator-pattern-finding-the-right-profile-at-runtime">The Service Locator Pattern: Finding the Right Profile at Runtime</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-layered-architecture-pattern-from-app-to-radio-without-losing-the-plot">The Layered Architecture Pattern: From App to Radio Without Losing the Plot</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-putting-it-all-together-designing-bluetooth-style-systems">Putting It All Together: Designing Bluetooth-Style Systems</a></p>
</li>
</ol>
<h2 id="heading-the-managerservice-pattern-divide-and-delegate">The Manager–Service Pattern: Divide and Delegate</h2>
<p>When you start exploring Android’s Bluetooth codebase, one of the first things you’ll notice is how often you come across the words “Manager” and “Service.” There is <code>BluetoothManagerService</code>, <code>AdapterService</code>, <code>GattService</code>, <code>A2dpService</code>, and many more.</p>
<p>At first, it seems repetitive and unnecessarily complicated. Why do we need so many layers just to connect to a pair of earbuds? Wouldn’t one class that says “connect” be enough? The short answer is no. The longer answer involves one of Android’s most reliable architectural habits: the separation of responsibility.</p>
<p>Think of a restaurant. The customers talk to the waiter. The waiter talks to the kitchen. The kitchen talks to suppliers. Everyone has a job. The waiter doesn’t need to know how to cook, and the chef doesn’t need to explain menu prices to customers. That separation is what keeps the whole operation smooth and manageable.</p>
<p>Android’s Bluetooth system works in exactly the same way. The <strong>Manager</strong> is like the waiter, the public face that interacts with apps, while the <strong>Service</strong> is like the kitchen, where the actual work happens out of sight.</p>
<p>When you write an app that uses Bluetooth, you might call something like <code>BluetoothAdapter.enable()</code> or <code>BluetoothDevice.connectGatt()</code>. These methods live inside Manager classes in the Android framework. They are deliberately simple, because their only job is to talk to the Bluetooth Service behind the scenes. That Service runs in another process entirely, one that has the necessary system permissions and the ability to interact with the native Bluetooth stack and hardware.</p>
<p>A small example from the Android source code shows this relationship very clearly:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BluetoothManagerService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">IBluetoothManager</span>.<span class="hljs-title">Stub</span> </span>{
    <span class="hljs-keyword">private</span> AdapterService mAdapterService;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">enable</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">if</span> (mAdapterService != <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span> mAdapterService.enable();
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }
}
</code></pre>
<p>At first glance, this looks trivial, but it demonstrates one of the most important ideas in the system. The <code>BluetoothManagerService</code> does not handle radio operations itself. Instead, it delegates to another internal class called <code>AdapterService</code>, which communicates with lower layers. That service will eventually pass instructions down to native C++ code, which then communicates with the Bluetooth controller chip through the Host Controller Interface.</p>
<p>This relay-style design has several advantages. The first is reliability. If the lower-level service crashes, the Manager layer can detect it and restart it, keeping the system stable. Because the Manager and the Service live in separate processes, your app will not crash when the service does. You might see Bluetooth temporarily toggle off and on again, but that recovery is intentional and automatic.</p>
<p>The second advantage is security. Every Bluetooth action goes through permission checks in the Manager layer before it reaches the Service. If an app without proper privileges tries to perform a restricted operation, the Manager stops it immediately. This prevents unsafe or malicious behavior and ensures that only trusted system components can access the hardware.</p>
<p>The third is flexibility. The Service layer can evolve without affecting the public API. That means Google and device manufacturers can modify or replace internal Bluetooth logic say, to support a new chipset or feature, without breaking existing apps. The Manager acts as a contract that remains stable even if the internal wiring changes.</p>
<p>If you trace what happens when you tap the Bluetooth toggle on your phone, you can see this pattern in action. Your tap calls <code>BluetoothAdapter.enable()</code> in the app layer. That call travels to <code>BluetoothManagerService</code> in the system server process. The manager checks permissions, then calls <code>AdapterService.enable()</code>. Inside the service, a JNI bridge triggers a native C++ function called <code>enableNative()</code>, which finally sends a command to the hardware abstraction layer. From there, it reaches the Bluetooth chip itself. Each layer knows its exact role.</p>
<p>This organization also makes debugging easier. If something goes wrong, you can tell whether it’s the Manager that didn’t send a message, the Service that failed to respond, or the native stack that stopped working. Each part logs its own activity in logcat, so you can follow the chain of events without guessing where the problem began.</p>
<p>At its core, the Manager–Service pattern is Android’s way of keeping large systems under control. It divides authority, enforces security, and lets the entire Bluetooth subsystem recover gracefully from errors. It may look complicated at first, but it is this design that makes Bluetooth remarkably resilient. Every time your phone connects to your car or your earbuds, it happens through this carefully choreographed handoff between the Manager and the Service. It’s a quiet partnership that keeps billions of connections running smoothly every single day.</p>
<h2 id="heading-the-facade-pattern-making-complexity-look-simple">The Facade Pattern: Making Complexity Look Simple</h2>
<p>If the Manager–Service pattern is about dividing responsibility, the Facade pattern is about hiding chaos behind elegance. In many ways, this is the reason most Android developers can use Bluetooth without needing to understand what happens inside the stack.</p>
<p>The Facade pattern provides a friendly public face that masks a labyrinth of underlying operations, creating an illusion of simplicity while managing a tremendous amount of behind-the-scenes work.</p>
<p>To understand this, think about the front desk of a large hotel. When you check in, you talk to one receptionist. That person gives you your key, answers questions, and takes requests. You never meet the maintenance crew fixing the air conditioning or the kitchen staff preparing food or the team handling room cleaning schedules. Yet all those systems quietly operate through that one friendly front desk.</p>
<p>That front desk is the Facade. It provides a simple interface to a complex system, ensuring guests never have to deal with the hotel’s internal machinery.</p>
<p>Android’s Bluetooth framework works in the same way. Developers interact with high-level classes such as <code>BluetoothAdapter</code>, <code>BluetoothDevice</code>, and <code>BluetoothGatt</code>. These classes are the front desks of the Bluetooth system. They provide clean, easy-to-use APIs like <code>enable()</code>, <code>getBondedDevices()</code>, and <code>connectGatt()</code>.</p>
<p>When a developer calls one of these methods, it looks straightforward. But beneath the surface, that call passes through multiple layers of services, IPC mechanisms, and native components before reaching the Bluetooth controller hardware.</p>
<p>Here is a simplified example to illustrate how this works in practice:</p>
<pre><code class="lang-java">BluetoothGatt gatt = device.connectGatt(context, <span class="hljs-keyword">false</span>, callback);
</code></pre>
<p>This single line looks simple. But in reality, it triggers an entire orchestra of operations. The call goes through the <code>BluetoothDevice</code> class, which forwards the request to <code>BluetoothGatt</code>. The <code>BluetoothGatt</code> instance then communicates with the system’s Bluetooth service through Binder IPC. That service eventually invokes native code that sets up an L2CAP channel, negotiates attributes, configures encryption, and starts the Generic Attribute Profile (GATT) procedure. None of that complexity is visible to the developer who wrote the original line.</p>
<p>This is what makes the Facade pattern so powerful. It provides abstraction without removing capability. The Android team knows that very few app developers want to worry about connection intervals, PHY configurations, or attribute protocol responses. They just want to connect to a device and get data. By exposing a Facade, Android lets developers stay productive while the internal layers handle the technical details.</p>
<p>If you look at the Android source tree, you can see this pattern clearly in how Bluetooth is organized. The classes in the <code>android.bluetooth</code> package are intentionally designed to be simple and self-contained. They never reveal how the system service works.</p>
<p>For example, <code>BluetoothAdapter</code> doesn’t know how to send HCI commands, and <code>BluetoothGatt</code> doesn’t know how to open a socket. Instead, they act as representatives, forwarding user requests to the Bluetooth Manager or the corresponding Service, which then interacts with the native stack.</p>
<p>This pattern is what makes the Bluetooth API approachable to beginners. Imagine if Android exposed every detail of the underlying protocols to developers. You would have to manually construct attribute requests, negotiate connection intervals, and handle packet fragmentation. The result would be technically accurate but completely unusable for most app developers. The Facade prevents that by serving as a translation layer between human expectations and machine complexity.</p>
<p>There is also a deeper design reason behind this approach. A Facade protects stability. Because developers only see the outermost layer, Android engineers can modify the internals without breaking existing apps. This allows the system to evolve freely, improving performance and adding new features while keeping the public API consistent.</p>
<p>The Bluetooth internals have changed countless times since the early days of Android, but <code>BluetoothAdapter.startDiscovery()</code> still works the same way it did a decade ago. That consistency is a direct benefit of the Facade pattern.</p>
<p>In a sense, the Facade pattern is about empathy. It respects the developer’s time by not forcing them to learn every Bluetooth nuance. It makes working with a complicated protocol feel human. Whether you are scanning for nearby devices, connecting to a smartwatch, or transferring data, you only need to call a few readable methods and handle a handful of callbacks. Behind those calls, a world of threads, sockets, and packet exchanges whirs silently to life, all hidden behind a calm, minimal interface.</p>
<p>So the next time you call <code>BluetoothAdapter.enable()</code> and your phone’s Bluetooth magically comes to life, remember that you are not flipping a simple switch. You are sending a message through a carefully designed Facade that talks to multiple services, native layers, and hardware interfaces. It is like pressing a single button on a spaceship console while a thousand mechanical parts start moving in perfect synchronization. You don’t see the complexity, and that is precisely the point.</p>
<h2 id="heading-the-state-machine-pattern-keeping-bluetooth-sane">The State Machine Pattern: Keeping Bluetooth Sane</h2>
<p>If you have ever debugged Bluetooth connections, you have probably experienced moments of pure confusion. One minute the device says “Connecting,” then suddenly it jumps to “Connected,” then “Disconnected,” then “Connecting” again, and before you know it, you have no idea what the current state actually is.</p>
<p>Bluetooth is, by nature, an unpredictable environment. Devices move in and out of range, radio interference causes delays, and remote devices can behave differently depending on their chipsets. To make sense of all this unpredictability, Android relies on one of the most battle-tested concepts in computer science: the <strong>State Machine</strong> pattern.</p>
<p>A state machine is like a rulebook that defines how a system behaves depending on its current situation. Instead of reacting randomly to every event, the system maintains a clear notion of “state.”</p>
<p>For Bluetooth, these states might include <em>Disconnected</em>, <em>Connecting</em>, <em>Connected</em>, or <em>Disconnecting</em>. Each state knows exactly what actions are allowed and what transitions are possible.</p>
<p>For example, you can only go from <em>Disconnected</em> to <em>Connecting</em> when a connection attempt starts, and you can only go from <em>Connecting</em> to <em>Connected</em> if the handshake succeeds. If something happens that does not make sense for the current state, the system simply ignores it. This structure prevents chaos.</p>
<p>In Android’s Bluetooth implementation, almost every major profile uses a state machine. You can find them in classes like <code>A2dpStateMachine.java</code> and <code>HeadsetStateMachine.java</code>. Each one extends a generic <code>StateMachine</code> framework that Android provides. The structure is surprisingly elegant. You define individual classes for each state, implement their behaviors, and let the system handle the transitions. Conceptually, it looks like this:</p>
<pre><code class="lang-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A2dpStateMachine</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StateMachine</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> State mDisconnected = <span class="hljs-keyword">new</span> Disconnected();
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> State mConnecting = <span class="hljs-keyword">new</span> Connecting();
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> State mConnected = <span class="hljs-keyword">new</span> Connected();

    A2dpStateMachine() {
        addState(mDisconnected);
        addState(mConnecting);
        addState(mConnected);
        setInitialState(mDisconnected);
    }
}
</code></pre>
<p>Although the code may look technical, the idea is simple. Each “State” represents a specific mode of operation, and each one defines how to react to incoming events.</p>
<p>The system starts in <em>Disconnected</em>. When a “connect” command arrives, it moves to <em>Connecting</em>. When the connection completes, it moves to <em>Connected</em>. If the user turns off Bluetooth or the remote device disappears, it transitions back to <em>Disconnected</em>. Every action follows a logical, well-defined path.</p>
<p>This pattern is what keeps Bluetooth stable despite the messy nature of wireless communication. Without it, you would constantly end up with half-open connections, dangling callbacks, and undefined behaviors. Imagine a phone that still thinks it’s connected to your headphones long after you have turned them off. The state machine eliminates that by keeping a single source of truth for connection status.</p>
<p>Beyond correctness, the state machine pattern also improves readability and maintenance. Each state is self-contained, so developers can easily locate the logic that handles a particular situation. If you need to change how Bluetooth behaves when connecting, you only modify the <em>Connecting</em> class, not the entire codebase. This modularity makes the Bluetooth stack easier to evolve as new profiles and features appear.</p>
<p>There is also a subtle psychological benefit to using state machines. When debugging, engineers can trace log messages that indicate transitions, such as “A2dpStateMachine: Transitioning from CONNECTING to CONNECTED.” These logs act like a map of the system’s thought process. Instead of guessing what happened, you can follow a clear narrative of cause and effect. That is invaluable in a system as complex as Bluetooth, where timing issues can hide bugs that are otherwise impossible to reproduce.</p>
<p>State machines also ensure graceful recovery. Suppose a connection fails halfway through. Without structured states, the system might leave resources allocated or callbacks registered. But with a state machine, the <em>Connecting</em> state knows how to clean up before returning to <em>Disconnected</em>. This reduces leaks, power drain, and inconsistent user experiences.</p>
<p>Even at higher levels of Android, you can see the influence of this pattern. For example, when you toggle Bluetooth on or off, the adapter itself transitions through a sequence of states internally: <em>Turning On</em>, <em>On</em>, <em>Turning Off</em>, <em>Off</em>. This ensures that all dependent services, such as GATT and A2DP, are brought up or down in the right order. The pattern guarantees that nothing jumps ahead or lags behind during these transitions.</p>
<p>In everyday terms, the state machine pattern is like traffic lights for Bluetooth. It prevents every component from driving through the intersection at the same time. Each action has a green, yellow, or red light depending on the current situation. This orderliness is what keeps Bluetooth from descending into radio chaos every time multiple devices try to connect or disconnect at once.</p>
<p>So, the next time your phone automatically reconnects to your headphones after a short disconnection, remember that it is not luck. It is a carefully choreographed set of state transitions keeping track of where everything stands. Behind every smooth Bluetooth experience lies a quiet but dependable state machine making sure each event happens exactly when it should and never when it shouldn’t.</p>
<h2 id="heading-the-handlerlooper-pattern-message-driven-concurrency">The Handler–Looper Pattern: Message-Driven Concurrency</h2>
<p>If Bluetooth had a personality, it would be that friend who cannot sit still. It’s constantly juggling tasks: scanning for devices, maintaining connections, handling GATT operations, streaming audio, and sending data to the controller, all at once. Underneath that hustle is one of Android’s most reliable design foundations: the <strong>Handler–Looper</strong> pattern. This pattern is what keeps Bluetooth responsive, synchronized, and stable even when a dozen things happen at the same time.</p>
<p>To understand why it exists, imagine running a busy coffee shop with only one employee who tries to handle every customer request immediately. One person takes an order, makes the drink, cleans the counter, and washes the cups all in real time. Within minutes, chaos erupts. Customers start yelling, the counter gets sticky, and no one knows who’s being served.</p>
<p>Now, imagine a more organized system: every order goes into a queue, and the barista processes them one by one. That’s essentially how the Handler–Looper system works.</p>
<p>In Android, almost everything that involves background work happens through <strong>message queues</strong>. The <strong>Looper</strong> represents a thread that waits for messages, and the <strong>Handler</strong> is the entity that posts those messages into the queue.</p>
<p>Instead of letting different threads modify shared Bluetooth state directly, which could easily lead to race conditions, Android forces all Bluetooth operations to happen on specific threads managed by loopers. Messages arrive, get handled in order, and the system never loses track of what happened first or last.</p>
<p>Inside the Bluetooth system, this pattern appears everywhere. Each service, such as <code>AdapterService</code>, <code>GattService</code>, or <code>A2dpService</code>, has its own Handler running on a dedicated thread. When a Bluetooth event occurs, like “Device Connected” or “Start Discovery,” the event is wrapped in a <code>Message</code> object and sent to the appropriate Handler. That Handler then decides what to do next. The pattern turns what could have been a tangle of multithreaded chaos into a clear, sequential pipeline.</p>
<p>Here’s a simplified example inspired by Android’s real Bluetooth code:</p>
<pre><code class="lang-java"><span class="hljs-keyword">private</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AdapterServiceHandler</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Handler</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span><span class="hljs-params">(Message msg)</span> </span>{
        <span class="hljs-keyword">switch</span> (msg.what) {
            <span class="hljs-keyword">case</span> MSG_START_DISCOVERY:
                startDiscoveryNative();
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> MSG_STOP_DISCOVERY:
                stopDiscoveryNative();
                <span class="hljs-keyword">break</span>;
        }
    }
}
</code></pre>
<p>This code might look plain, but it’s quietly doing something brilliant. Instead of running <code>startDiscoveryNative()</code> directly, the system posts a message saying, “Hey, when you get a chance, start discovery.” The Looper thread eventually picks up that message and executes it in the correct order. No two threads ever collide, and the main thread stays free to handle user interactions.</p>
<p>The beauty of this approach lies in its predictability. Bluetooth events often happen in unpredictable sequences: a connection attempt might fail while a scan is still in progress, or a new device might appear while another is being paired. Without strict message ordering, these overlaps could lead to deadlocks or inconsistent states. By channeling every operation through a single message queue, Android ensures that Bluetooth behaves deterministically, no matter how chaotic the radio environment becomes.</p>
<p>It also helps with <strong>thread safety</strong>. Instead of sprinkling locks everywhere in the code, Android simply guarantees that all critical Bluetooth work happens on the same thread. This means developers can focus on logic instead of worrying about synchronization bugs. It’s one of those design choices that looks simple but saves thousands of hours of debugging across devices and vendors.</p>
<p>There’s another hidden benefit too: <strong>graceful recovery</strong>. If something goes wrong inside a message handler, say a native call fails or a timeout occurs, the system can isolate that failure to a single message. The rest of the queue continues processing normally. This containment prevents one bad operation from crashing the entire Bluetooth stack.</p>
<p>When you watch logcat during a Bluetooth session, you can often see the Handler–Looper pattern in action. You’ll find lines like “MSG_START_DISCOVERY received” followed by “Starting discovery” and “MSG_STOP_DISCOVERY received.” Those logs are more than just printouts – they are breadcrumbs showing the system’s thought process as it moves through the queue.</p>
<p>In simpler terms, the Handler–Looper pattern is how Android Bluetooth keeps its cool. It takes a storm of asynchronous events, pairing requests, advertisements, data packets, disconnections, and lines them up in a single, calm queue. It ensures that everything happens in order, every time.</p>
<p>So, the next time your phone seamlessly switches from one Bluetooth speaker to another while still streaming music and scanning for your watch in the background, remember what’s quietly at work beneath it all. There’s a dedicated thread looping patiently, reading messages, and keeping order in a world of wireless chaos. It’s the unsung hero of concurrency, one message at a time.</p>
<h2 id="heading-the-observer-pattern-when-bluetooth-talks-back">The Observer Pattern: When Bluetooth Talks Back</h2>
<p>Bluetooth is a chatterbox. It never works alone, and is always reacting to something. A device connects, another disconnects, a new advertisement appears, a bond is created, or a characteristic changes its value. The system needs to keep dozens of components informed about these changes in real time.</p>
<p>This is where the <strong>Observer pattern</strong> comes in. This pattern is all about communication, letting different parts of the system stay updated without constantly asking what’s going on.</p>
<p>The basic idea is simple. You have one source of truth that broadcasts updates, and you have multiple listeners that care about those updates. Whenever the source changes, it notifies everyone who subscribed. It’s like a news channel that sends breaking alerts to subscribers instead of waiting for each viewer to call in and ask, “Anything new today?”</p>
<p>In Android Bluetooth, this is how almost all notifications and callbacks are delivered. When your phone connects to a Bluetooth device, the Bluetooth system service sends out an event. The app doesn’t have to keep checking the connection status every second. Instead, it simply registers a listener that reacts whenever the connection state changes. That listener could be a <code>BroadcastReceiver</code> in the app or a callback interface provided by the framework.</p>
<p>For example, when a device connects, Android sends out a broadcast intent like this:</p>
<pre><code class="lang-java">sendBroadcast(<span class="hljs-keyword">new</span> Intent(BluetoothDevice.ACTION_ACL_CONNECTED));
</code></pre>
<p>Apps that have registered for this intent receive it automatically. They can then update their user interface, show a notification, or start another operation based on the new state. The same mechanism works for disconnections, bonding events, and discovery results. It’s an elegant way of keeping apps informed without them wasting energy by constantly polling the system.</p>
<p>At the GATT level, the Observer pattern takes a slightly different form. When you connect to a Bluetooth Low Energy device and subscribe to a characteristic, you provide a callback called <code>BluetoothGattCallback</code>. This callback has methods such as <code>onConnectionStateChange()</code> and <code>onCharacteristicChanged()</code>. Whenever the device sends new data, the system automatically invokes the appropriate callback on your behalf. You don’t need to ask for updates repeatedly – you simply react when they arrive.</p>
<p>The real beauty of this pattern is how decoupled it makes the system. The Bluetooth framework can notify multiple apps and services simultaneously without knowing anything about how they use the information. It just broadcasts an event and moves on. Each listener independently decides what to do with it.</p>
<p>This design is crucial for a multitasking operating system like Android, where Bluetooth events may be relevant to different components at the same time. For example, the system settings might need to update the connection icon, the media framework might need to route audio, and an app might need to sync data — all triggered by the same connection event.</p>
<p>The Observer pattern also helps with efficiency. Because updates are sent only when something changes, there is no unnecessary processing or battery drain from constant status checks. This design allows the Bluetooth stack to stay responsive while minimizing overhead, which is especially important for mobile devices that need to preserve both power and performance.</p>
<p>In practical terms, this pattern is what makes Bluetooth feel alive. When you open your Bluetooth settings and instantly see your device name appear or disappear, that’s the result of observers doing their job. They are always listening for broadcasts and updating the interface the moment something changes. Without this mechanism, your Bluetooth menu would lag or require manual refreshing just to stay current.</p>
<p>There is also a subtle reliability benefit. Observers can join or leave at any time without breaking the system. If one app crashes or unregisters its listener, others still receive updates normally. This flexibility ensures that the Bluetooth service remains stable even if individual apps behave unpredictably.</p>
<p>So, the next time your phone pops up a notification that your earbuds have connected or your smartwatch silently syncs in the background, remember that it is not magic. It’s the Observer pattern at work: a polite messaging system that lets Bluetooth quietly talk to everyone who is listening, all without raising its voice.</p>
<h2 id="heading-the-builder-pattern-making-gatt-bearable">The Builder Pattern: Making GATT Bearable</h2>
<p>If you have ever worked with Bluetooth Low Energy, you already know that the GATT layer can be a maze. The Generic Attribute Profile, or GATT, is how devices expose data to one another. It defines services, characteristics, and descriptors that describe everything from a heart rate monitor’s readings to a light bulb’s brightness. On paper, it’s beautifully organized. In practice, setting it up manually can feel like assembling furniture without instructions, using only an Allen key and pure faith.</p>
<p>When Android engineers designed the Bluetooth GATT APIs, they realized that developers would need a way to build these services and characteristics without losing their minds. That is where the <strong>Builder pattern</strong> comes in. This pattern is all about constructing complex objects step by step, instead of trying to do everything in one chaotic go.</p>
<p>Think of it like building a sandwich. You start with a base, then add layers: bread, sauce, lettuce, tomato, cheese, and so on. You can add or skip ingredients as needed, and by the end, you have a complete meal that makes sense.</p>
<p>The Builder pattern works the same way. It lets you create a GATT service one piece at a time, adding characteristics and descriptors in a readable, modular fashion.</p>
<p>In Android, a GATT service is represented by the <code>BluetoothGattService</code> class, and each piece of data it exposes is represented by a <code>BluetoothGattCharacteristic</code>. Instead of requiring you to manually wire all of these together in one long, confusing block, Android allows you to build them step by step, like this:</p>
<pre><code class="lang-java">BluetoothGattService service = <span class="hljs-keyword">new</span> BluetoothGattService(SERVICE_UUID,
        BluetoothGattService.SERVICE_TYPE_PRIMARY);

BluetoothGattCharacteristic characteristic =
        <span class="hljs-keyword">new</span> BluetoothGattCharacteristic(CHAR_UUID,
                BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
                BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);

service.addCharacteristic(characteristic);
</code></pre>
<p>Even though this looks simple, it reflects a powerful design philosophy. Each method call adds a new layer of configuration without breaking readability. You can look at the code and instantly understand what kind of service you’re creating, what characteristics it contains, and what permissions each one has. There are no massive constructors, no messy parameter lists, and no confusion about what goes where.</p>
<p>This pattern does more than make code pretty. It also prevents errors. GATT structures are very sensitive to incorrect configurations, for example if a characteristic lacks the right permission or if a descriptor is missing. By breaking the setup into small, incremental steps, the Builder pattern helps developers validate each part as they go. It’s much easier to debug a missing characteristic when each one is clearly defined, rather than buried inside a giant, monolithic block of code.</p>
<p>The same idea applies internally within the Android Bluetooth stack. When the system builds its own GATT tables or processes client requests, it follows the same step-by-step assembly model. Each stage of the process adds more detail to the overall structure. The result is not only easier to read but also more robust in handling changes.</p>
<p>There is also a psychological benefit to this approach. Developers can focus on one small piece at a time instead of feeling overwhelmed by the entire setup. It feels like progress, and it reduces the cognitive load that often comes with working on protocols like GATT, where small mistakes can cause big headaches.</p>
<p>In a broader sense, the Builder pattern in Android Bluetooth is a lesson in humility. It acknowledges that complex systems are built incrementally, not in one heroic line of code. It invites you to slow down, define what you need clearly, and construct it carefully. Whether you are setting up a health monitor or designing a custom BLE sensor, the Builder pattern ensures that your code remains clear and maintainable as your project grows.</p>
<p>So the next time you define a Bluetooth service in your app and everything just works, take a moment to appreciate the quiet genius of the Builder pattern. It’s the reason you can build an entire wireless data model with a few readable lines instead of a spaghetti of function calls. It turns the intimidating world of GATT into something almost enjoyable, a reminder that even in low-level systems programming, design elegance still matters.</p>
<h2 id="heading-the-strategy-pattern-adapting-to-different-devices">The Strategy Pattern: Adapting to Different Devices</h2>
<p>Bluetooth, as anyone who has worked with it knows, is not one single, predictable standard in practice. It’s more like a family reunion where every cousin claims to follow the same rules but each one interprets them differently. One device might handle extended advertising perfectly, another insists on using legacy commands, and yet another behaves strangely when it comes to pairing.</p>
<p>In this unpredictable world, Android cannot rely on one fixed set of behaviors. It needs a system that can adapt depending on what kind of device or chipset it is dealing with. This is where the <strong>Strategy pattern</strong> quietly saves the day.</p>
<p>The Strategy pattern is all about flexibility. It allows a system to choose between multiple approaches at runtime depending on the situation. Instead of writing huge <code>if-else</code> blocks to handle every possible scenario, developers define a common interface that represents a behavior, and then create different implementations of that behavior. The system can then pick the right strategy dynamically.</p>
<p>Imagine you are a chef who must cook for guests with different dietary preferences. You don’t rewrite the entire recipe each time someone says they are vegan or gluten-free. Instead, you have multiple cooking strategies, one for each diet, and you simply pick the right one when the order comes in. Android does the same thing with Bluetooth.</p>
<p>Inside the Bluetooth stack, different devices and chipsets support different capabilities. Some controllers can handle multiple advertising sets, some cannot. Some prefer extended packet formats, while others only understand the older legacy commands. To manage this diversity without making the code unreadable, Android uses interchangeable strategies.</p>
<p>For example, when the system needs to start Bluetooth advertising, it doesn’t hard-code every possible hardware path. Instead, it defines an abstract interface, something like:</p>
<pre><code class="lang-java"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">AdvertisingStrategy</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">startAdvertising</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">stopAdvertising</span><span class="hljs-params">()</span></span>;
}
</code></pre>
<p>Then it provides specific implementations for each scenario, such as a <code>LegacyAdvertisingStrategy</code> and an <code>ExtendedAdvertisingStrategy</code>. Depending on the chipset capabilities, the system decides which strategy to use at runtime:</p>
<pre><code class="lang-java">AdvertisingStrategy strategy = controller.supportsExtendedAdvertising()
        ? <span class="hljs-keyword">new</span> ExtendedAdvertisingStrategy()
        : <span class="hljs-keyword">new</span> LegacyAdvertisingStrategy();
strategy.startAdvertising();
</code></pre>
<p>This design keeps the code clean and extensible. If a new Bluetooth version introduces a new advertising method, developers can simply implement another strategy class without touching the existing ones. The same approach appears in connection handling, power management, and even encryption policies.</p>
<p>The Strategy pattern also allows for graceful fallback. Suppose a modern device supports extended advertising but something goes wrong, maybe the controller firmware has a bug. Instead of crashing, the system can quietly switch back to the legacy strategy. Users never notice the change, and Bluetooth continues working.</p>
<p>Beyond hardware adaptability, this pattern also simplifies testing. Developers can easily substitute one strategy with another in unit tests to simulate different hardware configurations. It encourages modularity, which is crucial for a system that runs across hundreds of Android devices made by dozens of manufacturers.</p>
<p>You can also see the philosophical elegance in how this pattern aligns with Bluetooth itself. The Bluetooth protocol is inherently designed for negotiation. Devices exchange capabilities, choose compatible settings, and then proceed. Android’s software architecture mirrors that philosophy at the code level. By using strategies, it lets the system negotiate internally too, not between devices, but between code paths.</p>
<p>From a practical standpoint, the Strategy pattern gives Android the superpower of evolution. As new Bluetooth versions emerge with new features like LE Audio, Isochronous Channels, or Periodic Advertising, Android can keep up simply by introducing new strategy classes. There is no need to overhaul the entire system or rewrite large chunks of legacy logic.</p>
<p>So when your phone seamlessly connects to both a five-year-old Bluetooth speaker and a brand-new pair of earbuds using LE Audio, it’s not luck. It is design. Underneath the surface, Android is quietly picking the right strategy for each device, making the whole experience look effortless. It’s one of those cases where smart architecture turns what could have been a compatibility nightmare into a smooth, invisible handshake between hardware generations.</p>
<h2 id="heading-the-template-method-pattern-common-flows-custom-details">The Template Method Pattern: Common Flows, Custom Details</h2>
<p>In large systems like Android Bluetooth, not every part of the code can be entirely unique. Some operations follow the same general flow every time, but with small variations in the details. For example, connecting to a device, discovering services, or streaming audio all share similar high-level steps.</p>
<p>The pattern that allows Android to reuse these general flows while still letting each Bluetooth profile define its own personality is the <strong>Template Method</strong> pattern.</p>
<p>The essence of this pattern is simple: define the overall process once, but let subclasses decide how specific parts should behave. It’s like giving every chef in a restaurant the same recipe outline – prepare ingredients, cook, and plate – but letting each of them choose their own spices and techniques for flavor. The structure remains constant, but the details can vary.</p>
<p>Bluetooth needs this because different profiles, such as A2DP for audio or GATT for data exchange, often perform similar actions in slightly different ways. They all start connections, maintain states, and handle disconnections, but the way they handle timing, acknowledgments, or retries can differ. The Template Method pattern keeps these flows consistent while allowing room for customization.</p>
<p>Inside Android’s Bluetooth stack, you can see this pattern in how connection management is implemented. The process of connecting to a Bluetooth device typically follows the same structure: initialize the stack, attempt a connection, verify success, and then notify other components. Each profile, however, defines its own way of handling the lower-level details.</p>
<p>In conceptual form, it looks something like this:</p>
<pre><code class="lang-java"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BluetoothProfileConnection</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">connect</span><span class="hljs-params">()</span> </span>{
        prepareConnection();
        performConnection();
        finalizeConnection();
    }

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">void</span> <span class="hljs-title">prepareConnection</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">void</span> <span class="hljs-title">performConnection</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">void</span> <span class="hljs-title">finalizeConnection</span><span class="hljs-params">()</span></span>;
}
</code></pre>
<p>A class such as <code>A2dpService</code> or <code>GattService</code> would then implement the abstract methods in its own way. One might set up audio channels, while another negotiates attribute protocols. The overall template (prepare, perform, finalize) never changes. This is what keeps the Bluetooth system organized even when dozens of profiles coexist and evolve over time.</p>
<p>This pattern is particularly useful in a codebase as large as Android’s because it enforces discipline without killing flexibility. It ensures that every Bluetooth operation follows the same skeleton, which makes debugging and extending the system far easier. When an engineer wants to add a new feature or fix a connection bug, they already know where to look and which parts are shared or unique.</p>
<p>Another advantage of the Template Method pattern is that it reduces duplication. Without it, each profile might write its own version of “connect,” “disconnect,” and “reconnect,” each slightly different but doing almost the same thing. That would make the code hard to maintain and error-prone. With a template, the core logic lives in one place, and only the necessary variations appear in subclasses.</p>
<p>There is also an important design insight here: Bluetooth, like many communication protocols, is inherently procedural. You must do things in the correct order, initialize before connecting, connect before discovering, and discover before reading data. The Template Method pattern encodes this order directly into the architecture. It prevents accidental mistakes, such as skipping a required step or performing actions out of sequence.</p>
<p>From a broader perspective, this pattern teaches an important engineering lesson about balance. Too much abstraction, and systems become rigid and bureaucratic. Too little structure, and they turn into chaos. The Template Method pattern sits comfortably in the middle. It provides consistency while still leaving space for creativity and variation.</p>
<p>So the next time your phone connects to your car, switches to the right Bluetooth profile, and starts playing music without skipping a beat, you’ll know that there is a quiet choreography happening inside. Each profile follows the same dance steps – prepare, perform, and finalize – but each does it in its own rhythm. That harmony between structure and flexibility is what makes Bluetooth both powerful and adaptable.</p>
<h2 id="heading-the-service-locator-pattern-finding-the-right-profile-at-runtime">The Service Locator Pattern: Finding the Right Profile at Runtime</h2>
<p>At this point, we have seen how Android Bluetooth manages complexity through delegation, structure, and controlled flexibility. But there is still a practical question to answer: with so many Bluetooth services and profiles running in the system (like A2DP, GATT, HFP, MAP, HID, and more), how does the framework know which one to talk to at any given moment? When you stream audio, it needs A2DP. When you sync contacts, it needs PBAP. When you connect a keyboard, it needs HID. Android’s answer to this problem is the <strong>Service Locator</strong> pattern.</p>
<p>In the simplest terms, the Service Locator is a central registry that helps different parts of a system find the service or component they need without having to know where it lives. It’s like the information desk at a large airport. You don’t need to memorize the location of every gate or airline office – you just ask the information desk, and they point you to the right place.</p>
<p>Inside the Android Bluetooth system, this pattern appears everywhere, especially within the <code>AdapterService</code> and <code>BluetoothManagerService</code> classes. These services manage a variety of Bluetooth profiles, and each profile is responsible for its own behavior. Instead of hard-coding every possible profile into every part of the stack, Android maintains a registry where each service can be looked up dynamically.</p>
<p>Here is a simplified version of what this looks like conceptually:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AdapterService</span> </span>{
    <span class="hljs-keyword">private</span> Map&lt;Integer, ProfileService&gt; mProfileServices = <span class="hljs-keyword">new</span> HashMap&lt;&gt;();

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerProfile</span><span class="hljs-params">(<span class="hljs-keyword">int</span> profileId, ProfileService service)</span> </span>{
        mProfileServices.put(profileId, service);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> ProfileService <span class="hljs-title">getProfileService</span><span class="hljs-params">(<span class="hljs-keyword">int</span> profileId)</span> </span>{
        <span class="hljs-keyword">return</span> mProfileServices.get(profileId);
    }
}
</code></pre>
<p>When a Bluetooth operation occurs, such as starting audio streaming or initiating a data transfer, the system asks the AdapterService for the correct profile implementation. The Service Locator then returns the matching service instance, such as the A2DP service for audio or the GATT service for BLE data. Each profile operates independently, but the Service Locator acts as the phonebook that ties them all together.</p>
<p>This pattern solves several key problems. First, it removes the need for every part of the system to know about every other part. Without it, each class would have to keep track of dozens of others, creating a tangled web of dependencies. With a Service Locator, everything becomes more modular. Each component can register itself once and be discovered whenever needed.</p>
<p>Second, it makes the system flexible. Android devices can enable or disable certain Bluetooth profiles depending on hardware support or user configuration. For example, a smartwatch might only need GATT, while a car infotainment system needs A2DP, HFP, and MAP. The Service Locator allows Android to load only the relevant profiles at runtime instead of baking them all in permanently.</p>
<p>Third, it helps with scalability. As new Bluetooth profiles are introduced, such as LE Audio or Broadcast Audio, they can be added without rewriting existing code. The Service Locator acts as the central meeting point that stays the same even as new services join the system. It’s like a well-organized switchboard that never needs rewiring, no matter how many new phones, watches, or speakers show up.</p>
<p>From a debugging standpoint, this design also makes life easier. Developers can trace which service is currently active or verify that a profile is registered correctly simply by inspecting the registry. It provides a single source of truth that reflects the system’s state at any moment.</p>
<p>On a philosophical level, the Service Locator pattern represents Android’s pragmatic approach to complexity. Instead of trying to make every module aware of the entire Bluetooth world, it centralizes coordination in a controlled, predictable way. It acknowledges that Bluetooth is not a single, monolithic feature but an ecosystem of cooperating components that need a shared directory to find each other efficiently.</p>
<p>So when your phone automatically switches from streaming audio over A2DP to transferring a file over OBEX or syncing notifications with your smartwatch, it happens seamlessly because the system always knows exactly which profile to use. That knowledge comes from the quiet work of the Service Locator pattern, acting like a backstage coordinator ensuring that the right performer walks on stage at the right time.</p>
<h2 id="heading-the-layered-architecture-pattern-from-app-to-radio-without-losing-the-plot">The Layered Architecture Pattern: From App to Radio Without Losing the Plot</h2>
<p><img src="https://source.android.com/static/docs/core/connect/bluetooth/images/fluoride_architecture.png" alt="Bluetooth | Android Open Source Project" class="image--center mx-auto" width="636" height="434" loading="lazy"></p>
<p>If there is one pattern that truly defines Android’s Bluetooth design philosophy, it is <strong>Layered Architecture</strong>. This is the invisible backbone that keeps the entire system structured, predictable, and scalable. In a world where Bluetooth involves everything from mobile apps to kernel drivers, layering is not just a matter of organization, but one of survival.</p>
<p>At first glance, Bluetooth might seem like a single feature. You turn it on, pair a device, and it works. But in reality, it’s a long, intricate journey that starts at the app layer, where you press “Connect”, and travels all the way down to the radio hardware, which emits electromagnetic signals into the air. Between those two points lies an entire vertical stack of software layers, each playing a distinct role, each isolated from the others by well-defined interfaces.</p>
<p>Think of it as a city with multiple levels. The top layer is where people live and work: that’s your app. Below that are roads and traffic systems, which are your Android framework services. Beneath that, you have subways and utilities, the native daemons written in C and C++ that handle protocol specifics. At the very bottom is the foundation, the hardware abstraction layer and the Bluetooth controller chip itself. Every level has a clear boundary. You can remodel one floor without collapsing the whole building.</p>
<p>Here is how those layers roughly line up in Android’s Bluetooth stack.</p>
<p>At the <strong>top layer</strong>, app developers interact with classes such as <code>BluetoothAdapter</code>, <code>BluetoothDevice</code>, and <code>BluetoothGatt</code>. These are part of the Android framework, written in Java or Kotlin, and serve as the public interface. They provide clean, stable methods like <code>startDiscovery()</code> and <code>connectGatt()</code>, hiding the technical chaos below.</p>
<p>The <strong>next layer down</strong> is the system service layer. This includes classes such as <code>BluetoothManagerService</code> and <code>AdapterService</code>. These are responsible for managing Bluetooth as a system feature, enforcing permissions, and coordinating multiple profiles. They act as the brain of the operation, processing commands, routing messages, and maintaining global state.</p>
<p>Below that is the <strong>JNI and native layer</strong>, written primarily in C and C++. This is where the logic gets closer to the metal. JNI (Java Native Interface) acts as a translator between the Java world and the native code. When a Java method like <code>enable()</code> is called, JNI forwards it to the native daemon that actually speaks Bluetooth protocol commands. This bridge keeps performance high while maintaining safety through strict boundaries.</p>
<p>Finally, we reach the <strong>hardware abstraction layer (HAL)</strong> and the <strong>Bluetooth controller</strong>. The HAL defines how the operating system interacts with the underlying hardware. It sends and receives HCI (Host Controller Interface) packets, the low-level binary messages that control the Bluetooth chip. From there, the controller takes over, turning digital instructions into radio signals that travel invisibly through the air to another device.</p>
<p>The brilliance of this design is in how each layer only needs to know about the one directly below it. The app layer never worries about the hardware, and the hardware never needs to know about the app. This clear separation makes it possible for Android to run across thousands of devices built by different manufacturers using different chipsets. It is a pattern that enforces order through boundaries.</p>
<p>There are practical benefits, too. The layered architecture makes the system modular. For instance, when new Bluetooth features arrive, like LE Audio or Bluetooth 5.4, Android engineers can modify only the relevant layers. The app APIs at the top can remain stable while the lower layers evolve to support the new specifications. This is how Android manages to maintain backward compatibility while still introducing new capabilities with every release.</p>
<p>The layering also helps with debugging and reliability. When something breaks, engineers can trace the issue by moving down through the layers like a detective. If an app crashes, the problem is likely near the top. If packets are missing, the issue may be in the native layer or HAL. Each layer leaves its own signature in the logs, helping developers pinpoint where things went wrong.</p>
<p>This pattern also teaches a timeless software design lesson: complexity becomes manageable only when divided. The layered architecture prevents the Bluetooth stack from turning into a tangled mess of cross-dependencies. It lets Android evolve gracefully rather than collapse under the weight of its own history.</p>
<p>So when you tap “Pair new device” on your phone and watch your earbuds connect, remember that your request travels down a carefully organized highway of software, from the app you see, through the framework, into native code, across the hardware abstraction, and finally out into the air as a radio signal. Every piece knows its role, every layer does its part, and together they make Bluetooth feel effortless. The magic of wireless connection is not just in the radio waves, but in the architecture that makes those waves behave.</p>
<h2 id="heading-putting-it-all-together-designing-bluetooth-style-systems">Putting It All Together: Designing Bluetooth-Style Systems</h2>
<p>By now, it’s easy to see that Android’s Bluetooth stack is not just a pile of random services and classes. It’s a carefully choreographed system built on timeless design principles that keep it reliable, flexible, and surprisingly elegant despite its complexity.</p>
<p>Each pattern – the Manager–Service split, the Facade, the State Machine, the Handler–Looper, the Observer, the Builder, the Strategy, the Template Method, the Service Locator, and the Layered Architecture – exists for a reason. Together, they form the invisible scaffolding that allows Bluetooth to connect billions of devices every day without falling apart.</p>
<p>The magic of these patterns is not that they make Bluetooth simple. Bluetooth will never be simple, as it’s an enormous specification with quirks, edge cases, and competing priorities. What these patterns do instead is make the system <strong>manageable</strong>. They turn unpredictability into structure, they replace chaos with order, and they make it possible for teams of engineers around the world to work on the same stack without tripping over each other.</p>
<p>If you step back, you’ll notice that every pattern in the Bluetooth system reflects a deeper philosophy:</p>
<ul>
<li><p>The Manager–Service pattern teaches the value of separation.</p>
</li>
<li><p>The Facade reminds us that good design hides unnecessary complexity.</p>
</li>
<li><p>The State Machine shows the power of predictability.</p>
</li>
<li><p>The Handler–Looper demonstrates the beauty of serialized concurrency.</p>
</li>
<li><p>The Observer proves that communication doesn’t require coupling.</p>
</li>
<li><p>The Builder celebrates incremental construction.</p>
</li>
<li><p>The Strategy encourages adaptability.</p>
</li>
<li><p>The Template Method enforces discipline without rigidity.</p>
</li>
<li><p>The Service Locator maintains organization in a crowded ecosystem.</p>
</li>
<li><p>And the Layered Architecture ties it all together, ensuring that every piece fits logically into the whole.</p>
</li>
</ul>
<p>These same ideas extend far beyond Bluetooth. You can apply them to almost any software system, a web service, a game engine, or even a simple mobile app. The principles remain the same: divide responsibilities, enforce clear boundaries, keep your interfaces stable, and design for change rather than permanence.</p>
<p>Systems that last are not the ones that are perfect on day one. They are the ones that can grow without collapsing under their own weight.</p>
<p>Android Bluetooth has been evolving for more than a decade. It has absorbed new technologies like LE Audio, Fast Pair, and broadcast audio. It has adapted to new hardware, new chipsets, and new use cases. Yet, at its core, the same patterns continue to guide it. That consistency is the reason Bluetooth on Android, despite its quirks, works as well as it does. It’s not just a story of wireless communication, it’s a story of good architecture.</p>
<p>So the next time you tap “Connect” on your phone and your earbuds instantly respond, pause for a moment. Beneath that single tap lies an orchestra of design patterns working in perfect harmony: managers delegating to services, handlers processing messages, observers reacting to broadcasts, and strategies choosing the right behavior for your hardware. It’s a quiet miracle of software design, a reminder that even the most invisible features on your device are built with care, patience, and an eye for long-term evolution.</p>
<p>And if you ever find yourself building a complex system that seems impossible to manage, take a cue from Android Bluetooth. Start small, define your layers, choose the right patterns, and let structure do the heavy lifting. The real magic in engineering isn’t in writing clever code. It’s in designing systems that stay calm, even when the world around them isn’t.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the Compound Components Pattern in React: Prop Soup to Flexible UIs ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever opened React project source code and wondered why things are so messy? Have you ever tried adding a feature to a React component created by someone else and felt that you needed to rewrite it? Have you felt nightmarish in tackling state... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/compound-components-pattern-in-react/</link>
                <guid isPermaLink="false">68e70e0ecfc3d2834515166c</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tapas Adhikary ]]>
                </dc:creator>
                <pubDate>Thu, 09 Oct 2025 01:21:18 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759972853846/49e605c8-be15-44a4-9fc6-283be0cc0e4c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever opened React project source code and wondered why things are so messy? Have you ever tried adding a feature to a React component created by someone else and felt that you needed to rewrite it? Have you felt nightmarish in tackling state and props for a component and its children?</p>
<p>If you happen to shout out “Yes!” to the above, you’re not alone. It’s a common feeling among many React developers across the globe. But React itself is not responsible for any of these issues. These situations arise because of code smells like:</p>
<ul>
<li><p>Props drilled six levels down.</p>
</li>
<li><p>A single bloated component doing everything.</p>
</li>
<li><p>Logic that’s duplicated across different components.</p>
</li>
<li><p>Careless rendering (and re-rendering) causing performance issues.</p>
</li>
</ul>
<p>A <code>Code Smell</code> doesn’t mean broken code. Rather, it’s an indication that the code may work now, but is difficult to maintain, reuse, scale, and much harder to debug.</p>
<p>And that’s exactly where we need to use <code>Design Patterns</code>. They’re well-tested solutions to the various code smell problems that developers have been encountering for decades. When you know how to use them well, you achieve a clean, maintainable codebase that is easy to enhance, debug, and scale.</p>
<p>Today, we will take a deep dive into one of the most prominent design patterns in React called the <code>Compound Components Pattern</code>. This pattern saves React developers from passing a long list of props and helps build composable user interface components.</p>
<p>This is going to be a complete hands-on tutorial. So get your favourite code editor ready, and let’s get started. This article is also available as a video tutorial as part of the <a target="_blank" href="https://www.youtube.com/playlist?list=PLIJrr73KDmRyQVT__uFZvaVfWPdfyMFHC">15 Days of React Design Patterns</a> initiative. Please check it out.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/LglWulOqh6k" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-react-19-code-set-up">React 19 Code Set Up</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-messy-modal-component">A Messy Modal Component</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-problems-with-this-messy-modal-component">The Problems with this messy Modal Component</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-compound-components-pattern">The Compound Components Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-a-modal-component-using-the-compound-components-pattern">How to Build a Modal Component using the Compound Components Pattern</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-didnt-we-create-separate-files-for-the-subcomponents">Why Didn’t We Create Separate Files for the SubComponents?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-the-modal-component">How to Use the Modal Component</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-an-accordion-component-using-the-compound-components-pattern">How to build an Accordion Component using the Compound Components Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-add-the-accordion-to-the-modal">Add the Accordion to the Modal</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-the-use-cases">The Use Cases</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-pitfalls-and-anti-patterns">The Pitfalls and Anti-Patterns</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-15-days-of-react-patterns">15 Days of React Design Patterns</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-before-we-end">Before We End...</a></p>
</li>
</ol>
<h2 id="heading-react-19-code-set-up">React 19 Code Set Up</h2>
<p>The best way to understand how to apply a design pattern is by refactoring messy code with code smells to improve it to cleaner code. So let’s set up a coding ground so that we can start putting in our messy code first, and then go about applying the design pattern to it.</p>
<p>Note: you can find all the source code used in this tutorial on the <a target="_blank" href="https://github.com/tapascript/15-days-of-react-design-patterns/tree/main/day-03/compound-components-patterns">tapaScript GitHub</a>. Feel free to follow along with it side by side.</p>
<p>Also, make sure you have Node.js installed (preferably v18+). You can check it out by typing this command on your terminal/command prompt:</p>
<pre><code class="lang-bash">node -v
</code></pre>
<p>If you get an output with the installed Node.js version, you are all set. Otherwise, just download and install Node.js from <a target="_blank" href="https://nodejs.org/en/download">here</a>.</p>
<p>Now, run this command in your terminal to create a React 19 project scaffolding:</p>
<pre><code class="lang-bash">npx degit atapas/code-in-react-19<span class="hljs-comment">#main compound-components-pattern</span>
</code></pre>
<p>It will create a folder called <code>compound-components-pattern</code> with the Vite-based React project files under it. Now, change the directory using this command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> compound-components-pattern
</code></pre>
<p>Then install the dependencies using this command:</p>
<pre><code class="lang-bash">npm install <span class="hljs-comment">## Or, yarn install, or pnpm install, etc,</span>
</code></pre>
<p>Now, you can import the project folder into your favourite code editor (I use VS Code).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759478983493/692ac0f9-4780-4d60-bc72-55f27b9a6074.png" alt="Code Scaffolding" class="image--center mx-auto" width="481" height="552" loading="lazy"></p>
<p>Finally, to start the project locally, use the following command:</p>
<pre><code class="lang-bash">npm run dev <span class="hljs-comment">## Or, yarn dev, or pnpm dev</span>
</code></pre>
<p>Now the project should be running locally and should be accessible on the default URL, <a target="_blank" href="http://localhost:5173"><code>http://localhost:5173</code></a>. You can access the URL in your browser. Now we’re all set to start coding.</p>
<h2 id="heading-a-messy-modal-component">A Messy Modal Component</h2>
<p>Let’s get started by creating a Modal component. Start by creating a directory called <code>messy</code> under the <code>src/</code> directory. Now, create a file called <code>Modal.jsx</code> under <code>src/messy/</code> with the following code snippet:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Modal</span>(<span class="hljs-params">{ title, body, primaryAction, secondaryAction }</span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-backdrop"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-container"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-header"</span>&gt;</span>{title}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-body"</span>&gt;</span>{body}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-footer"</span>&gt;</span>
                    {secondaryAction}
                    {primaryAction}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Modal;
</code></pre>
<p>This is a simple React implementation of a modal component that accepts a title, body, and a couple of actions as props to render them as a modal.</p>
<ul>
<li><p>The <code>title</code>: The header title of the modal.</p>
</li>
<li><p>The <code>body</code>: The modal content.</p>
</li>
<li><p>The <code>primaryAction</code>: An action button like delete, create, save, and so on to place in the footer section of the modal.</p>
</li>
<li><p>The <code>secondaryAction</code>: An action button like cancel, close, and so on to place in the footer section of the modal.</p>
</li>
</ul>
<p>Next, open the <code>App.jsx</code> file and replace the existing code with the following code snippet:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Modal <span class="hljs-keyword">from</span> <span class="hljs-string">"./messy/Modal"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Modal</span>
                <span class="hljs-attr">title</span>=<span class="hljs-string">"Delete Account"</span>
                <span class="hljs-attr">body</span>=<span class="hljs-string">"Are you sure you want to delete your account?"</span>
                <span class="hljs-attr">primaryAction</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">button</span>&gt;</span>Delete<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>}
                secondaryAction={<span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Cancel<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Here, we have imported the <code>Modal</code> component and used it by passing its props values. Go to the browser tab and access the app’s URL. You should see the modal appearing like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759409687600/825901e9-5b38-49a7-8d7d-74a0867f6a01.png" alt="messy modal without style" class="image--center mx-auto" width="2172" height="1312" loading="lazy"></p>
<p>Well, as it doesn’t look like a traditional modal with a backdrop and all, so let’s fix that using CSS. Open the <code>App.css</code> and paste the following CSS styles into it and save it:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.modal-backdrop</span> {
    <span class="hljs-attribute">position</span>: fixed;
    <span class="hljs-attribute">inset</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.5</span>);
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
}
<span class="hljs-selector-class">.modal-container</span> {
    <span class="hljs-attribute">background</span>: white;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">400px</span>;
    <span class="hljs-attribute">position</span>: relative;
}
<span class="hljs-selector-class">.modal-header</span> {
    <span class="hljs-attribute">font-weight</span>: bold;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
}
<span class="hljs-selector-class">.modal-footer</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: flex-end;
    <span class="hljs-attribute">gap</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">1rem</span>;
}
<span class="hljs-selector-class">.modal-close</span> {
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">top</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">right</span>: <span class="hljs-number">8px</span>;
    <span class="hljs-attribute">background</span>: none;
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>;
}
</code></pre>
<p>Great! Now you have a cool-looking modal dialog asking for your confirmation to delete your account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759409736382/ceaaa23e-26d3-4d7d-93ed-ae83aa69b421.png" alt="Messy Modal with style" class="image--center mx-auto" width="2170" height="1314" loading="lazy"></p>
<h2 id="heading-the-problems-with-this-messy-modal-component">The Problems with This Messy Modal Component</h2>
<p>Question for you: What problems do you think this modal implementation might have?</p>
<p>Let’s find the answers:</p>
<ol>
<li><p><code>Lack of Flexibility</code>: The modal has a rigid structure that dictates exactly what it renders. What if you want a modal without a title? Or a modal with a custom layout? Or more than two action buttons? You need to write additional logic and pass additional props every time you think of enhancing the modal for another use case. These changes in the component will bring maintainability issues and increase code smell.</p>
</li>
<li><p><code>Mixed Responsibilities</code>: The modal tries to do multiple things. It handles both layout and content. This violates the separation of concerns principle that we learn from other design patterns, such as the <a target="_blank" href="https://www.youtube.com/watch?v=1UHbhikwg-s">Container-Presenter Pattern</a>.</p>
</li>
<li><p><code>Hard Reusability</code>: The modal lacks reusability due to its rigidness. Right now, if you want a modal with this:</p>
<pre><code class="lang-javascript"> &lt;h2&gt;Something Wrong!&lt;/h2&gt;
 <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"warning.png"</span> /&gt;</span></span>
 <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Something went wrong. please see the logs for more details.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
</code></pre>
<p> You can not reuse this component, and you will end up creating a new one.</p>
</li>
<li><p><code>Poor Scalability</code>: The modal component is not scalable. Think, for example, if you’re creating a component library and you end up creating multiple modal instances like ConfirmationModal, InfoModal, FormModal, ImageModal, and so on. It would be a huge ding on the scalability of that component library to create and maintain every new version of the modal.</p>
</li>
<li><p><code>Hard to Test</code>: This modal implementation is hard to test due to its tight coupling with props.</p>
</li>
</ol>
<p>With these issues in mind, let’s welcome the compound components pattern and see how it can help us solve them.</p>
<h2 id="heading-the-compound-components-pattern">The Compound Components Pattern</h2>
<p><code>Compound Components Pattern</code> in React is a design pattern where a parent component works together with its child components to share an implicit state and behaviour. Instead of passing a long list of props, the parent manages the state and exposes flexible child components (&lt;Modal.Header&gt;, &lt;Modal.Body&gt;, &lt;Modal.Footer&gt;, and so on) so that consumers can compose the UI naturally, just like using native HTML elements.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759409949714/8144535d-c0f3-4ae7-bd8d-ab93fe7bf6c2.png" alt="Compound Components Pattern Diagram" class="image--center mx-auto" width="2572" height="1238" loading="lazy"></p>
<p>Think of Compound Composition Pattern like LEGO blocks.</p>
<ul>
<li><p>The parent component is like the LEGO base plate.</p>
</li>
<li><p>The child components are the LEGO blocks (door, window, roof, and so on).</p>
</li>
<li><p>You don’t pass any props to the base plate, saying, <em>“add a door here, add a window there.”</em> Instead, you simply place the pieces where you want them.</p>
</li>
<li><p>The base plate (parent) still provides the rules and structure (studs, alignment, stability), but you get the flexibility to assemble your model however you like.</p>
</li>
</ul>
<p>Got it? That’s the power of compound components. It’s a flexible composition with a shared state/behaviour underneath.</p>
<p>Let’s now refactor our messy (and smelly) modal component by applying the compound components pattern.</p>
<h2 id="heading-how-to-build-a-modal-component-using-the-compound-components-pattern">How to Build a Modal Component Using the Compound Components Pattern</h2>
<p>Create a folder called <code>with-pattern</code> under the <code>src/</code> folder. We will arrange and maintain the modal component, and in the future, an accordion component under this new folder.</p>
<p>Next, create a folder called <code>modal</code> under the <code>src/with-pattern</code>. Finally, create a file called <code>Modal.jsx</code> with the following code snippet:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// File Location: src/with-pattern/modal/Modal.jsx</span>

<span class="hljs-keyword">const</span> Modal = <span class="hljs-function">(<span class="hljs-params">{ children, isOpen, onClose }</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span>(!isOpen) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-backdrop"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-container"</span>&gt;</span>
                {children}
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-close"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClose}</span>&gt;</span>
                    ✖
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ModalHeader</span>(<span class="hljs-params">{ children }</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-header"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ModalBody</span>(<span class="hljs-params">{ children }</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-body"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ModalFooter</span>(<span class="hljs-params">{ children }</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"modal-footer"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

Modal.Header = ModalHeader;
Modal.Body = ModalBody;
Modal.Footer = ModalFooter;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Modal;
</code></pre>
<p>Let me break it down for you:</p>
<ul>
<li><p>First, focus on the Modal component above. It doesn’t take title, body, and so on as props anymore. Rather, it accepts <code>children</code>, a special prop in React to pass any HTML elements, a group of HTMLs, JSX, or even a React component. It brings flexibility that we are no longer fixed to any particular structure to pass to the Modal component.</p>
</li>
<li><p>The JSX of the Modal component just renders the <code>children</code> prop as is, giving the entire power to the consumer of the Modal component to pass any structure it’s willing to. The Modal component uses the backdrop and container style to dictate the basic look and feel of a modal.</p>
</li>
<li><p>The Modal’s JSX also has a button to close the modal by clicking on an x. To open and close the modal, we have passed two additional props, <code>isOpen</code> and <code>onClose</code>. You can imagine <code>isOpen</code> is a state value that the consumer of this modal uses to open the modal, and the <code>onClose</code> is a function that sets the value of the <code>isOpen</code> to false to close the modal.</p>
</li>
<li><p>Then, we have defined three more components, <code>ModalHeader</code>, <code>ModalBody</code>, and <code>ModalFooter</code> which are equally flexible to accept any legit HTML structure or React component through the <code>children</code> prop. Now you can pass anything to render to the modal header. The same goes for the body and footer as well.</p>
</li>
<li><p>Next, we add the header, body, and footer as the subcomponents to the <code>Modal</code> component.</p>
<pre><code class="lang-javascript">  Modal.Header = ModalHeader;
  Modal.Body = ModalBody;
  Modal.Footer = ModalFooter;
</code></pre>
</li>
<li><p>Finally, we exported the <code>Modal</code> component.</p>
</li>
</ul>
<h3 id="heading-why-didnt-we-create-separate-files-for-the-subcomponents">Why Didn’t We Create Separate Files for the SubComponents?</h3>
<p>This question is quite natural. In general, we follow the standard practice of one component in one source file(.jsx/.tsx). Here, we seem to be breaking that rule…so are we? Actually not.</p>
<p>The golden rules are:</p>
<ul>
<li><p>The subcomponents (ModalHeader, ModalBody, and ModalFooter) are only meaningful in the context of Modal. They don’t have (or need) any existence beyond the modal.</p>
</li>
<li><p>They are small helper components that you don’t expect to reuse anywhere else.</p>
</li>
<li><p>Keeping them together is good for discoverability and is safe from potential misuse that we’ll discuss in the pitfalls section later.</p>
</li>
</ul>
<h3 id="heading-how-to-use-the-modal-component">How to Use the Modal Component</h3>
<p>So we’re sorted. Let’s now learn how to use this Modal component and see how it can bring flexibility, reusability, scalability, and testability.</p>
<p>Open the <code>App.jsx</code> file and replace the content of it with the following code snippet:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-comment">// import Modal from "./messy/Modal";</span>
<span class="hljs-keyword">import</span> Modal <span class="hljs-keyword">from</span> <span class="hljs-string">"./with-pattern/modal/Modal"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [isOpen, setIsOpen] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsOpen(true)}&gt;Open Modal<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">Modal</span> <span class="hljs-attr">isOpen</span>=<span class="hljs-string">{isOpen}</span> <span class="hljs-attr">onClose</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsOpen(false)}&gt;

        <span class="hljs-tag">&lt;<span class="hljs-name">Modal.Header</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome!<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Modal.Header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">Modal.Body</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
              This is a modal built with the Compound Component
              pattern.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Modal.Body</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">Modal.Footer</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Help!<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsOpen(false)}&gt;Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> alert("Action Performed!")}&gt;Do Action<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Modal.Footer</span>&gt;</span>

      <span class="hljs-tag">&lt;/<span class="hljs-name">Modal</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Check out how we have passed a bunch of JSX inside &lt;Modal&gt;…&lt;/Modal&gt; as the children. It’s so powerful. We’re passing the subcomponents header, body, and footer in the sequence we want them to appear in the modal.</p>
<p>Next, if we look into the &lt;ModalHeader&gt;, &lt;ModalBody&gt;, or &lt;ModalFooter&gt; components, we can again pass anything as children to them. For example, the &lt;ModalFooter /&gt; can now take three buttons (in fact, anything else) based on the needs.</p>
<p>We can compose the components like Lego blocks to build the kind of Modal that we wish to. You don't need to have different components to represent different kinds of modals now. This single component can cater to all your modal needs without introducing any props soup drama.</p>
<p>We have a button to open the modal, and the App.jsx component manages a state called <code>isOpen</code> to tackle the opening and closing of the modal.</p>
<p>You should be able to see these changes now in the browser. Click on the open modal button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759410185904/b7adbab0-9d24-4245-92ad-84766817af12.png" alt="Open Modal Button" class="image--center mx-auto" width="1528" height="964" loading="lazy"></p>
<p>The modal dialog opens up with all the content we have passed to it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759410275413/9bff6791-3cf8-45eb-aea8-a85e9a95777d.png" alt="Modal With Pattern" class="image--center mx-auto" width="1892" height="1280" loading="lazy"></p>
<p>It’s a big leap towards achieving clean code to use the compound components design pattern. Now that you’re familiar with the basics, let’s quickly do another classic implementation of this pattern by building an Accordion component.</p>
<h2 id="heading-how-to-build-an-accordion-component-using-the-compound-components-pattern">How to Build an Accordion Component Using the Compound Components Pattern</h2>
<p>An accordion component is an array of Accordion Items. It’s a combination of a header and body that shows and hides the content when users click on the header.</p>
<p>Create a folder called <code>accordion</code> under <code>src/with-pattern</code> folder. Now, create a file called <code>Accordion.jsx</code> inside the <code>src/with-pattern/accordion</code> with the following code snippet:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Accordion</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"accordion"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AccordionItem</span>(<span class="hljs-params">{ title, children }</span>) </span>{
  <span class="hljs-keyword">const</span> [isOpen, setIsOpen] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"accordion-item"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"accordion-title"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsOpen(!isOpen)}&gt;
        {title}
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      {isOpen &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"accordion-content"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Attach subcomponents</span>
Accordion.Item = AccordionItem;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Accordion;
</code></pre>
<p>Here,</p>
<ul>
<li><p>We have followed the same pattern as we did for the modal previously. We have an <code>Accordion</code> component that takes a special prop called <code>children</code>, enabling the Accordion to accept any HTML/JSX/React component and render it.</p>
</li>
<li><p>Then we defined the <code>AccordionItem</code>. It takes two props: the title to create the header, and the special prop called children to form the accordion content flexibly.</p>
</li>
<li><p>The header is formed using the button that is driven by a state called <code>isOpen</code> to show/hide the content area.</p>
</li>
<li><p>The content area of an <code>AccordionItem</code> could be anything: a paragraph, a table, an image, or even a JSX combining them.</p>
</li>
<li><p>Finally, we have added the AccordionItem as the subcomponent to the Accordion component.</p>
</li>
</ul>
<p>To make the accordion look better, let’s add a few styles. Open the <code>App.css</code> file and add these styles at the end of the file:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.accordion-item</span> {
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ddd</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
}
<span class="hljs-selector-class">.accordion-title</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">text-align</span>: left;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">font-weight</span>: bold;
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#f9f9f9</span>;
    <span class="hljs-attribute">border</span>: none;
}
<span class="hljs-selector-class">.accordion-content</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#fff</span>;
}
</code></pre>
<p>Great, let’s now use the Accordion component. Create a new file called <code>AccordionDemo.jsx</code> under the folder <code>src/with-pattern/accordion</code> with the following code snippet:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Accordion <span class="hljs-keyword">from</span> <span class="hljs-string">"./Accordion"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AccordionDemo</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Accordion</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Accordion.Item</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"What is Compound Component Pattern?"</span>&gt;</span>
        It’s a React pattern that allows parent and child components to work
        together seamlessly while giving developers flexible composition.
      <span class="hljs-tag">&lt;/<span class="hljs-name">Accordion.Item</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">Accordion.Item</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Why use it?"</span>&gt;</span>
        It makes UI libraries like modals, tabs, accordions, menus, etc. easier
        to build and use.
      <span class="hljs-tag">&lt;/<span class="hljs-name">Accordion.Item</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">Accordion.Item</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Pitfalls?"</span>&gt;</span>
        Overusing it can lead to deeply nested structures or make things harder
        to debug if not documented well.
      <span class="hljs-tag">&lt;/<span class="hljs-name">Accordion.Item</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Accordion</span>&gt;</span></span>
  );
}
</code></pre>
<p>Check out how the <code>Accordion</code> component can accept a bunch of AccordionItem components. You can also create an array of <code>AccordionItem</code> components and pass them dynamically to the Accordion component.</p>
<p>Each of the AccordionItem components accepts the title prop value, and we passed the text as the children. If needed, you can pass any other valid JSX as a child. That’s amazing!</p>
<h3 id="heading-add-the-accordion-to-the-modal">Add the Accordion to the Modal</h3>
<p>Now, let’s take the usage of this pattern to the next level. How about using the <code>AccordionDemo</code> inside the <code>Modal</code> component? Can we do it without changing the Modal component?</p>
<p>Oh yes! Remember, the Modal component accepts any JSX as children, and so does the ModalBody component. So we can just import the AccordionDemo component into the App.jsx file and use it inside the &lt;Modal.Body&gt;…&lt;/Modal.Body&gt; as shown below, right?</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-comment">// import Modal from "./messy/Modal";</span>
<span class="hljs-keyword">import</span> Modal <span class="hljs-keyword">from</span> <span class="hljs-string">"./with-pattern/modal/Modal"</span>;

<span class="hljs-keyword">import</span> AccordionDemo <span class="hljs-keyword">from</span> <span class="hljs-string">"./with-pattern/accordion/AccordionDemo"</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [isOpen, setIsOpen] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsOpen(true)}&gt;Open Modal<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">Modal</span> <span class="hljs-attr">isOpen</span>=<span class="hljs-string">{isOpen}</span> <span class="hljs-attr">onClose</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsOpen(false)}&gt;

        <span class="hljs-tag">&lt;<span class="hljs-name">Modal.Header</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome!<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Modal.Header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">Modal.Body</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
              This is a modal built with the Compound Component
              pattern.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">AccordionDemo</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Modal.Body</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">Modal.Footer</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Help!<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsOpen(false)}&gt;Close<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> alert("Action Performed!")}&gt;Do Action<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Modal.Footer</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Modal</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Now, if you run the app with these code changes, you should see the accordion appearing inside the modal. You will also be able to show/hide the accordion content and open/close the modal. This means their individual states are intact as expected.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759410454778/69fdcb6a-77a0-42e1-b060-fa2666543c94.png" alt="Accordion" class="image--center mx-auto" width="1902" height="1280" loading="lazy"></p>
<h2 id="heading-the-use-cases">The Use Cases</h2>
<p>So far, we have seen a couple of important usages of the Compound Components pattern with modal and accordion. Similarly, you can use this pattern to build reusable components like:</p>
<ul>
<li><p>Tables (Table.Head, Table.Body, Table.Row).</p>
</li>
<li><p>Any component where layout and nesting matter.</p>
</li>
</ul>
<p>Also, if you’re ever writing your own component library or design system, this pattern is a must. If you need some inspiration, look at ShadCN, Material UI, or Radix UI. They all do this.</p>
<h2 id="heading-the-pitfalls-and-anti-patterns">The Pitfalls and Anti-Patterns</h2>
<p>As you know, with great power comes great responsibility. And with patterns comes the pitfalls and anti-patterns you’ll need to be aware of. When you’re using the compound components pattern, just make sure that you:</p>
<ul>
<li><p>Don’t attach subcomponents randomly. They should belong to the parent semantically.</p>
</li>
<li><p>Avoid re-exporting subcomponents separately. It will be a disaster if someone uses the ModalFooter without a Modal. What if the ModalFooter changes tomorrow in the context of the Modal, and the other consumers are not in need/aware of that change?</p>
</li>
<li><p>Don’t attempt to make everything in the compound components pattern. The rule of thumb is, only use it when the children's structure matters, and you want to keep it flexible.</p>
</li>
</ul>
<h2 id="heading-15-days-of-react-design-patterns">15 Days of React Design Patterns</h2>
<p>I have some great news for you! After the <em>40 days of the JavaScript</em> initiative, I have now started a brand new initiative called <a target="_blank" href="https://www.youtube.com/playlist?list=PLIJrr73KDmRyQVT__uFZvaVfWPdfyMFHC">15 Days of React Design Patterns</a>.</p>
<p>If you enjoyed learning from this article, I am sure you will love this series, featuring the 15 most important React design patterns. Check it out and join.</p>
<p><a target="_blank" href="https://www.youtube.com/playlist?list=PLIJrr73KDmRyQVT__uFZvaVfWPdfyMFHC"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759482303884/694491a4-2fd9-4515-b595-eafc925d2a18.png" alt="15 Days of React Design Patterns" class="image--center mx-auto" width="1557" height="820" loading="lazy"></a></p>
<h2 id="heading-before-we-end"><strong>Before We End...</strong></h2>
<p>That’s all! I hope you found this article insightful.</p>
<p>Let’s connect:</p>
<ul>
<li><p>Subscribe to my <a target="_blank" href="https://www.youtube.com/tapasadhikary?sub_confirmation=1">YouTube Channel</a>.</p>
</li>
<li><p>Subscribe to my fortnightly newsletter, <a target="_blank" href="https://tapascript.substack.com/subscribe?utm_medium=fcc">The Commit Log</a>.</p>
</li>
<li><p>Follow on <a target="_blank" href="https://www.linkedin.com/in/tapasadhikary/">LinkedIn</a> if you don't want to miss the daily dose of up-skilling tips.</p>
</li>
<li><p>Join my <a target="_blank" href="https://discord.gg/zHHXx4vc2H">Discord Server</a>, and let’s learn together.</p>
</li>
</ul>
<p>See you soon with my next article. Until then, please take care of yourself and keep learning.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Object-Oriented Design Patterns with Java ]]>
                </title>
                <description>
                    <![CDATA[ In this article I will introduce some of the most useful object-oriented design patterns. Design patterns are solutions to common problems that show up over and over again. These problems will show up in many different contexts but always have the sa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/object-oriented-design-patterns-with-java/</link>
                <guid isPermaLink="false">6887df408810970f0e04fae6</guid>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mark Mahoney ]]>
                </dc:creator>
                <pubDate>Mon, 28 Jul 2025 20:36:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753734965769/4d53f28e-7d85-4571-831f-1760490e06dc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article I will introduce some of the most useful object-oriented <a target="_blank" href="https://www.freecodecamp.org/news/javascript-design-patterns-explained/">design patterns</a>. Design patterns are solutions to common problems that show up over and over again. These problems will show up in many different contexts but always have the same problem at the root.</p>
<p>A design pattern attempts to describe an effective solution to the problem in a generic way so that it can be applied to a specific set of circumstances.</p>
<p>I will use Java to build an example of each pattern. I’m assuming that you have some programming experience in Java. In particular, you should be (at least somewhat) familiar with the concepts of inheritance and polymorphism. These design patterns really show the power of inheritance and polymorphism, so if you are just learning about these topics this is a great opportunity to dig deeper.</p>
<p>What if you not a Java programmer? If you are familiar with any Object-Oriented language you will probably still get a lot out of the examples. Give it a shot!</p>
<h2 id="heading-code-playbacks"><strong>Code Playbacks</strong></h2>
<p>To make design patterns more approachable, I developed an interactive tutorial that uses annotated <a target="_blank" href="https://markm208.github.io/"><strong>code playbacks</strong></a> to walk through key design pattern features step-by-step.</p>
<p>Each design pattern is presented as a code playback that shows how a program changes over time along with my explanation about what's happening. This format helps you focus on the reasoning behind the code changes.</p>
<p>You can access the free 'book' of code playbacks here:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook">OO Design Patterns with Java</a>, by Mark Mahoney (that’s me)</p>
</blockquote>
<p>To view a code playback, click on the comments in the left panel. Each comment updates the code in the editor and highlights any changes. Read the explanation and study the code. If you get stuck, use the AI assistant like a tutor to help explain what is happening in the code.</p>
<p>For more information about code playbacks, you can watch a short demo here.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/uYbHqCNjVDM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-key-design-patterns-you-should-know">Key Design Patterns You Should Know</h2>
<h3 id="heading-strategy-pattern"><strong>Strategy Pattern</strong></h3>
<p>The <a target="_blank" href="https://www.freecodecamp.org/news/a-beginners-guide-to-the-strategy-design-pattern/"><strong>Strategy Pattern</strong></a> is used to define a 'family' of algorithms, encapsulate each one, and make them interchangeable. Software developers use the Strategy pattern when they know there are many different ways of accomplishing some behavior. Rather than include all the different ways in a single class, they separate them out into individual classes and plug them in when necessary.</p>
<p>This program creates some classes to hold student grades. Some instructors like to adjust the entire course’s grades to make them higher. Some instructors do this by dropping every student's lowest grade. Other instructors 'curve' each assignment. Since there are several different options, I will use the <strong>Strategy Pattern</strong> to isolate them and let the client choose which one they prefer.</p>
<p>Start by looking at the <code>Assignment</code>, <code>Student</code>, and <code>Course</code> classes. Once you are familiar with the core classes, watch as I change the code to implement two different approaches to <em>curving</em> the grades using the <strong>Strategy Pattern</strong>:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook/chapter/1/1"><strong>Strategy Pattern</strong> Adjusting Grades in a Course</a></p>
</blockquote>
<h3 id="heading-singleton-pattern"><strong>Singleton Pattern</strong></h3>
<p>There are times when you need to make sure there is only one instance of a class and it is accessible everywhere in your code. This is the problem that the <a target="_blank" href="https://en.wikipedia.org/wiki/Singleton_pattern"><strong>Singleton Pattern</strong></a> solves.</p>
<p>In this program, I will create a class that generates random numbers. I will rely on Java's built-in <code>Random</code> class but will be able to reproduce the exact same sequence of random numbers when in 'test mode'. I'll make sure that there is only one instance of this random number generator using the <strong>Singleton Pattern</strong>:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook/chapter/1/2"><strong>Singleton Pattern</strong> A Testable Random Number Class</a></p>
</blockquote>
<h3 id="heading-composite-pattern"><strong>Composite Pattern</strong></h3>
<p>Often, we’ll create whole/part containment tree structures. For example, in a file system there are simple files. I call these simple elements, <em>primitives</em>. We can group primitives together to form larger <em>composites</em>. Files can be grouped into directories. These composites (directories) can be grouped into still larger composites, and so on.</p>
<p>We could treat composites and primitives differently. But it often makes sense to treat them the same. Having to distinguish between the object types makes the application more complex.</p>
<p>The <a target="_blank" href="https://en.wikipedia.org/wiki/Composite_pattern"><strong>Composite Pattern</strong></a> describes how to use recursive composition so that clients don't need to make this distinction.</p>
<p>This program creates classes for printing a hierarchical collection of files and directories using the <strong>Composite Pattern</strong>:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook/chapter/1/3"><strong>Composite Pattern</strong> Displaying a Hierarchical File System</a></p>
</blockquote>
<h3 id="heading-decorator-pattern"><strong>Decorator Pattern</strong></h3>
<p>Sometimes we want to add responsibilities to individual objects, not an entire class. The <a target="_blank" href="https://en.wikipedia.org/wiki/Decorator_pattern"><strong>Decorator Pattern</strong></a> allows us to create <em>decorators</em> to provide a flexible alternative to inheritance for extending a class.</p>
<p>In this program, I create an interface for logging messages while a program is running. I use the interface to create a <code>ConsoleLogger</code> that prints the log messages to the screen. Then I start to add decorator objects that surround, or wrap, the <code>ConsoleLogger</code>. I add decorators to attach the date, author name, and time to the log messages using the <strong>Decorator Pattern</strong>:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook/chapter/1/4"><strong>Decorator Pattern</strong> Logging with Decorators</a></p>
</blockquote>
<h3 id="heading-state-pattern"><strong>State Pattern</strong></h3>
<p>Sometimes there are systems that react differently based on the 'state' that they are in. A state is a period of time during which a system will react to events according to certain rules. This state-based behavior is implemented using the <a target="_blank" href="https://en.wikipedia.org/wiki/State_pattern"><strong>State Pattern</strong></a>.</p>
<p>I’ll show you how to move through the characters in a string and parse it to account for quotes within it. For example, the following string:</p>
<p><code>"hamburgers chips 'hot dogs' pickles 'french fries'"</code></p>
<p>can be split into a collection like this:</p>
<p><code>["hamburgers", "chips", "hot dogs", "pickles", "french fries"]</code></p>
<p>There are may ways to accomplish this in Java, but I’ll show a state-based approach. When a single quote is encountered within a string I will use that as an event and move between different states using the <strong>State Pattern</strong>:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook/chapter/1/5"><strong>State Pattern</strong> String Splitting for Search Bars</a></p>
</blockquote>
<h3 id="heading-observer-pattern"><strong>Observer Pattern</strong></h3>
<p>The <a target="_blank" href="https://en.wikipedia.org/wiki/Observer_pattern"><strong>Observer Pattern</strong></a> is used when the update of a single piece of data in one object needs to be propagated to a collection of other objects.</p>
<p>For example, when the value of a cell in a spreadsheet changes, several other cells may need to be notified of that change so that they can update themselves. Similarly, in a social network application when a user makes a post, all of their friends need to be notified so that their feeds can be updated. Both of these are essentially the same problem that the <strong>Observer Pattern</strong> solves.</p>
<p>This program creates a class to hold a time in a day called <code>MyTime</code>. Then I create two different types of <code>Observers</code> that will be notified when the time changes. The two observers will re-display the time every time it changes using the <strong>Observer Pattern</strong>:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook/chapter/1/6"><strong>Observer Pattern</strong> Observing the Time Change</a></p>
</blockquote>
<h3 id="heading-proxy-pattern"><strong>Proxy Pattern</strong></h3>
<p>Sometimes we design a set of objects that have a client/server relationship but later decide that the two objects should not interact directly. This program shows how to use the <a target="_blank" href="https://en.wikipedia.org/wiki/Proxy_pattern"><strong>Proxy Pattern</strong></a> to place some new functionality in between two previously cooperating classes.</p>
<p>I create a <code>Card</code> and <code>Deck</code> class for card games. The <code>Deck</code> starts out being hosted on the same machine as the <code>Driver</code>. Then I split the <code>Driver</code> and the <code>Deck</code> class so that they can be run on different machines using the <strong>Proxy Pattern</strong>:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook/chapter/1/7"><strong>Proxy Pattern</strong> Dealing Cards from a Remote Deck</a></p>
</blockquote>
<h3 id="heading-factory-pattern"><strong>Factory Pattern</strong></h3>
<p>The <a target="_blank" href="https://en.wikipedia.org/wiki/Factory_method_pattern"><strong>Factory Pattern</strong></a> provides a mechanism for creating 'families' of related objects without specifying their concrete classes. Instantiating concrete objects in an application makes it hard to change those objects later.</p>
<p>In this program, I will create two different families of classes for a help system for two different computing platforms using the <strong>Factory Pattern</strong>:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook/chapter/1/8"><strong>Factory Pattern</strong> Getting Help in Mac and Windows</a></p>
</blockquote>
<h3 id="heading-visitor-pattern"><strong>Visitor Pattern</strong></h3>
<p>The <a target="_blank" href="https://en.wikipedia.org/wiki/Visitor_pattern"><strong>Visitor Pattern</strong></a> lets you add functionality to a hierarchy of classes without changing its interface.</p>
<p>The reason why this is important is that there are times when we cannot change an existing hierarchy of classes. Perhaps I am using a hierarchy of classes that I am not in control of but I want to add some new functionality to it anyway. This is where the <strong>Visitor Pattern</strong> comes in.</p>
<p>In this program, I’ll add functionality to the <code>File</code> and <code>Directory</code> classes from the <em>Composite</em> program that I wrote earlier with minimal changes to those classes.</p>
<p>I create a <em>visitor</em> to count the number of files and directories in a topmost directory. Then I write a <em>visitor</em> to collect only the filenames in a directory including its sub-directories using the <strong>Visitor Pattern</strong>:</p>
<blockquote>
<p><a target="_blank" href="https://playbackpress.com/books/patternsbook/chapter/1/9"><strong>Visitor Pattern</strong> Adding Functionality to a Hierarchy of Classes (File and Directory)</a></p>
</blockquote>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>I hope you enjoyed learning about object-oriented design patterns. If you are interested in other programming paradigms, you can check out some of my other 'books' <a target="_blank" href="https://playbackpress.com/books">here</a>.</p>
<p>Questions and feedback are always welcome here: <a target="_blank" href="mailto:mark@playbackpress.com">mark@playbackpress.com</a></p>
<p>If you'd like to support my work and help keep Playback Press free for all, consider donating using <a target="_blank" href="https://github.com/sponsors/markm208">GitHub Sponsors</a>. I use all of the donations for hosting costs. Your support helps me continue creating educational content like this. Thank you!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Polymorphism in Python? Explained with an Example ]]>
                </title>
                <description>
                    <![CDATA[ Polymorphism is an object-oriented programming (OOP) principle that helps you write high quality, flexible, maintainable, reusable, testable, and readable software. If you plan to work with object-oriented software, it is crucial to understand polymo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-polymorphism-in-python-example/</link>
                <guid isPermaLink="false">67a4d16ab891dd1403996d28</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Object Oriented Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ oop ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Danny ]]>
                </dc:creator>
                <pubDate>Thu, 06 Feb 2025 15:12:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738335631634/ef8f79a0-73df-430c-b955-a5325ca22f04.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Polymorphism is an object-oriented programming (OOP) principle that helps you write high quality, flexible, maintainable, reusable, testable, and readable software. If you plan to work with object-oriented software, it is crucial to understand polymorphism.</p>
<h2 id="heading-what-is-polymorphism">What is Polymorphism?</h2>
<p>The word <em>polymorphism</em> is derived from Greek, and means "having multiple forms":</p>
<ul>
<li><p>Poly = many</p>
</li>
<li><p>Morph = forms</p>
</li>
</ul>
<p><strong>In programming, polymorphism is the ability of an object to take many forms</strong>.</p>
<p>The key advantage of polymorphism is that it allows us to write more <strong>generic</strong> and <strong>reusable</strong> code. Instead of writing separate logic for different classes, we define common behaviours in a parent class and let child classes override them as needed. This eliminates the need for excessive <code>if-else</code> checks, making the code more maintainable and extensible.</p>
<p>MVC frameworks like <a target="_blank" href="http://djangoproject.com/">Django</a> use polymorphism to make code more flexible. For example, Django supports different databases like SQLite, MySQL, and PostgreSQL. Normally, each database requires different code to interact with it, but Django provides a single database API that works with all of them. This means you can write the same code for database operations, no matter which database you use. So, if you start a project with SQLite and later switch to PostgreSQL, you won’t need to rewrite much of your code, thanks to polymorphism.</p>
<p>In this article, to make things easy to understand, I’ll show you a bad code example with no polymorphism. We’ll discuss the issues that this bad code causes, and then solve the issues by refactoring the code to use polymorphism.</p>
<p>(Btw, if you learn better by video, checkout my <a target="_blank" href="https://youtu.be/zuPg8_qsL7A">Polymorphism in Python</a> YouTube video.)</p>
<h2 id="heading-first-an-example-with-no-polymorphism">First, an example with no polymorphism:</h2>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Car</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, brand, model, year, number_of_doors</span>):</span>
        self.brand = brand
        self.model = model
        self.year = year
        self.number_of_doors = number_of_doors

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Car is starting."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">stop</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Car is stopping."</span>)
</code></pre>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Motorcycle</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, brand, model, year</span>):</span>
        self.brand = brand
        self.model = model
        self.year = year

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start_bike</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Motorcycle is starting."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">stop_bike</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Motorcycle is stopping."</span>)
</code></pre>
<p>Let’s say that we want to create a list of vehicles, then loop through it and perform an inspection on each vehicle:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create list of vehicles to inspect</span>
vehicles = [
    Car(<span class="hljs-string">"Ford"</span>, <span class="hljs-string">"Focus"</span>, <span class="hljs-number">2008</span>, <span class="hljs-number">5</span>),
    Motorcycle(<span class="hljs-string">"Honda"</span>, <span class="hljs-string">"Scoopy"</span>, <span class="hljs-number">2018</span>),
]

<span class="hljs-comment"># Loop through list of vehicles and inspect them</span>
<span class="hljs-keyword">for</span> vehicle <span class="hljs-keyword">in</span> vehicles:
    <span class="hljs-keyword">if</span> isinstance(vehicle, Car):
        print(<span class="hljs-string">f"Inspecting <span class="hljs-subst">{vehicle.brand}</span> <span class="hljs-subst">{vehicle.model}</span> (<span class="hljs-subst">{type(vehicle).__name__}</span>)"</span>)
        vehicle.start()
        vehicle.stop()
    <span class="hljs-keyword">elif</span> isinstance(vehicle, Motorcycle):
        print(<span class="hljs-string">f"Inspecting <span class="hljs-subst">{vehicle.brand}</span> <span class="hljs-subst">{vehicle.model}</span> (<span class="hljs-subst">{type(vehicle).__name__}</span>)"</span>)
        vehicle.start_bike()
        vehicle.stop_bike()
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"Object is not a valid vehicle"</span>)
</code></pre>
<p>Notice the ugly code inside the <code>for</code> loop! Because <code>vehicles</code> is a list of any type of object, we have to figure out what type of object we are dealing with inside each loop before we can access any information on the object.</p>
<p>This code will continue to get uglier as we add more vehicle types. For example, if we <em>extended</em> our codebase to include a new <code>Plane</code> class, then we’d need to <em>modify</em> (and potentially break) existing code – we’d have to add another conditional check in the <code>for</code> loop for planes.</p>
<h3 id="heading-introducing-polymorphism"><strong>Introducing: Polymorphism…</strong></h3>
<p>Cars and motorcycles are both vehicles. They both share some common properties and methods. So, let’s create a parent class that contains these shared properties and methods:</p>
<p>Parent class (or "superclass"):</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Vehicle</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, brand, model, year</span>):</span>
        self.brand = brand
        self.model = model
        self.year = year

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Vehicle is starting."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">stop</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Vehicle is stopping."</span>)
</code></pre>
<p><code>Car</code> and <code>Motorcycle</code> can now <em>inherit</em> from <code>Vehicle</code>. Let’s create the child classes (or "subclasses") of the <code>Vehicle</code> superclass:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Car</span>(<span class="hljs-params">Vehicle</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, brand, model, year, number_of_doors</span>):</span>
        super().__init__(brand, model, year)
        self.number_of_doors = number_of_doors

    <span class="hljs-comment"># Below, we "override" the start and stop methods, inherited from Vehicle, to provide car-specific behaviour</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Car is starting."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">stop</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Car is stopping."</span>)
</code></pre>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Motorcycle</span>(<span class="hljs-params">Vehicle</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, brand, model, year</span>):</span>
        super().__init__(brand, model, year)

    <span class="hljs-comment"># Below, we "override" the start and stop methods, inherited from Vehicle, to provide bike-specific behaviour</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Motorcycle is starting."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">stop</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Motorcycle is stopping."</span>)
</code></pre>
<p><code>Car</code> and <code>Motorcycle</code> both extend <code>Vehicle</code>, as they are vehicles. But what’s the point in <code>Car</code> and <code>Motorcycle</code> both extending <code>Vehicle</code> if they are going to implement their own versions of the <code>start()</code> and <code>stop()</code> methods? Look at the code below:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create list of vehicles to inspect</span>
vehicles = [Car(<span class="hljs-string">"Ford"</span>, <span class="hljs-string">"Focus"</span>, <span class="hljs-number">2008</span>, <span class="hljs-number">5</span>), Motorcycle(<span class="hljs-string">"Honda"</span>, <span class="hljs-string">"Scoopy"</span>, <span class="hljs-number">2018</span>)]

<span class="hljs-comment"># Loop through list of vehicles and inspect them</span>
<span class="hljs-keyword">for</span> vehicle <span class="hljs-keyword">in</span> vehicles:
    <span class="hljs-keyword">if</span> isinstance(vehicle, Vehicle):
        print(<span class="hljs-string">f"Inspecting <span class="hljs-subst">{vehicle.brand}</span> <span class="hljs-subst">{vehicle.model}</span> (<span class="hljs-subst">{type(vehicle).__name__}</span>)"</span>)
        vehicle.start()
        vehicle.stop()
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"Object is not a valid vehicle"</span>)
</code></pre>
<p><strong>In this example</strong>:</p>
<ul>
<li><p>We have a list, <code>vehicles</code>, containing instances of both <code>Car</code> and <code>Motorcycle</code>.</p>
</li>
<li><p>We iterate through each vehicle in the list and perform a general inspection on each one.</p>
</li>
<li><p>The inspection process involves starting the vehicle, checking its brand and model, and stopping it afterwards.</p>
</li>
<li><p>Despite the vehicles being of different types, polymorphism allows us to treat them all as instances of the base <code>Vehicle</code> class. The specific implementations of the <code>start()</code> and <code>stop()</code> methods for each vehicle type are invoked dynamically at runtime, based on the actual type of each vehicle.</p>
</li>
</ul>
<p>Because the list can <em>only</em> contain objects that extend the <code>Vehicle</code> class, we know that every object will share some common fields and methods. This means that we can safely call them, without having to worry about whether each specific vehicle has these fields or methods.</p>
<p>This demonstrates how polymorphism enables code to be written in a more generic and flexible manner, allowing for easy extension and maintenance as new types of vehicles are added to the system.</p>
<p>For example, if we wanted to add another vehicle to the list, we don’t have to modify the code used to inspect vehicles (“the client code”). Instead, we can just <em>extend</em> our code base (that is, create a new class), without <em>modifying</em> existing code:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Plane</span>(<span class="hljs-params">Vehicle</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, brand, model, year, number_of_doors</span>):</span>
        super().__init__(brand, model, year)
        self.number_of_doors = number_of_doors

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Plane is starting."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">stop</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Plane is stopping."</span>)
</code></pre>
<pre><code class="lang-python"><span class="hljs-comment"># Create list of vehicles to inspect</span>
vehicles = [
    Car(<span class="hljs-string">"Ford"</span>, <span class="hljs-string">"Focus"</span>, <span class="hljs-number">2008</span>, <span class="hljs-number">5</span>),
    Motorcycle(<span class="hljs-string">"Honda"</span>, <span class="hljs-string">"Scoopy"</span>, <span class="hljs-number">2018</span>),

    <span class="hljs-comment">########## ADD A PLANE TO THE LIST: #########</span>

    Plane(<span class="hljs-string">"Boeing"</span>, <span class="hljs-string">"747"</span>, <span class="hljs-number">2015</span>, <span class="hljs-number">16</span>),

    <span class="hljs-comment">############################################</span>
]
</code></pre>
<p>The code to perform the vehicle inspections doesn’t have to change to account for a plane. Everything still works, without having to modify our inspection logic.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Polymorphism allows clients to treat different types of objects in the same way. This greatly improves the flexibility of software and maintainability of software, as new classes can be created without you having to modify (often by adding extra <code>if</code>/<code>else if</code> blocks) existing working and tested code.</p>
<h2 id="heading-further-learning">Further Learning</h2>
<p>Polymorphism is related to many other object-oriented programming principles, such as <em>dependency injection</em> and the <em>open-closed</em> SOLID principle. If you’d like to master OOP, then check out my Udemy course:</p>
<ul>
<li><a target="_blank" href="https://www.udemy.com/course/python-oop-object-oriented-programming-from-beginner-to-pro">Python OOP: Object Oriented Programming From Beginner to Pro 🎥</a></li>
</ul>
<p>If you prefer book to video, check out my books:</p>
<ul>
<li><p><a target="_blank" href="https://www.amazon.com/dp/B0DR6ZPZQ8">Amazon Kindle and paperback 📖</a></p>
</li>
<li><p><a target="_blank" href="https://doabledanny.gumroad.com/l/python-oop-beginner-to-pro">Gumroad PDF 📖</a></p>
</li>
</ul>
<p>Thanks for reading :)</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
