<?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>Tue, 29 Sep 2026 13:07:12 +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[ The Composite Design Pattern: How to Work with Individual Objects and Groups Through the Same Interface ]]>
                </title>
                <description>
                    <![CDATA[ Structural design patterns deal with how objects are created in terms of their structure and hierarchy. One of the patterns that explicitly helps you manage complex hierarchical scenarios is the Compo ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-composite-design-pattern-work-with-individual-objects-and-groups-through-the-same-interface/</link>
                <guid isPermaLink="false">6aa1a112cbd6145df593b93b</guid>
                
                    <category>
                        <![CDATA[ composite-desing-pattern ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Composite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design principles ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ structural design pattern ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwaseyi Fatunmole ]]>
                </dc:creator>
                <pubDate>Wed, 09 Sep 2026 18:10:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/2b0e9823-e943-4665-bc01-4390cf5b6a01.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Structural design patterns deal with how objects are created in terms of their structure and hierarchy.</p>
<p>One of the patterns that explicitly helps you manage complex hierarchical scenarios is the Composite Design Pattern.</p>
<p>So what does this pattern really do?</p>
<p>Let me give you an example. You have a dataset and you want a common interface to be responsible for managing that dataset, ensuring one method is used for everything. You also want to allow a blueprint to manage this data and allow the data to grow as much as it can. This is a great fit for the Composite Design Pattern in object composition.</p>
<p>Here are some other clear use cases:</p>
<ul>
<li><p>A shopping cart contains individual items. It also has bundles of items sold together. Both need a price.</p>
</li>
<li><p>A tax system has individual taxpayers. It also has family groups and corporate groups. All of them need tax calculated, discounts applied, and year-to-date totals computed.</p>
</li>
<li><p>A file system has individual files. It also has folders that contain files or other folders. Both need a size.</p>
</li>
</ul>
<p>The naïve approach is to write separate logic for individuals and groups, then add a type check wherever you need to handle both. But the logic diverges. The type checks multiply. And every new operation means updating both branches. The code becomes harder to extend and harder to trust.</p>
<p>The Composite Design Pattern eliminates this entirely. It defines a common interface that both individual objects and groups implement. The calling code never checks types. It calls the same method on a leaf or a composite and gets the correct result either way.</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-what-is-the-composite-design-pattern">What is the Composite Design Pattern</a>?</p>
</li>
<li><p><a href="#heading-the-three-layers">The Three Layers</a></p>
</li>
<li><p><a href="#heading-real-world-example-one-shopping-cart-pricing">Real World Example One: Shopping Cart Pricing</a></p>
</li>
<li><p><a href="#heading-real-world-example-two-tax-management">Real World Example Two: Tax Management</a></p>
</li>
<li><p><a href="#heading-the-power-of-nested-composites">The Power of Nested Composites</a></p>
</li>
<li><p><a href="#heading-the-composite-pattern-in-c">The Composite Pattern in C#</a></p>
</li>
<li><p><a href="#heading-when-to-use-the-composite-pattern">When to Use the Composite 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-prerequisites">Prerequisites</h2>
<p>Before reading this article, you should be comfortable with:</p>
<ul>
<li><p>Object-oriented programming: abstract classes, interfaces, and inheritance</p>
</li>
<li><p>What a design pattern is at a conceptual level</p>
</li>
<li><p>Basic Dart or C# syntax</p>
</li>
</ul>
<p>You don't need prior experience with structural design patterns. This article introduces the Composite pattern from first principles with real production examples.</p>
<h2 id="heading-what-is-the-composite-design-pattern">What is the Composite Design Pattern?</h2>
<p>The Composite Design Pattern is a structural design pattern. Where creational patterns deal with how objects are created and behavioral patterns deal with how objects communicate, structural patterns deal with how objects are composed and related to each other.</p>
<p>The Composite pattern specifically deals with part-whole hierarchies. It lets you compose objects into tree structures and then work with those trees as if every node in the tree is the same type of thing.</p>
<p>The core idea is deceptively simple: define a common interface, make individual objects implement it, and make groups of objects implement it too. Now everything in the hierarchy responds to the same methods and the calling code never needs to distinguish between a leaf and a composite.</p>
<p>This is what "treating individual objects and groups through a unified interface" means in practice. One method call, any object in the hierarchy, correct result regardless of whether you are calling it on a single item or a nested group containing hundreds of items.</p>
<h2 id="heading-the-three-layers">The Three Layers</h2>
<p>The Composite pattern has three distinct layers. Understanding each one before looking at code makes the implementation much clearer.</p>
<h3 id="heading-the-component-layer">The Component Layer</h3>
<p>This is the abstract class or interface that defines the contract for every object in the hierarchy. It declares the methods that both individual objects and groups must implement.</p>
<p>The Component is what makes uniform treatment possible: because everything in the hierarchy implements this interface, everything responds to the same method calls.</p>
<h3 id="heading-the-leaf-layer">The Leaf Layer</h3>
<p>A Leaf is a concrete implementation of the Component. It represents an individual object with no children, like a single item in a shopping cart, a single taxpayer, or a single file. The Leaf implements the Component methods with its own specific logic.</p>
<h3 id="heading-the-composite-layer">The Composite Layer</h3>
<p>A Composite is also a concrete implementation of the Component. But unlike a Leaf, it holds a collection of children. Each child is a Component, which means each child can be either a Leaf or another Composite.</p>
<p>The Composite implements the Component methods by delegating to its children and aggregating the results.</p>
<p>The relationship between these layers is what enables the tree structure and the uniform interface simultaneously.</p>
<h2 id="heading-real-world-example-one-shopping-cart-pricing">Real World Example One: Shopping Cart Pricing</h2>
<p>A shopping cart needs to calculate prices. Individual items have their own prices. Bundles group multiple items and their price is the sum of their contents. Both need to respond to <code>getPrice()</code>.</p>
<h3 id="heading-the-component">The Component</h3>
<pre><code class="language-csharp">abstract class PriceComponent {
  double getPrice();
}
</code></pre>
<p><code>PriceComponent</code> is the contract. Every object in the cart hierarchy must implement <code>getPrice()</code>. That is the entire interface: one method that's uniform across all objects.</p>
<h3 id="heading-the-leaf">The Leaf</h3>
<pre><code class="language-csharp">class CartItem extends PriceComponent {
  final int id;
  final String name;
  final double price;

  CartItem({required this.id, required this.name, required this.price});

  @override
  double getPrice() {
    return price;
  }
}
</code></pre>
<p><code>CartItem</code> is the Leaf. It represents a single item in the cart. Its <code>getPrice()</code> returns its own price directly. There's no delegation or children. Just its own value.</p>
<h3 id="heading-the-composite">The Composite</h3>
<pre><code class="language-csharp">class ItemBundle extends PriceComponent {
  final int bundleId;
  final String bundleName;
  final List&lt;PriceComponent&gt; _items = [];

  ItemBundle({required this.bundleId, required this.bundleName});

  void add(PriceComponent component) {
    _items.add(component);
  }

  void remove(PriceComponent component) {
    _items.remove(component);
  }

  @override
  double getPrice() {
    return _items.fold(0, (total, item) =&gt; total + item.getPrice());
  }
}
</code></pre>
<p><code>ItemBundle</code> is the Composite. It holds a list of <code>PriceComponent</code> children. Its <code>getPrice()</code> delegates to its children using <code>fold</code>, summing up whatever each child returns.</p>
<p>The critical detail: <code>_items</code> is a <code>List&lt;PriceComponent&gt;</code>, not a <code>List&lt;CartItem&gt;</code>. This means an <code>ItemBundle</code> can contain both <code>CartItem</code> leaves and other <code>ItemBundle</code> composites. The hierarchy can nest as deeply as needed.</p>
<h3 id="heading-using-it">Using It</h3>
<pre><code class="language-dart">void main() {
  
  final burger = CartItem(id: 1, name: 'Burger', price: 5.99);
  final fries = CartItem(id: 2, name: 'Fries', price: 2.99);
  final drink = CartItem(id: 3, name: 'Drink', price: 1.99);
  final apple = CartItem(id: 4, name: 'Apple', price: 0.99);

 
  final comboMeal = ItemBundle(bundleId: 1, bundleName: 'Combo Meal');
  comboMeal
    ..add(burger)
    ..add(fries)
    ..add(drink);


  final cart = ItemBundle(bundleId: 0, bundleName: 'My Cart');
  cart
    ..add(comboMeal) 
    ..add(apple);    

  // same method call on everything
  print('Burger: \$${burger.getPrice()}');           
  print('Combo Meal: \$${comboMeal.getPrice()}');    
  print('Full Cart: \$${cart.getPrice()}');          
}
</code></pre>
<p><code>burger.getPrice()</code> calls the Leaf implementation directly. <code>comboMeal.getPrice()</code> calls the Composite implementation which delegates to its three children. <code>cart.getPrice()</code> calls the Composite implementation which delegates to the <code>combo meal</code> composite and the <code>apple</code> leaf.</p>
<p>The calling code treats all of them identically: <code>getPrice()</code>, result, done.</p>
<h2 id="heading-real-world-example-two-tax-management">Real World Example Two: Tax Management</h2>
<p>This example shows the Composite pattern applied to a more complex domain. A tax management system needs to calculate tax amounts, apply discounts, and compute year-to-date totals. These calculations need to work for individual taxpayers and for groups of taxpayers through exactly the same interface.</p>
<h3 id="heading-the-component">The Component</h3>
<pre><code class="language-csharp">abstract class TaxManager {
  num getTaxAmount();
  num getTaxDiscount();
  num getTotalTaxYTD();
}
</code></pre>
<p><code>TaxManager</code> defines three methods. Every object in the tax hierarchy must implement all three. A single taxpayer implements them with their own data. A group implements them by aggregating across all members. The calling code calls any of these methods on any object and gets the correct result.</p>
<h3 id="heading-the-leaf">The Leaf</h3>
<pre><code class="language-csharp">class SingleUser extends TaxManager {
  final num _amount;
  final List&lt;num&gt; _allTaxes;

  SingleUser(this._amount, this._allTaxes);

  @override
  num getTaxAmount() {
    return _amount;
  }

  @override
  num getTaxDiscount() {
   
    return _amount % 2 == 0 ? _amount : (_amount / 2);
  }

  @override
  num getTotalTaxYTD() {
    num total = 0;
    for (final tax in _allTaxes) {
      total += tax;
    }
    return total;
  }
}
</code></pre>
<p><code>SingleUser</code> is the Leaf. It represents one individual taxpayer. <code>_amount</code> is their current tax amount. <code>_allTaxes</code> is a list of their tax payments over the year. Each method operates on this person's data only.</p>
<p><code>getTaxDiscount()</code> applies a simple discount rule: even amounts receive the full amount, odd amounts receive half. This rule lives on the individual and is automatically propagated through any group that contains this user.</p>
<p><code>getTotalTaxYTD()</code> sums the user's historical tax payments to produce their year-to-date total.</p>
<h3 id="heading-the-composite">The Composite</h3>
<pre><code class="language-csharp">class UserGroup extends TaxManager {
  final String groupName;
  final List&lt;TaxManager&gt; _members = [];

  UserGroup(this.groupName);

  void add(TaxManager member) {
    _members.add(member);
  }

  void remove(TaxManager member) {
    _members.remove(member);
  }

  @override
  num getTaxAmount() {
    return _members.fold(0, (total, member) =&gt; total + member.getTaxAmount());
  }

  @override
  num getTaxDiscount() {
    return _members.fold(0, (total, member) =&gt; total + member.getTaxDiscount());
  }

  @override
  num getTotalTaxYTD() {
    return _members.fold(0, (total, member) =&gt; total + member.getTotalTaxYTD());
  }
}
</code></pre>
<p><code>UserGroup</code> is the Composite. It holds a list of <code>TaxManager</code> members. Each method delegates to every member and folds the results.</p>
<p>Notice that <code>_members</code> is typed as <code>List&lt;TaxManager&gt;</code>, not <code>List&lt;SingleUser&gt;</code>. This means a <code>UserGroup</code> can contain individual <code>SingleUser</code> leaves or other <code>UserGroup</code> composites. A family group can contain individual members. A corporate group can contain family groups. The hierarchy can grow as needed and the interface never changes.</p>
<h3 id="heading-using-it">Using It</h3>
<pre><code class="language-dart">void main() {
  
  final seyisTax = SingleUser(100000, List.generate(12, (_) =&gt; 20000));
  final ronkesTax = SingleUser(5000, List.generate(12, (_) =&gt; 50000));
  final inisTax = SingleUser(20000, List.generate(12, (_) =&gt; 20000));
  final tiwasTax = SingleUser(10000, List.generate(12, (_) =&gt; 10000));

 
  print('Seyi tax amount: ${seyisTax.getTaxAmount()}');       
  print('Ronke tax amount: ${ronkesTax.getTaxAmount()}');     
  print('Seyi discount: ${seyisTax.getTaxDiscount()}');        
  print('Seyi YTD: ${seyisTax.getTotalTaxYTD()}');            

  // build a family group
  final fatunmoles = UserGroup('Fatunmoles');
  fatunmoles
    ..add(seyisTax)
    ..add(ronkesTax)
    ..add(inisTax)
    ..add(tiwasTax);

 
  
  print('Total tax: ${fatunmoles.getTaxAmount()}');      
  print('Total discount: ${fatunmoles.getTaxDiscount()}'); 
  print('Total YTD: ${fatunmoles.getTotalTaxYTD()}');    

  // a second family group
  final child1 = SingleUser(100000, List.generate(12, (_) =&gt; 20000));
  final child2 = SingleUser(100000, List.generate(12, (_) =&gt; 20000));
  final child3 = SingleUser(100000, List.generate(12, (_) =&gt; 20000));

  final unknownFamily = UserGroup('UnknownFamily');
  unknownFamily
    ..add(child1)
    ..add(child2)
    ..add(child3);


  print('Tax: ${unknownFamily.getTaxAmount()}');
  print('Discount: ${unknownFamily.getTaxDiscount()}');
  print('YTD: ${unknownFamily.getTotalTaxYTD()}');
}
</code></pre>
<p>The same three method calls work on <code>seyisTax</code> (one person), <code>fatunmoles</code> (four people), and <code>unknownFamily</code> (three people). The calling code is identical. The results are correct for each level of the hierarchy.</p>
<h2 id="heading-the-power-of-nested-composites">The Power of Nested Composites</h2>
<p>The most powerful aspect of the Composite pattern is that a Composite can contain other Composites. A <code>UserGroup</code> can contain other <code>UserGroup</code> objects. This enables deep hierarchies while maintaining the same uniform interface at every level.</p>
<pre><code class="language-dart">void demonstrateNesting() {
  
  final seyi = SingleUser(100000, List.generate(12, (_) =&gt; 20000));
  final ronke = SingleUser(5000, List.generate(12, (_) =&gt; 50000));
  final ini = SingleUser(20000, List.generate(12, (_) =&gt; 20000));
  final tiwa = SingleUser(10000, List.generate(12, (_) =&gt; 10000));

  final child1 = SingleUser(100000, List.generate(12, (_) =&gt; 20000));
  final child2 = SingleUser(100000, List.generate(12, (_) =&gt; 20000));
  final child3 = SingleUser(100000, List.generate(12, (_) =&gt; 20000));

  // family groups
  final fatunmoles = UserGroup('Fatunmoles');
  fatunmoles
    ..add(seyi)
    ..add(ronke)
    ..add(ini)
    ..add(tiwa);

  final unknownFamily = UserGroup('UnknownFamily');
  unknownFamily
    ..add(child1)
    ..add(child2)
    ..add(child3);

  // a composite that contains other composites
  // both families treated as one unit
  final allFamilies = UserGroup('AllFamilies');
  allFamilies
    ..add(fatunmoles)    
    ..add(unknownFamily); 

  // same interface, now covers all 7 people across both families
  print('All families combined:');
  print('Total tax: ${allFamilies.getTaxAmount()}');
  print('Total discount: ${allFamilies.getTaxDiscount()}');
  print('Total YTD: ${allFamilies.getTotalTaxYTD()}');
}
</code></pre>
<p><code>allFamilies.getTaxAmount()</code> traverses the entire tree: it asks <code>fatunmoles</code> for its total, which asks each of its four members, and asks <code>unknownFamily</code> for its total, which asks each of its three members. Seven people, one method call. The caller doesn't know the depth of the tree or how many members exist at any level.</p>
<p>This is where the pattern demonstrates its full value. You can build an organization with divisions, departments, teams, and individuals, all implementing the same <code>TaxManager</code> interface, and call <code>getTaxAmount()</code> on the organization to get the total for every single person in it. Or call it on a single department. Or call it on a single person. The interface is always the same.</p>
<h2 id="heading-the-composite-pattern-in-c">The Composite Pattern in C#</h2>
<p>The same pattern in C# shows that this is a universal structural principle. Here is the tax management system replicated in C# for a corporate payroll context.</p>
<h3 id="heading-the-component">The Component</h3>
<pre><code class="language-csharp">public interface ITaxManager
{
    decimal GetTaxAmount();
    decimal GetTaxDiscount();
    decimal GetTotalTaxYTD();
}
</code></pre>
<h3 id="heading-the-leaf">The Leaf</h3>
<pre><code class="language-csharp">public class Employee : ITaxManager
{
    private readonly string _name;
    private readonly decimal _taxAmount;
    private readonly List&lt;decimal&gt; _yearlyTaxes;

    public Employee(string name, decimal taxAmount, List&lt;decimal&gt; yearlyTaxes)
    {
        _name = name;
        _taxAmount = taxAmount;
        _yearlyTaxes = yearlyTaxes;
    }

    public decimal GetTaxAmount() =&gt; _taxAmount;

    public decimal GetTaxDiscount()
    {
        return _taxAmount % 2 == 0 ? _taxAmount : _taxAmount / 2;
    }

    public decimal GetTotalTaxYTD()
    {
        return _yearlyTaxes.Sum();
    }
}
</code></pre>
<h3 id="heading-the-composite">The Composite</h3>
<pre><code class="language-csharp">public class Department : ITaxManager
{
    private readonly string _name;
    private readonly List&lt;ITaxManager&gt; _members = new();

    public Department(string name)
    {
        _name = name;
    }

    public void Add(ITaxManager member) =&gt; _members.Add(member);
    public void Remove(ITaxManager member) =&gt; _members.Remove(member);

    public decimal GetTaxAmount()
    {
        return _members.Sum(m =&gt; m.GetTaxAmount());
    }

    public decimal GetTaxDiscount()
    {
        return _members.Sum(m =&gt; m.GetTaxDiscount());
    }

    public decimal GetTotalTaxYTD()
    {
        return _members.Sum(m =&gt; m.GetTotalTaxYTD());
    }
}
</code></pre>
<h3 id="heading-using-it-in-c">Using It in C#</h3>
<pre><code class="language-csharp">
var alice = new Employee("Alice", 150000, Enumerable.Repeat(25000m, 12).ToList());

var bob = new Employee("Bob", 80000, Enumerable.Repeat(15000m, 12).ToList());

var carol = new Employee("Carol", 120000, Enumerable.Repeat(20000m, 12).ToList());

var dave = new Employee("Dave", 95000, Enumerable.Repeat(18000m, 12).ToList());


var engineering = new Department("Engineering");
engineering.Add(alice);
engineering.Add(bob);


var design = new Department("Design");
design.Add(carol);
design.Add(dave);


var company = new Department("TechCorp");
company.Add(engineering);
company.Add(design);


Console.WriteLine($"Alice tax: {alice.GetTaxAmount()}");
Console.WriteLine($"Engineering total: {engineering.GetTaxAmount()}");

// entire company — traverses all departments and all employees
Console.WriteLine($"Company total tax: {company.GetTaxAmount()}");
Console.WriteLine($"Company total discount: {company.GetTaxDiscount()}");
Console.WriteLine($"Company YTD: {company.GetTotalTaxYTD()}");
</code></pre>
<p>The structure is identical to the Dart implementation. <code>ITaxManager</code> is the Component, <code>Employee</code> is the Leaf, and <code>Department</code> is the Composite. The hierarchy nests: a <code>Department</code> of <code>Departments</code> forms the company. The calling code calls the same three methods on any node in the tree and gets the correct aggregated result.</p>
<p>This is the same pattern solving the same problem in a different language. The structural principle is universal.</p>
<h2 id="heading-when-to-use-the-composite-pattern">When to Use the Composite Pattern</h2>
<p>Use the Composite pattern when you have a part-whole hierarchy where both parts and wholes need to be treated uniformly.</p>
<p>It's also a good choice when the calling code shouldn't need to distinguish between individual objects and groups. If you find yourself writing writing conditional statements to check if an object is a group or single frequently, that's a signal that Composite would eliminate those branches.</p>
<p>It's helpful when the hierarchy needs to be flexible and deeply nestable. File systems, organizational charts, UI component trees, category hierarchies, tax systems, or shopping carts with bundles: any domain where containers can hold other containers benefits from Composite.</p>
<p>And it's useful when new types of leaves or composites might be added in the future. Because everything implements the same Component interface, adding a new type of leaf (a <code>CorporateTaxpayer</code> alongside <code>SingleUser</code>) or a new type of composite (a <code>TaxBracketGroup</code>) means creating one new class. The calling code and all existing classes remain unchanged.</p>
<h2 id="heading-when-not-to-use-it">When Not to Use It</h2>
<p>Avoid Composite when the hierarchy is simple and unlikely to nest. If you have individual items and exactly one level of grouping with no nesting, the pattern adds abstraction that a simpler approach would not require.</p>
<p>It's also not a good choice when individual objects and groups genuinely need different interfaces. If groups need many additional methods that individuals never need, forcing them into the same interface creates an interface that's too broad and violates the Interface Segregation Principle.</p>
<p>Avoid it when performance is critical and the overhead of recursive traversal matters. Deep hierarchies with millions of nodes traversed frequently might benefit from a different approach that caches aggregated results.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The Composite Design Pattern solves a fundamental problem in hierarchical systems: how do you perform the same operation on both individual objects and groups of objects without writing two separate implementations or littering your code with type checks?</p>
<p>The answer is a common interface. Every object in the hierarchy implements the same contract. Individual objects implement it with their own data. Groups implement it by delegating to their children and aggregating the results. The calling code calls the same method and gets the correct answer regardless of whether it's talking to a leaf or a composite containing a hundred nested levels.</p>
<p>The shopping cart example shows this for pricing: one <code>getPrice()</code> method, called identically on a single item or a bundle containing other bundles. The tax management example shows this for a domain with multiple operations: <code>getTaxAmount()</code>, <code>getTaxDiscount()</code>, and <code>getTotalTaxYTD()</code> called identically on a single taxpayer, a family group, or a composite of family groups.</p>
<p>The nested composite demonstration shows the full power: a <code>UserGroup</code> containing other <code>UserGroups</code>, each containing <code>SingleUsers</code>, all responding to the same interface and producing correct aggregated results at every level. Seven people, one method call. The tree is traversed automatically.</p>
<p>C# shows the same principle in a corporate payroll context: employees as leaves, departments as composites, and the company as a composite of departments. One interface, any level of the hierarchy. Correct result every time.</p>
<p>The Composite pattern doesn't eliminate complexity. It contains it. The complexity of aggregating results across a deep hierarchy lives inside the Composite's <code>fold</code> calls, not scattered across the calling code. The calling code stays clean. The hierarchy stays flexible. New types can be added without changing anything that already exists.</p>
<p>That's the structural discipline the Composite pattern provides.</p>
<p>Happy Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Design Patterns Handbook: Learn Popular Design Patterns with C# Code Examples ]]>
                </title>
                <description>
                    <![CDATA[ Design patterns are reusable solutions to common problems in software design. Think of them as blueprints: not finished code, but proven templates you can adapt to solve a specific problem in your own ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-design-patterns-handbook-learn-popular-design-patterns-with-c-code-examples/</link>
                <guid isPermaLink="false">6a9eec68a0d0c091f35da7b8</guid>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Isaiah Clifford Opoku ]]>
                </dc:creator>
                <pubDate>Mon, 07 Sep 2026 16:55:04 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/4b19ec2c-2756-44d4-9a96-a5f196fdaae3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Design patterns are <strong>r</strong>eusable solutions to common problems in software design. Think of them as blueprints: not finished code, but proven templates you can adapt to solve a specific problem in your own codebase.</p>
<p>This handbook serves as a practical guide to understanding software design patterns. I wrote it for every developer, regardless of the language you program in. Examples are written in C#, but every concept here applies equally to Python, Java, TypeScript, Go, and beyond.</p>
<p>The source code lives at <a href="https://github.com/Clifftech123/design-patterns-handbook">github.com/Clifftech123/design-patterns-handbook</a>.</p>
<h3 id="heading-things-to-keep-in-mind">Things to Keep in Mind:</h3>
<ul>
<li><p><strong>Design patterns aren't code.</strong> They're a way of <em>thinking</em> about how to structure your code. They're a tool, not a silver bullet, for solving specific design problems.</p>
</li>
<li><p><strong>The concepts are universal.</strong> The examples here are written in C#, but the same patterns exist in every language. If you write Python, Java, Go, or TypeScript, you're already using some of these without knowing it.</p>
</li>
<li><p><strong>There's no one-size-fits-all pattern.</strong> Each pattern exists to address a particular kind of problem. Understanding <em>what problem a pattern solves</em> is more important than memorizing the implementation.</p>
</li>
</ul>
<p>I use C# here as the teaching language because it's clear, readable, and widely understood. The goal of this handbook is for you to walk away understanding the pattern itself, not just the C# code.</p>
<p>There are three main types of design patterns: Creational, Structural, and Behavioral. We'll look at each one in turn here, starting with Creational.</p>
<h3 id="heading-what-well-cover">What We'll Cover:</h3>
<ul>
<li><p><a href="#heading-things-to-keep-in-mind">Things to Keep in Mind</a></p>
</li>
<li><p><a href="#heading-creational-design-patterns">Creational Design Patterns</a></p>
<ul>
<li><p><a href="#heading-1-singleton-design-pattern">1. Singleton Design Pattern</a></p>
</li>
<li><p><a href="#heading-2-the-factory-method">2. The Factory Method</a></p>
</li>
<li><p><a href="#heading-3-the-abstract-factory-design-pattern">3. The Abstract Factory Design Pattern</a></p>
</li>
<li><p><a href="#heading-4-the-builder-design-pattern">4. The Builder Design Pattern</a></p>
</li>
<li><p><a href="#heading-5-the-prototype-design-pattern">5. The Prototype Design Pattern</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-structural-design-patterns">Structural Design Patterns</a></p>
<ul>
<li><p><a href="#heading-1-the-adapter-design-pattern">1. The Adapter Design Pattern</a></p>
</li>
<li><p><a href="#heading-2-the-bridge-design-pattern">2. The Bridge Design Pattern</a></p>
</li>
<li><p><a href="#heading-3-the-composite-design-pattern">3. The Composite Design Pattern</a></p>
</li>
<li><p><a href="#heading-4-the-decorator-design-pattern">4. The Decorator Design Pattern</a></p>
</li>
<li><p><a href="#heading-5-the-facade-design-pattern">5. The Facade Design Pattern</a></p>
</li>
<li><p><a href="#heading-6-the-flyweight-design-pattern">6. The Flyweight Design Pattern</a></p>
</li>
<li><p><a href="#heading-7-the-proxy-design-pattern">7. The Proxy Design Pattern</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-behavioral-design-patterns">Behavioral Design Patterns</a></p>
<ul>
<li><p><a href="#heading-1-the-chain-of-responsibility-design-pattern">1. The Chain of Responsibility Design Pattern</a></p>
</li>
<li><p><a href="#heading-2-the-command-design-pattern">2. The Command Design Pattern</a></p>
</li>
<li><p><a href="#heading-3-the-interpreter-design-pattern">3. The Interpreter Design Pattern</a></p>
</li>
<li><p><a href="#heading-4-the-iterator-design-pattern">4. The Iterator Design Pattern</a></p>
</li>
<li><p><a href="#heading-5-the-mediator-design-pattern">5. The Mediator Design Pattern</a></p>
</li>
<li><p><a href="#heading-6-the-memento-design-pattern">6. The Memento Design Pattern</a></p>
</li>
<li><p><a href="#heading-7-the-observer-design-pattern">7. The Observer Design Pattern</a></p>
</li>
<li><p><a href="#heading-8-the-state-design-pattern">8. The State Design Pattern</a></p>
</li>
<li><p><a href="#heading-9-the-strategy-design-pattern">9. The Strategy Design Pattern</a></p>
</li>
<li><p><a href="#heading-10-the-template-method-design-pattern">10. The Template Method Design Pattern</a></p>
</li>
<li><p><a href="#heading-11-the-visitor-design-pattern">11. The Visitor Design Pattern</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
<ul>
<li><a href="#heading-a-few-things-worth-remembering">A few things worth remembering</a></li>
</ul>
</li>
</ul>
<h2 id="heading-creational-design-patterns">Creational Design Patterns</h2>
<p>Simply put, Creational patterns are all about <strong>how objects are created</strong>. They can be divided into class-creation patterns, which use inheritance to decide which class to instantiate, and object-creation patterns, which use delegation to get the job done.</p>
<p>Wikipedia describes them as:</p>
<blockquote>
<p><em>"A creational pattern aims to separate a system from how its objects are created, composed, and represented. They increase the system's flexibility in terms of the what, who, how, and when of object creation."</em></p>
<p><strong>(</strong><a href="https://en.wikipedia.org/wiki/Creational_pattern"><strong>Source</strong></a><strong>)</strong></p>
</blockquote>
<p>So Creational patterns keep the details of object creation <strong>hidden from the client code</strong>, making the system easier to manage and maintain.</p>
<p>They also abstract away how objects are created, composed, and represented, so the rest of your code doesn't need to care.</p>
<p>There are five Creational design patterns, which we'll go over one by one below:</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>: Defines an interface for creating an object, but lets subclasses decide which class to instantiate.</p>
</li>
<li><p><strong>Abstract Factory</strong>: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.</p>
</li>
<li><p><strong>Builder</strong>: Separates the construction of a complex object from its representation, so the same construction process can produce different results.</p>
</li>
<li><p><strong>Prototype</strong>: Creates new objects by cloning an existing instance rather than building one from scratch.</p>
</li>
</ol>
<h3 id="heading-1-singleton-design-pattern">1. Singleton Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example:</h4>
<p>Think of the conductor of an orchestra. An orchestra has one conductor. Every musician on stage looks to that same conductor for direction: when to start, when to stop, how fast to play, and how loud to go.</p>
<p>The conductor is the single point of authority that all musicians connect to and take decisions from. You can't have two conductors standing at the front giving different instructions. That would cause chaos. No matter which musician needs guidance, they all reach the same one person.</p>
<p>That's exactly how the Singleton works in code: one instance, shared by everyone who needs it, making decisions from one place.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if two musicians get different conductors giving different instructions? The performance falls apart. There must be one conductor that every musician looks to, without exception.</p>
</li>
<li><p>How does a musician find the conductor? They don't go searching. There's one well-known place everyone looks, and the same conductor is always there.</p>
</li>
<li><p>What stops someone from appointing a second conductor? The orchestra itself controls this. Once a conductor is on the podium, no second one can take it.</p>
</li>
</ul>
<p>In simple terms, there's only one instance of the class, and every part of the system that needs it gets access to that exact same instance (never a new one).</p>
<p>Here's how Wikipedia describes the Singleton pattern:</p>
<blockquote>
<p><em>"In object-oriented programming, the singleton pattern is a software design pattern that restricts the instantiation of a class to a singular instance. The pattern is useful when exactly one object is needed to coordinate actions across a system." (</em><a href="https://en.wikipedia.org/wiki/Singleton_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>We'll model the analogy directly now. The <code>OrchestraConductor</code> is the Singleton: one instance, shared by all musicians, making all decisions.</p>
<pre><code class="language-csharp">public class OrchestraConductor
{
    // Step 1: Hold the one instance here
    private static OrchestraConductor _instance;

    // Step 2: Private constructor - nobody outside can do: new OrchestraConductor()
    private OrchestraConductor() { }

    // Step 3: The only way to get the conductor
    public static OrchestraConductor GetInstance()
    {
        if (_instance == null)
        {
            _instance = new OrchestraConductor();
        }

        return _instance;
    }

    // Decisions the conductor makes
    public void Start()                    =&gt; Console.WriteLine("Conductor: Begin playing.");
    public void Stop()                     =&gt; Console.WriteLine("Conductor: Stop playing.");
    public void SetTempo(string tempo)     =&gt; Console.WriteLine($"Conductor: Tempo is now {tempo}.");
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">// Violinist asks for the conductor
OrchestraConductor violinist = OrchestraConductor.GetInstance();

// Pianist asks for the conductor
OrchestraConductor pianist = OrchestraConductor.GetInstance();

// Are they talking to the same conductor?
Console.WriteLine(object.ReferenceEquals(violinist, pianist)); // True

violinist.SetTempo("Allegro");
pianist.Start();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">True
Conductor: Tempo is now Allegro.
Conductor: Begin playing.
</code></pre>
<p>Both musicians got the <strong>same conductor</strong>. The constructor never ran twice. That is the Singleton pattern.</p>
<h4 id="heading-when-to-use-the-singleton-pattern">When to Use the Singleton Pattern</h4>
<p>Reach for Singleton when you need one shared resource that the whole application talks to, such as a logger, a configuration manager, or a database connection pool.</p>
<p>It's also a good idea when having more than one instance would cause incorrect behaviour or conflicting state.</p>
<p>And it's helpful when you want a global point of access to an object without passing it around everywhere.</p>
<h3 id="heading-2-the-factory-method">2. The Factory Method</h3>
<p>Think of a recruitment agency. A company calls the agency and says "we need a worker." The company doesn't go out and create the worker themselves. They just make the request.</p>
<p>The agency decides which specific person to send: a developer, a designer, or a tester, depending on what the company needs. The company doesn't know or care exactly who is coming. They just know the person will be able to do the job.</p>
<p>That's the Factory Method. Your code asks for an object. The Factory decides which specific type to create and hands it back. You work with it without needing to know exactly what it is under the hood.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>The company shouldn't need to know who they're getting. They just need someone who can do the job. The agency handles the decision of whom to send. The company never has to worry about the details.</p>
</li>
<li><p>What if the company needs a different type of worker tomorrow? They call the same agency. The agency decides. The company's process doesn't change, only the agency's decision does.</p>
</li>
<li><p>What if a new type of worker needs to be introduced? A new specialist agency is created to handle that. Everything else stays exactly the same.</p>
</li>
</ul>
<p>In simple terms, we define an interface for creating an object, but let subclasses decide which class to instantiate. The factory method lets a class defer instantiation to subclasses.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"In object-oriented programming, the factory method pattern is a design pattern that uses factory methods to deal with the problem of creating objects without having to specify their exact classes. Factory methods can be specified in an interface and implemented by subclasses, or implemented in a base class and optionally overridden by subclasses." (</em><a href="https://en.wikipedia.org/wiki/Factory_method_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>The agency is the factory. The worker types are the products. The company is the client.</p>
<pre><code class="language-csharp">// The worker interface - all workers can do a job
public interface IWorker
{
    void DoWork();
}
</code></pre>
<pre><code class="language-csharp">// The concrete workers
public class Developer : IWorker
{
    public void DoWork() =&gt; Console.WriteLine("Developer: Writing code.");
}

public class Designer : IWorker
{
    public void DoWork() =&gt; Console.WriteLine("Designer: Creating designs.");
}
</code></pre>
<pre><code class="language-csharp">// The base agency - declares the factory method
public abstract class RecruitmentAgency
{
    // This is the Factory Method - subclasses decide who to hire
    public abstract IWorker HireWorker();
}
</code></pre>
<pre><code class="language-csharp">// Concrete agencies - each one decides which worker to send
public class TechAgency : RecruitmentAgency
{
    public override IWorker HireWorker() =&gt; new Developer();
}

public class DesignAgency : RecruitmentAgency
{
    public override IWorker HireWorker() =&gt; new Designer();
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">// Company A needs a tech worker
RecruitmentAgency agency = new TechAgency();
IWorker worker = agency.HireWorker();
worker.DoWork();

// Company B needs a design worker
RecruitmentAgency agency2 = new DesignAgency();
IWorker worker2 = agency2.HireWorker();
worker2.DoWork();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Developer: Writing code.
Designer: Creating designs.
</code></pre>
<p>The company never used <code>new Developer()</code> or <code>new Designer()</code> directly. The agency made that decision. That is the Factory Method.</p>
<h3 id="heading-3-the-abstract-factory-design-pattern">3. The Abstract Factory Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a furniture store that sells collections. You walk in and choose a style: Modern or Victorian. Once you choose, everything you get comes from that same collection. The sofa, the chair, and the coffee table all match. The store ensures that you never walk out with a modern sofa paired with a Victorian chair. You don't pick individual pieces and hope they go together. The collection guarantees they will.</p>
<p>That's the Abstract Factory. You choose a family, and the Factory produces every object you need from that same family. Everything it gives you is guaranteed to work together.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if a customer mixes furniture from different collections? The room looks inconsistent. The store solves this by grouping everything into collections. You pick one collection and everything comes from it.</p>
</li>
<li><p>What if the store wants to introduce a new collection? They create a new collection set. Every existing collection stays untouched. The customer's experience doesn't change, only the options grow.</p>
</li>
<li><p>What if different stores carry different collections? Each store is its own factory. A customer walks into any store and follows the same process. The store handles which specific pieces to provide.</p>
</li>
</ul>
<p>In simple terms, you provide an interface for creating families of related objects, without specifying their concrete classes.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The abstract factory pattern provides a way to create families of related objects without imposing their concrete classes, by encapsulating a group of individual factories that have a common theme without specifying their concrete classes."</em></p>
<p><strong>Source:</strong> <a href="https://en.wikipedia.org/wiki/Abstract_factory_pattern">Wikipedia - Abstract factory pattern</a></p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>The furniture store is the abstract factory. Modern and Victorian are the concrete factories. Sofa and Chair are the products.</p>
<pre><code class="language-csharp">// The product interfaces - every furniture type has a contract
public interface ISofa  { void Describe(); }
public interface IChair { void Describe(); }
</code></pre>
<pre><code class="language-csharp">// Modern collection
public class ModernSofa : ISofa
{
    public void Describe() =&gt; Console.WriteLine("Sofa: Sleek modern design.");
}

public class ModernChair : IChair
{
    public void Describe() =&gt; Console.WriteLine("Chair: Minimalist modern style.");
}
</code></pre>
<pre><code class="language-csharp">// Victorian collection
public class VictorianSofa : ISofa
{
    public void Describe() =&gt; Console.WriteLine("Sofa: Ornate Victorian design.");
}

public class VictorianChair : IChair
{
    public void Describe() =&gt; Console.WriteLine("Chair: Classic Victorian style.");
}
</code></pre>
<pre><code class="language-csharp">// The abstract factory - every store can produce a sofa and a chair
public interface IFurnitureFactory
{
    ISofa  CreateSofa();
    IChair CreateChair();
}
</code></pre>
<pre><code class="language-csharp">// Concrete factories - each one produces its own collection
public class ModernFurnitureFactory : IFurnitureFactory
{
    public ISofa  CreateSofa()  =&gt; new ModernSofa();
    public IChair CreateChair() =&gt; new ModernChair();
}

public class VictorianFurnitureFactory : IFurnitureFactory
{
    public ISofa  CreateSofa()  =&gt; new VictorianSofa();
    public IChair CreateChair() =&gt; new VictorianChair();
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">// Customer orders a Modern collection
IFurnitureFactory factory = new ModernFurnitureFactory();
ISofa  sofa  = factory.CreateSofa();
IChair chair = factory.CreateChair();
sofa.Describe();
chair.Describe();

// Customer orders a Victorian collection
IFurnitureFactory factory2 = new VictorianFurnitureFactory();
ISofa  sofa2  = factory2.CreateSofa();
IChair chair2 = factory2.CreateChair();
sofa2.Describe();
chair2.Describe();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Sofa: Sleek modern design.
Chair: Minimalist modern style.
Sofa: Ornate Victorian design.
Chair: Classic Victorian style.
</code></pre>
<p>Every piece came from the same collection. The client never used <code>new ModernSofa()</code> or <code>new VictorianChair()</code> directly. The factory kept the family together. That's the Abstract Factory.</p>
<h4 id="heading-when-to-use-abstract-factory">When to Use Abstract Factory:</h4>
<p>Use the Abstract Factory pattern when your system needs to work with multiple families of related objects and you need to ensure they're always used together.</p>
<p>It also works well when you want to swap out an entire family of objects in one place without touching the rest of your code.</p>
<p>And it's a good choice when you want to enforce consistency across related objects, so nothing from one family gets accidentally mixed with another.</p>
<h3 id="heading-4-the-builder-design-pattern">4. The Builder Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a tailor making a suit. Every customer that walks in goes through the same process: take measurements, choose the fabric, select the lining, pick the buttons, and decide on the lapel style.</p>
<p>The tailor follows those same steps for every order. But the finished suit is completely unique to each customer. A businessman walks out with a sharp formal suit. A wedding guest walks out with something entirely different. Same process, same tailor, but with different result every time.</p>
<p>That's the Builder. The construction process stays the same. What changes are the choices made at each step.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if a suit had to be assembled all at once with no steps? You would have to know every detail upfront and get it all right in one go. The tailor breaks it down into steps so each decision is made clearly, one at a time.</p>
</li>
<li><p>What if two customers want completely different suits but go through the same tailor? The tailor follows the same process for both. The steps don't change, only the choices within each step.</p>
</li>
<li><p>What if a new suit style needs to be introduced? A new set of choices is defined for that style. The tailoring process itself stays untouched.</p>
</li>
</ul>
<p>In simple terms, you separate the construction of a complex object from its representation, so that the same construction process can create different results.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The Builder pattern separates the construction of a complex object from its representation so that the same construction process can create different representations."</em> <a href="https://en.wikipedia.org/wiki/Builder_pattern">(Source)</a></p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>The tailor is the director. The suit is the product. The builder handles the step-by-step construction.</p>
<pre><code class="language-csharp">// The product
public class Suit
{
    public string Fabric  { get; set; }
    public string Lining  { get; set; }
    public string Buttons { get; set; }

    public void Describe()
    {
        Console.WriteLine($"Suit: {Fabric} fabric, {Lining} lining, {Buttons} buttons.");
    }
}
</code></pre>
<pre><code class="language-csharp">// The builder - defines the steps
public interface ISuitBuilder
{
    void SetFabric();
    void SetLining();
    void SetButtons();
    Suit GetSuit();
}
</code></pre>
<pre><code class="language-csharp">// Business suit builder
public class BusinessSuitBuilder : ISuitBuilder
{
    private Suit _suit = new Suit();

    public void SetFabric()  =&gt; _suit.Fabric  = "Dark wool";
    public void SetLining()  =&gt; _suit.Lining  = "Silk";
    public void SetButtons() =&gt; _suit.Buttons = "Black horn";
    public Suit GetSuit()    =&gt; _suit;
}

// Wedding suit builder
public class WeddingSuitBuilder : ISuitBuilder
{
    private Suit _suit = new Suit();

    public void SetFabric()  =&gt; _suit.Fabric  = "Ivory linen";
    public void SetLining()  =&gt; _suit.Lining  = "Satin";
    public void SetButtons() =&gt; _suit.Buttons = "Pearl";
    public Suit GetSuit()    =&gt; _suit;
}
</code></pre>
<pre><code class="language-csharp">// The tailor - the director who runs the process
public class Tailor
{
    public Suit MakeSuit(ISuitBuilder builder)
    {
        builder.SetFabric();
        builder.SetLining();
        builder.SetButtons();
        return builder.GetSuit();
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">Tailor tailor = new Tailor();

Suit businessSuit = tailor.MakeSuit(new BusinessSuitBuilder());
businessSuit.Describe();

Suit weddingSuit = tailor.MakeSuit(new WeddingSuitBuilder());
weddingSuit.Describe();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-plaintext">Suit: Dark wool fabric, Silk lining, Black horn buttons.
Suit: Ivory linen fabric, Satin lining, Pearl buttons.
</code></pre>
<p>The same tailor, following the same process, creates two completely different suits. That is the Builder.</p>
<h4 id="heading-when-to-use-the-builder-design-pattern">When to Use the Builder Design Pattern</h4>
<p>Use the Builder pattern when an object has many parts or configurations and building it all at once would be confusing.</p>
<p>It's also a good choice when you want the same construction process to produce different results depending on the choices made at each step.</p>
<p>And reach for it when you want to keep the construction logic separate from the object itself, so each can change independently.</p>
<h3 id="heading-5-the-prototype-design-pattern">5. The Prototype Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Imagine you're building a drawing application. Users can create shapes like circles, rectangles, or triangles, each with its own colour, size, and position.</p>
<p>Now imagine the user wants ten red circles of the same size placed across the canvas. Creating each one from scratch means repeating the same setup ten times. What if the shape is complex, with many configured properties? That becomes expensive and repetitive.</p>
<p>The Prototype pattern solves this by letting you take one fully configured shape and clone it. The clone starts as an exact copy. The user then moves it, recolours it, or resizes it independently. The original shape is never touched. This also means new shape types can be added at runtime without the application needing to know about them in advance.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>Creating a new shape from scratch every time is expensive. If a shape has many properties, setting them all up repeatedly wastes resources. Cloning an already configured object is far cheaper.</p>
</li>
<li><p>The application shouldn't need to know the exact type of shape it's copying. At runtime, shapes can be added or removed dynamically. The app just calls clone and gets back a ready object, whatever type it happens to be.</p>
</li>
<li><p>Modifying a copy should never affect the original. Each cloned shape is fully independent. Changes to the copy stay with the copy.</p>
</li>
</ul>
<p>In simple terms, you can create new objects by copying an existing one. The copy starts identical to the original and can then be changed independently.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The Prototype pattern is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects." (</em><a href="https://en.wikipedia.org/wiki/Prototype_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>Every shape knows how to clone itself. The application never calls <code>new Circle()</code> or <code>new Rectangle()</code> directly at runtime. It clones what already exists.</p>
<pre><code class="language-csharp">// The prototype interface - every shape must be able to clone itself
public abstract class Shape
{
    public string Colour { get; set; }
    public int    Size   { get; set; }

    public abstract Shape Clone();
    public abstract void  Describe();
}
</code></pre>
<pre><code class="language-csharp">// Concrete shapes
public class Circle : Shape
{
    public override Shape Clone()    =&gt; (Shape)this.MemberwiseClone();
    public override void  Describe() =&gt; Console.WriteLine($"Circle  | Colour: {Colour} | Size: {Size}");
}

public class Rectangle : Shape
{
    public override Shape Clone()    =&gt; (Shape)this.MemberwiseClone();
    public override void  Describe() =&gt; Console.WriteLine($"Rectangle | Colour: {Colour} | Size: {Size}");
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">// Create one configured circle
Circle original = new Circle { Colour = "Red", Size = 50 };

// Clone it instead of building from scratch
Shape clone1 = original.Clone();
Shape clone2 = original.Clone();

// Modify the clones independently
clone2.Colour = "Blue";

original.Describe();
clone1.Describe();
clone2.Describe();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-plaintext">Circle  | Colour: Red  | Size: 50
Circle  | Colour: Red  | Size: 50
Circle  | Colour: Blue | Size: 50
</code></pre>
<p><code>clone2</code> changed to blue. The original stayed red. Each object is fully independent. That's the Prototype.</p>
<h4 id="heading-when-to-use-the-prototype-design-pattern">When to Use the Prototype Design Pattern:</h4>
<p>Use Prototype when creating a new object from scratch is expensive or complex and an existing object already has everything configured.</p>
<p>It's also helpful when the application needs to create objects at runtime without knowing their exact type in advance.</p>
<p>And it's great when you need many variations of an object and want to start from a known good state rather than rebuild every time.</p>
<h2 id="heading-structural-design-patterns">Structural Design Patterns</h2>
<p>Simply put, structural patterns are all about <strong>how classes and objects are composed to form larger structures</strong>. They use inheritance and composition to let you build flexible, efficient structures without having to rewrite everything from scratch.</p>
<p>Wikipedia describes them as:</p>
<blockquote>
<p><em>"In software engineering, structural patterns are design patterns that ease the design by identifying a simple way to realize relationships among entities." (</em><a href="https://en.wikipedia.org/wiki/Structural_pattern">Source</a>)</p>
</blockquote>
<p>Structural design patterns describe how objects and classes are combined to form <strong>larger, more complex structures</strong> while keeping those structures flexible and efficient.</p>
<p>They focus on composition over inheritance: how you connect things, not just what things are.</p>
<p>There are seven Structural design patterns:</p>
<ol>
<li><p><strong>Adapter</strong>: Converts one interface into another that a client expects, letting incompatible interfaces work together.</p>
</li>
<li><p><strong>Bridge</strong>: Decouples an abstraction from its implementation so the two can vary independently.</p>
</li>
<li><p><strong>Composite</strong>: Composes objects into tree structures to represent part-whole hierarchies, letting clients treat individual objects and compositions uniformly.</p>
</li>
<li><p><strong>Decorator</strong>: Attaches additional responsibilities to an object dynamically, as a flexible alternative to subclassing.</p>
</li>
<li><p><strong>Facade</strong>: Provides a simplified, unified interface to a complex subsystem.</p>
</li>
<li><p><strong>Flyweight</strong>: Uses sharing to efficiently support a large number of fine-grained objects.</p>
</li>
<li><p><strong>Proxy</strong>: Provides a surrogate or placeholder for another object to control access to it.</p>
</li>
</ol>
<h3 id="heading-1-the-adapter-design-pattern">1. The Adapter Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a language translator at a business meeting. A British CEO needs to address a Japanese team. The CEO speaks only English. The team speaks only Japanese. A translator sits between them, converting every English sentence into Japanese and delivering it to the team. Both sides keep speaking their own language. Neither the CEO nor the team change anything about how they communicate. The translator makes them compatible.</p>
<p>That's the Adapter. The client speaks one interface, while the other side speaks a different one. The Adapter sits between them and makes both sides work together without either having to change.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>The CEO can't speak Japanese, and the team can't speak English. They're incompatible. The translator adapts one to the other without changing either side.</p>
</li>
<li><p>What if the CEO now needs to address a French team? A French translator is brought in. The CEO's process doesn't change. Only the translator changes.</p>
</li>
<li><p>What if an existing class has a useful method but the wrong interface? You wrap it in an adapter. The rest of the system talks to the adapter while the existing class stays untouched.</p>
</li>
</ul>
<p>In simple terms, you wrap an existing class with a new interface so the client can use it without any changes to either side.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"In software engineering, the adapter pattern is a software design pattern (also known as wrapper) that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code."</em> <a href="https://en.wikipedia.org/wiki/Adapter_pattern">(Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>The CEO is the client and the Japanese team member is the adaptee. They're useful, but they speak the wrong interface. The Translator is the adapter.</p>
<pre><code class="language-csharp">// What the CEO expects, someone who can receive a message in English
public interface IEnglishSpeaker
{
    void Speak(string message);
}
</code></pre>
<pre><code class="language-csharp">// The Japanese team member, speaks only Japanese (the adaptee)
public class JapaneseTeamMember
{
    public void SpeakJapanese(string message)
    {
        Console.WriteLine($"Team member (Japanese): {message}");
    }
}
</code></pre>
<pre><code class="language-csharp">// The Translator, adapts the Japanese speaker to the English interface
public class Translator : IEnglishSpeaker
{
    private readonly JapaneseTeamMember _teamMember;

    public Translator(JapaneseTeamMember teamMember)
    {
        _teamMember = teamMember;
    }

    public void Speak(string message)
    {
        string translated = TranslateToJapanese(message);
        _teamMember.SpeakJapanese(translated);
    }

    private string TranslateToJapanese(string english) =&gt; english switch
    {
        "Good morning, team."         =&gt; "おはようございます、チームの皆さん。",
        "Please review the proposal." =&gt; "提案書を確認してください。",
        _                             =&gt; $"[Japanese: {english}]"
    };
}
</code></pre>
<pre><code class="language-csharp">// The CEO, only knows how to talk to an IEnglishSpeaker
public class CEO
{
    private readonly IEnglishSpeaker _speaker;

    public CEO(IEnglishSpeaker speaker)
    {
        _speaker = speaker;
    }

    public void Address(string message)
    {
        Console.WriteLine($"CEO (English): {message}");
        _speaker.Speak(message);
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">JapaneseTeamMember teamMember = new JapaneseTeamMember();
IEnglishSpeaker translator    = new Translator(teamMember);
CEO ceo = new CEO(translator);

ceo.Address("Good morning, team.");
ceo.Address("Please review the proposal.");
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">CEO (English): Good morning, team.
Team member (Japanese): おはようございます、チームの皆さん。
CEO (English): Please review the proposal.
Team member (Japanese): 提案書を確認してください。
</code></pre>
<p>The CEO never knew about <code>JapaneseTeamMember</code>. The team never knew about the CEO's interface. The <code>Translator</code> made both sides work together without touching either. That's the Adapter.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Adapter when you want to use an existing class but its interface doesn't match what your code expects.</p>
<p>It's also helpful when you want to create a reusable class that cooperates with classes that don't have compatible interfaces.</p>
<p>And reach for it when you need to integrate a third-party library or legacy code without modifying it.</p>
<h3 id="heading-2-the-bridge-design-pattern">2. The Bridge Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a TV remote control and a television. The remote is one thing and the TV is another. You can have a basic remote or a smart remote. You can have a Sony TV or a Samsung TV. Any remote works with any TV you're not locked in. Buy a new Samsung TV, and your old remote still works. Buy a smart universal remote, and it works with every TV you own. Neither side needs to know the inner details of the other.</p>
<p>That's the Bridge. The abstraction (remote) and the implementation (TV) are two separate hierarchies that can grow and change completely independently of each other.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if every remote was hardwired to one specific TV brand? You would need a SonyBasicRemote, a SamsungBasicRemote, a SonySmartRemote, a SamsungSmartRemote...and so on. One class for every combination. Adding one new TV brand would double your remote classes. The Bridge stops this explosion.</p>
</li>
<li><p>What if you want to add a new remote type without touching the TVs? With Bridge, you just create a new remote class. The TVs are untouched.</p>
</li>
<li><p>What if you want to add a new TV brand without touching the remotes? Same answer. You add a new TV class. Every existing remote already works with it.</p>
</li>
</ul>
<p>In simple terms, you split a large class into two separate hierarchies (the abstraction and the implementation) so each can be changed and extended without affecting the other.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The bridge pattern is a design pattern used in software engineering that is meant to decouple an abstraction from its implementation so that the two can vary independently." (</em><a href="https://en.wikipedia.org/wiki/Bridge_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>The remote control is the abstraction and the TV brand is the implementation. They're connected through a bridge (the <code>ITV</code> interface), but neither hierarchy depends on the other's details.</p>
<pre><code class="language-csharp">// The implementation interface — what any TV must be able to do
public interface ITV
{
    void TurnOn();
    void TurnOff();
    void SetChannel(int channel);
    void SetVolume(int volume);
}
</code></pre>
<pre><code class="language-csharp">// Concrete implementations — each brand handles things its own way
public class SonyTV : ITV
{
    public void TurnOn()           =&gt; Console.WriteLine("Sony TV: Powering on. BRAVIA display ready.");
    public void TurnOff()          =&gt; Console.WriteLine("Sony TV: Shutting down.");
    public void SetChannel(int ch) =&gt; Console.WriteLine($"Sony TV: Switching to channel {ch}.");
    public void SetVolume(int vol) =&gt; Console.WriteLine($"Sony TV: Volume set to {vol}.");
}

public class SamsungTV : ITV
{
    public void TurnOn()           =&gt; Console.WriteLine("Samsung TV: Turning on. Smart Hub loading.");
    public void TurnOff()          =&gt; Console.WriteLine("Samsung TV: Powering off.");
    public void SetChannel(int ch) =&gt; Console.WriteLine($"Samsung TV: Channel {ch} selected.");
    public void SetVolume(int vol) =&gt; Console.WriteLine($"Samsung TV: Volume at {vol}.");
}
</code></pre>
<pre><code class="language-csharp">// The abstraction — the remote holds a reference to whichever TV it controls
public abstract class RemoteControl
{
    protected ITV _tv;

    protected RemoteControl(ITV tv) { _tv = tv; }

    public abstract void TurnOn();
    public abstract void TurnOff();
    public abstract void SetChannel(int channel);
}
</code></pre>
<pre><code class="language-csharp">// Refined abstraction — a basic remote, does exactly what the TV does
public class BasicRemote : RemoteControl
{
    public BasicRemote(ITV tv) : base(tv) { }

    public override void TurnOn()           =&gt; _tv.TurnOn();
    public override void TurnOff()          =&gt; _tv.TurnOff();
    public override void SetChannel(int ch) =&gt; _tv.SetChannel(ch);
}

// Refined abstraction — a smart remote, adds its own behaviour on top
public class SmartRemote : RemoteControl
{
    public SmartRemote(ITV tv) : base(tv) { }

    public override void TurnOn()
    {
        Console.WriteLine("Smart Remote: Activating voice control.");
        _tv.TurnOn();
    }

    public override void TurnOff()
    {
        Console.WriteLine("Smart Remote: Saving watch history.");
        _tv.TurnOff();
    }

    public override void SetChannel(int ch)
    {
        Console.WriteLine("Smart Remote: Looking up channel guide.");
        _tv.SetChannel(ch);
    }

    public void SetVolume(int vol) =&gt; _tv.SetVolume(vol);
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">// Basic remote paired with a Sony TV
Console.WriteLine("--- Basic Remote + Sony TV ---");
RemoteControl basicSony = new BasicRemote(new SonyTV());
basicSony.TurnOn();
basicSony.SetChannel(5);
basicSony.TurnOff();

// Smart remote paired with a Samsung TV
Console.WriteLine("\n--- Smart Remote + Samsung TV ---");
SmartRemote smartSamsung = new SmartRemote(new SamsungTV());
smartSamsung.TurnOn();
smartSamsung.SetChannel(10);
smartSamsung.SetVolume(20);
smartSamsung.TurnOff();

// Swap freely — smart remote now with Sony, no code changes needed
Console.WriteLine("\n--- Smart Remote + Sony TV ---");
SmartRemote smartSony = new SmartRemote(new SonyTV());
smartSony.TurnOn();
smartSony.SetChannel(3);
smartSony.TurnOff();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">--- Basic Remote + Sony TV ---
Sony TV: Powering on. BRAVIA display ready.
Sony TV: Switching to channel 5.
Sony TV: Shutting down.

--- Smart Remote + Samsung TV ---
Smart Remote: Activating voice control.
Samsung TV: Turning on. Smart Hub loading.
Smart Remote: Looking up channel guide.
Samsung TV: Channel 10 selected.
Samsung TV: Volume at 20.
Smart Remote: Saving watch history.
Samsung TV: Powering off.

--- Smart Remote + Sony TV ---
Smart Remote: Activating voice control.
Sony TV: Powering on. BRAVIA display ready.
Smart Remote: Looking up channel guide.
Sony TV: Switching to channel 3.
Smart Remote: Saving watch history.
Sony TV: Shutting down.
</code></pre>
<p>The same <code>SmartRemote</code> worked with both Sony and Samsung without any changes. Adding a new TV brand like LG means creating one new class, and every existing remote works with it immediately. That's the Bridge.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use the Bridge pattern when you want to avoid a permanent binding between an abstraction and its implementation, so either can be swapped at runtime.</p>
<p>You can also use it when both the abstraction and the implementation should be independently extensible through subclassing.</p>
<p>And it's a good fit when changes to the implementation should have no impact on the client code. The client shouldn't need to be recompiled.</p>
<h3 id="heading-3-the-composite-design-pattern">3. The Composite Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a company organisation chart. A company has a CEO. Under the CEO are department heads, each leading a department full of employees. Under some departments are even smaller sub-teams.</p>
<p>Now imagine you want to know the total salary cost. You can ask a single employee they tell you their salary. You can ask a whole department, which adds up every person inside it, including nested teams. Or you can ask the entire company: it rolls up every salary across every level.</p>
<p>The same question, asked the same way, whether you're talking to one person or thousands.</p>
<p>That's the Composite pattern. Individual items and groups of items share the same interface. The caller never needs to know which one they're dealing with.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if you had to write different code to handle a single employee versus a whole department? You would end up with <code>if</code> checks everywhere just to figure out what you're talking to. Composite removes that entirely: one interface, always.</p>
</li>
<li><p>What if departments can contain other departments? Composite handles any depth of nesting naturally. The caller just asks the top of the tree and the operation flows down automatically.</p>
</li>
<li><p>What if you want to add a new type of team or role? You implement the same interface. Everything above it in the tree keeps working without any changes.</p>
</li>
</ul>
<p>In simple terms, you compose objects into tree structures. This lets individual objects and groups of objects be treated through the same interface, so the caller never has to care about the difference.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object. The intent of a composite is to compose objects into tree structures to represent part-whole hierarchies."</em> <a href="https://en.wikipedia.org/wiki/Composite_pattern">(Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>Every node in the tree (whether a single employee or an entire department) implements <code>IEmployee</code>. The caller treats them identically.</p>
<pre><code class="language-csharp">// The component interface — every leaf and composite shares this contract
public interface IEmployee
{
    string Name    { get; }
    int    GetSalary();
    void   GetDetails(string indent = "");
}
</code></pre>
<pre><code class="language-csharp">// The leaf — a single employee with no reports
public class Employee : IEmployee
{
    private readonly int _salary;

    public string Name { get; }

    public Employee(string name, int salary)
    {
        Name    = name;
        _salary = salary;
    }

    public int  GetSalary()                    =&gt; _salary;
    public void GetDetails(string indent = "") =&gt; Console.WriteLine($"{indent}- {Name} (£{_salary:N0})");
}
</code></pre>
<pre><code class="language-csharp">// The composite — a department that holds employees or other departments
public class Department : IEmployee
{
    private readonly List&lt;IEmployee&gt; _members = new();

    public string Name { get; }

    public Department(string name) { Name = name; }

    public void Add(IEmployee employee)    =&gt; _members.Add(employee);
    public void Remove(IEmployee employee) =&gt; _members.Remove(employee);

    public int GetSalary() =&gt; _members.Sum(m =&gt; m.GetSalary());

    public void GetDetails(string indent = "")
    {
        Console.WriteLine($"{indent}[{Name}] Total: £{GetSalary():N0}");
        foreach (var member in _members)
            member.GetDetails(indent + "  ");
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">// Individual employees
var ceo        = new Employee("Alice (CEO)",        120_000);
var cto        = new Employee("Bob (CTO)",           95_000);
var dev1       = new Employee("Carol (Developer)",   65_000);
var dev2       = new Employee("David (Developer)",   62_000);
var cfo        = new Employee("Eve (CFO)",           90_000);
var accountant = new Employee("Frank (Accountant)",  55_000);

// Build the Engineering department
var engineering = new Department("Engineering");
engineering.Add(cto);
engineering.Add(dev1);
engineering.Add(dev2);

// Build the Finance department
var finance = new Department("Finance");
finance.Add(cfo);
finance.Add(accountant);

// Build the whole company
var company = new Department("Acme Corp");
company.Add(ceo);
company.Add(engineering);
company.Add(finance);

// Ask the whole company — one call, rolls up everything
Console.WriteLine("=== Full Company ===");
company.GetDetails();

// Ask just one department — same call, same interface
Console.WriteLine("\n=== Engineering Only ===");
engineering.GetDetails();

// Ask a single employee — same call, same interface
Console.WriteLine("\n=== Single Employee ===");
dev1.GetDetails();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">=== Full Company ===
[Acme Corp] Total: £487,000
  - Alice (CEO) (£120,000)
  [Engineering] Total: £222,000
    - Bob (CTO) (£95,000)
    - Carol (Developer) (£65,000)
    - David (Developer) (£62,000)
  [Finance] Total: £145,000
    - Eve (CFO) (£90,000)
    - Frank (Accountant) (£55,000)

=== Engineering Only ===
[Engineering] Total: £222,000
  - Bob (CTO) (£95,000)
  - Carol (Developer) (£65,000)
  - David (Developer) (£62,000)

=== Single Employee ===
- Carol (Developer) (£65,000)
</code></pre>
<p><code>company.GetDetails()</code>, <code>engineering.GetDetails()</code>, and <code>dev1.GetDetails()</code>: the same call on three different levels of the tree. The caller never checked what it was talking to. That's the Composite pattern.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Composite when you need to represent part-whole hierarchies, like trees where individual items and groups of items need to be used interchangeably.</p>
<p>It also works well when you want client code to treat single objects and collections of objects uniformly, without any special-casing.</p>
<p>And it's a good fit when the structure can be nested to any depth and that depth shouldn't affect how the caller interacts with it.</p>
<h3 id="heading-4-the-decorator-design-pattern">4. The Decorator Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of ordering a coffee at a cafe. You start with a plain espresso. Then you ask for milk. Then vanilla syrup. Then whipped cream on top. Each addition wraps around or adds to what was already there, adding its own cost and its own description. The espresso at the centre never changes. You're just layering on top of it, one addition at a time. You could add two shots of syrup. You could skip the milk entirely. Every combination is possible without creating a new type of coffee for each one.</p>
<p>That's the Decorator pattern. You start with a base object and wrap it in layers. Each layer adds its own behaviour and then delegates to whatever is underneath it.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if you needed a class for every combination? EspressoWithMilk, EspressoWithMilkAndVanilla, EspressoWithMilkAndVanillaAndCream...the list explodes. Decorator adds behaviour at runtime, so you never need those classes.</p>
</li>
<li><p>What if the base coffee shouldn't change? It does not. The espresso class stays untouched. The decorators wrap around it and extend it independently.</p>
</li>
<li><p>What if a new topping needs to be added? You create one new decorator class. Every existing combination still works exactly as before.</p>
</li>
</ul>
<p>In simple terms, you wrap an object in one or more layers, where each layer adds its own behaviour before or after delegating to the layer beneath it.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The decorator pattern is a design pattern that allows behaviour to be added to an individual object, dynamically, without affecting the behaviour of other instances of the same class."</em> <a href="https://en.wikipedia.org/wiki/Decorator_pattern">(Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>The coffee is the component. Each topping is a decorator. Every decorator wraps the component and adds to its description and cost.</p>
<pre><code class="language-csharp">// The component interface — every coffee, plain or decorated, shares this
public interface ICoffee
{
    string GetDescription();
    double GetCost();
}
</code></pre>
<pre><code class="language-csharp">// The base component — a plain espresso
public class Espresso : ICoffee
{
    public string GetDescription() =&gt; "Espresso";
    public double GetCost()        =&gt; 1.00;
}
</code></pre>
<pre><code class="language-csharp">// The base decorator — wraps any ICoffee and delegates to it
public abstract class CoffeeDecorator : ICoffee
{
    protected readonly ICoffee _coffee;

    protected CoffeeDecorator(ICoffee coffee) { _coffee = coffee; }

    public virtual string GetDescription() =&gt; _coffee.GetDescription();
    public virtual double GetCost()        =&gt; _coffee.GetCost();
}
</code></pre>
<pre><code class="language-csharp">// Concrete decorators — each one adds its own layer
public class Milk : CoffeeDecorator
{
    public Milk(ICoffee coffee) : base(coffee) { }

    public override string GetDescription() =&gt; _coffee.GetDescription() + ", Milk";
    public override double GetCost()        =&gt; _coffee.GetCost() + 0.30;
}

public class VanillaSyrup : CoffeeDecorator
{
    public VanillaSyrup(ICoffee coffee) : base(coffee) { }

    public override string GetDescription() =&gt; _coffee.GetDescription() + ", Vanilla Syrup";
    public override double GetCost()        =&gt; _coffee.GetCost() + 0.50;
}

public class WhippedCream : CoffeeDecorator
{
    public WhippedCream(ICoffee coffee) : base(coffee) { }

    public override string GetDescription() =&gt; _coffee.GetDescription() + ", Whipped Cream";
    public override double GetCost()        =&gt; _coffee.GetCost() + 0.75;
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">// A plain espresso
ICoffee order = new Espresso();
Console.WriteLine($"{order.GetDescription()} =&gt; £{order.GetCost():F2}");

// Wrap it with milk
order = new Milk(order);
Console.WriteLine($"{order.GetDescription()} =&gt; £{order.GetCost():F2}");

// Wrap it with vanilla syrup on top
order = new VanillaSyrup(order);
Console.WriteLine($"{order.GetDescription()} =&gt; £{order.GetCost():F2}");

// Wrap it with whipped cream on top of that
order = new WhippedCream(order);
Console.WriteLine($"{order.GetDescription()} =&gt; £{order.GetCost():F2}");
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Espresso =&gt; £1.00
Espresso, Milk =&gt; £1.30
Espresso, Milk, Vanilla Syrup =&gt; £1.80
Espresso, Milk, Vanilla Syrup, Whipped Cream =&gt; £2.55
</code></pre>
<p>Each line is a new layer wrapped around the previous one. The espresso never changed. The cost and description grew with every wrapper. That's the Decorator pattern.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use the Decorator pattern when you want to add responsibilities to individual objects without affecting other objects of the same class.</p>
<p>It's also a good choice when subclassing would lead to an explosion of classes to cover every possible combination of behaviours.</p>
<p>And use it when you need to be able to stack behaviours in any order at runtime, independently of each other.</p>
<h3 id="heading-5-the-facade-design-pattern">5. The Facade Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of clicking "Place Order" on a shopping website. In that single click, several things happen behind the scenes: the system checks whether the item is in stock, your payment is charged, a shipping label is generated, and a confirmation email is sent to you. You don't see any of that. You click one button and get one result. The complexity of four separate systems is hidden behind a single, clean action.</p>
<p>That's the Facade pattern: one simple interface in front of many complex moving parts. The caller doesn't need to know what's happening behind the scenes.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if the client had to call each subsystem directly? Check inventory, then process payment, then generate a label, and then send an email, all in the right order, handling each failure separately. The Facade wraps all of that into one call.</p>
</li>
<li><p>What if one of the subsystems changes? The Facade absorbs the change. The client code never needs to know. Only the Facade is updated.</p>
</li>
<li><p>What if different clients need the same flow? They all call the same Facade method. The logic is in one place, not duplicated across every caller.</p>
</li>
</ul>
<p>In simple terms, you provide a single, simple interface that hides the complexity of a set of subsystems behind it.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The facade pattern (also spelled façade) is a software-design pattern commonly used in object-oriented programming. Analogous to a facade in architecture, a facade is an object that serves as a front-facing interface masking more complex underlying or structural code."</em><a href="https://en.wikipedia.org/wiki/Facade_pattern">(Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>Each subsystem does its own job. The <code>OrderFacade</code> is the single entry point that coordinates all of them. The client only ever talks to the facade.</p>
<pre><code class="language-csharp">// Subsystem 1: checks whether the item is available
public class InventoryService
{
    public bool CheckStock(string item)
    {
        Console.WriteLine($"Inventory: Checking stock for {item}.");
        return true;
    }
}
</code></pre>
<pre><code class="language-csharp">// Subsystem 2: handles the payment
public class PaymentService
{
    public bool ProcessPayment(string cardNumber, double amount)
    {
        Console.WriteLine($"Payment: Charging £{amount:F2} to card ending {cardNumber[^4..]}.");
        return true;
    }
}
</code></pre>
<pre><code class="language-csharp">// Subsystem 3: generates a shipping label
public class ShippingService
{
    public string GenerateLabel(string item, string address)
    {
        Console.WriteLine($"Shipping: Generating label for {item} to {address}.");
        return "TRACK-29384";
    }
}
</code></pre>
<pre><code class="language-csharp">// Subsystem 4: sends the confirmation email
public class EmailService
{
    public void SendConfirmation(string email, string trackingCode)
    {
        Console.WriteLine($"Email: Confirmation sent to {email}. Tracking code: {trackingCode}.");
    }
}
</code></pre>
<pre><code class="language-csharp">// The Facade — one method, hides all four subsystems
public class OrderFacade
{
    private readonly InventoryService _inventory = new();
    private readonly PaymentService   _payment   = new();
    private readonly ShippingService  _shipping  = new();
    private readonly EmailService     _email     = new();

    public void PlaceOrder(string item, string cardNumber, double amount, string address, string email)
    {
        Console.WriteLine("=== Placing Order ===");

        if (!_inventory.CheckStock(item))
        {
            Console.WriteLine("Order failed: item out of stock.");
            return;
        }

        if (!_payment.ProcessPayment(cardNumber, amount))
        {
            Console.WriteLine("Order failed: payment declined.");
            return;
        }

        string trackingCode = _shipping.GenerateLabel(item, address);
        _email.SendConfirmation(email, trackingCode);

        Console.WriteLine($"\nOrder complete. Your tracking code is {trackingCode}.");
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">OrderFacade store = new OrderFacade();

store.PlaceOrder(
    item:       "Wireless Headphones",
    cardNumber: "4111111111111234",
    amount:     79.99,
    address:    "42 Maple Street, London",
    email:      "customer@email.com"
);
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">=== Placing Order ===
Inventory: Checking stock for Wireless Headphones.
Payment: Charging £79.99 to card ending 1234.
Shipping: Generating label for Wireless Headphones to 42 Maple Street, London.
Email: Confirmation sent to customer@email.com. Tracking code: TRACK-29384.

Order complete. Your tracking code is TRACK-29384.
</code></pre>
<blockquote>
<p>The client called one method. Four subsystems ran in the right order. None of that complexity was visible to the caller. That is the Facade.</p>
</blockquote>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Facade when you want to provide a simple interface to a complex subsystem so callers aren't burdened by its internals.</p>
<p>It also works well when you want to layer your system so that high-level code talks to facades, not directly to low-level subsystems.</p>
<p>And it's a good choice when you want a single entry point that coordinates a sequence of steps across multiple services.</p>
<h3 id="heading-6-the-flyweight-design-pattern">6. The Flyweight Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a game that renders a forest. The forest has ten thousand trees. Each tree has a type name, a colour, and a texture. But most of those trees are Oaks, and all Oaks look exactly the same.</p>
<p>Creating ten thousand separate objects, each storing the same name, colour, and texture, wastes enormous amounts of memory. Instead, you create one shared Oak object that holds all that data. Every Oak tree in the forest points to that same shared object and only stores its own position on the map.</p>
<p>That's the Flyweight pattern. The data that's the same across many instances is shared. The data that's unique per instance is stored separately and passed in only when needed.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if you created a full object for every single tree? With ten thousand trees, you store the same name, colour, and texture ten thousand times. Flyweight stores that shared data once and reuses it everywhere.</p>
</li>
<li><p>What if a new tree type is introduced? The factory creates one new shared object for it. Every tree of that type immediately uses it without any extra memory.</p>
</li>
<li><p>What if the forest needs to render each tree at its own position? The position is unique per tree, so it's stored on the tree itself and passed to the shared object only at render time. The shared object never holds it.</p>
</li>
</ul>
<p>In simple terms, you split an object's data into what's shared across many instances and what's unique per instance. Share the common part. Pass the unique part in only when needed.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"A flyweight is an object that minimizes memory usage by sharing as much data as possible with other similar objects. It is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory."</em> <a href="https://en.wikipedia.org/wiki/Flyweight_pattern">(Source</a>)</p>
</blockquote>
<h4 id="heading-programming-examplel">Programming ExampleL</h4>
<p><code>TreeType</code> is the flyweight: it holds shared data. <code>Tree</code> holds only the unique position and a reference to a shared <code>TreeType</code>. The factory ensures each <code>TreeType</code> is created only once.</p>
<pre><code class="language-csharp">// The flyweight — holds shared intrinsic state (same for all trees of this type)
public class TreeType
{
    public string Name    { get; }
    public string Colour  { get; }
    public string Texture { get; }

    public TreeType(string name, string colour, string texture)
    {
        Name    = name;
        Colour  = colour;
        Texture = texture;
    }

    public void Render(int x, int y)
    {
        Console.WriteLine($"Rendering {Name} tree ({Colour}, {Texture}) at ({x}, {y})");
    }
}
</code></pre>
<pre><code class="language-csharp">// The flyweight factory — creates and caches tree types so they are never duplicated
public class TreeTypeFactory
{
    private readonly Dictionary&lt;string, TreeType&gt; _cache = new();

    public TreeType GetTreeType(string name, string colour, string texture)
    {
        string key = $"{name}_{colour}_{texture}";

        if (!_cache.ContainsKey(key))
        {
            Console.WriteLine($"Factory: Creating new TreeType for '{name}'.");
            _cache[key] = new TreeType(name, colour, texture);
        }

        return _cache[key];
    }

    public int TotalTypes =&gt; _cache.Count;
}
</code></pre>
<pre><code class="language-csharp">// The context — holds unique extrinsic state (position) and a reference to a shared flyweight
public class Tree
{
    private readonly int      _x;
    private readonly int      _y;
    private readonly TreeType _type;

    public Tree(int x, int y, TreeType type)
    {
        _x    = x;
        _y    = y;
        _type = type;
    }

    public void Render() =&gt; _type.Render(_x, _y);
}
</code></pre>
<pre><code class="language-csharp">// The forest — plants trees using shared flyweights
public class Forest
{
    private readonly List&lt;Tree&gt;      _trees   = new();
    private readonly TreeTypeFactory _factory = new();

    public void PlantTree(int x, int y, string name, string colour, string texture)
    {
        TreeType type = _factory.GetTreeType(name, colour, texture);
        _trees.Add(new Tree(x, y, type));
    }

    public void Render()
    {
        foreach (var tree in _trees)
            tree.Render();
    }

    public int TreeCount     =&gt; _trees.Count;
    public int TreeTypeCount =&gt; _factory.TotalTypes;
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">Forest forest = new Forest();

// Plant 6 trees — but only 2 unique types
forest.PlantTree(1,  5,  "Oak",  "Dark Green",  "Rough bark");
forest.PlantTree(3,  12, "Oak",  "Dark Green",  "Rough bark");
forest.PlantTree(7,  2,  "Oak",  "Dark Green",  "Rough bark");
forest.PlantTree(10, 8,  "Pine", "Light Green", "Smooth bark");
forest.PlantTree(15, 3,  "Pine", "Light Green", "Smooth bark");
forest.PlantTree(20, 14, "Pine", "Light Green", "Smooth bark");

forest.Render();

Console.WriteLine($"\nTrees planted:              {forest.TreeCount}");
Console.WriteLine($"Unique tree types in memory: {forest.TreeTypeCount}");
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-plaintext">Factory: Creating new TreeType for 'Oak'.
Factory: Creating new TreeType for 'Pine'.
Rendering Oak tree (Dark Green, Rough bark) at (1, 5)
Rendering Oak tree (Dark Green, Rough bark) at (3, 12)
Rendering Oak tree (Dark Green, Rough bark) at (7, 2)
Rendering Pine tree (Light Green, Smooth bark) at (10, 8)
Rendering Pine tree (Light Green, Smooth bark) at (15, 3)
Rendering Pine tree (Light Green, Smooth bark) at (20, 14)

Trees planted:              6
Unique tree types in memory: 2
</code></pre>
<p>Six trees, but only two <code>TreeType</code> objects were ever created. Scale that to ten thousand trees and the factory still creates exactly two. The positions are unique per tree, and the appearance is shared. That's the Flyweight.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Flyweight when your application needs to create a very large number of similar objects that would otherwise consume too much memory.</p>
<p>It's also useful when most of the object's state can be made shared across instances, with only a small part being unique per instance.</p>
<p>And it's a good choice when the unique part of the state can be passed in externally rather than stored inside every object.</p>
<h3 id="heading-7-the-proxy-design-pattern">7. The Proxy Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a security guard at the entrance of an office building. You can't walk straight into the building. You have to go through the guard first. The guard checks your name against the authorised list, logs your visit, and only then lets you through. If you're not on the list, you're turned away. The building itself never deals with any of that. It just lets people in. All the checking, logging, and decision-making happens at the guard (the proxy) before the building ever gets involved.</p>
<p>That's the Proxy pattern. It sits between the caller and the real object, controls what gets through, and can add behaviour like access checks or logging without the real object knowing anything about it.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if anyone could walk straight into the building? There would be no access control. The proxy intercepts every request and decides whether it should be allowed through.</p>
</li>
<li><p>What if you need to log every entry without changing the building? The proxy handles it. The real building stays simple and focused on its own job.</p>
</li>
<li><p>What if the real object is expensive to create and you want to delay that? The proxy can hold off creating it until someone actually passes the check and needs it.</p>
</li>
</ul>
<p>In simple terms, you place an object in front of another object to control access to it. The caller thinks it's talking directly to the real object, but the proxy is handling it first.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate." (</em><a href="https://en.wikipedia.org/wiki/Proxy_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>The client talks to <code>IBuilding</code>. The <code>SecurityGuard</code> is the proxy: it implements the same interface, controls access, and only lets authorised visitors through to the <code>OfficeBuilding</code>.</p>
<pre><code class="language-csharp">// The subject interface — the building and the proxy both implement this
public interface IBuilding
{
    void Enter(string visitorName);
}
</code></pre>
<pre><code class="language-csharp">// The real subject — the actual building, just grants entry
public class OfficeBuilding : IBuilding
{
    public void Enter(string visitorName)
    {
        Console.WriteLine($"Building: {visitorName} has entered.");
    }
}
</code></pre>
<pre><code class="language-csharp">// The proxy — the security guard controls who gets through
public class SecurityGuard : IBuilding
{
    private readonly OfficeBuilding _building          = new();
    private readonly List&lt;string&gt;   _authorisedVisitors = new() { "Alice", "Bob", "Carol" };

    public void Enter(string visitorName)
    {
        Console.WriteLine($"Guard: {visitorName} is requesting entry.");

        if (_authorisedVisitors.Contains(visitorName))
        {
            Console.WriteLine("Guard: ID verified. Access granted.");
            _building.Enter(visitorName);
        }
        else
        {
            Console.WriteLine($"Guard: {visitorName} is not on the list. Access denied.");
        }
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">IBuilding entrance = new SecurityGuard();

entrance.Enter("Alice");
Console.WriteLine();
entrance.Enter("David");
Console.WriteLine();
entrance.Enter("Bob");
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Guard: Alice is requesting entry.
Guard: ID verified. Access granted.
Building: Alice has entered.

Guard: David is requesting entry.
Guard: David is not on the list. Access denied.

Guard: Bob is requesting entry.
Guard: ID verified. Access granted.
Building: Bob has entered.
</code></pre>
<p>The client called <code>Enter()</code> on what it thought was the building. It was actually the security guard. The guard decided what happened. The building only ever saw the people who were allowed through. That's the Proxy.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Proxy when you need access control, like only letting certain callers through to the real object.</p>
<p>It's a good fit when you want to add behaviour such as logging, caching, or validation without changing the real object.</p>
<p>And you can use it when the real object is expensive to create and you want to delay or guard that creation until it's truly needed.</p>
<h2 id="heading-behavioral-design-patterns">Behavioral Design Patterns</h2>
<p>Simply put, behavioral patterns are all about <strong>how objects communicate and share responsibility</strong>. They focus on the assignment of responsibilities between objects, and how objects cooperate to get a job done.</p>
<p>Wikipedia describes them as:</p>
<blockquote>
<p><em>"In software engineering, behavioral design patterns are design patterns that identify common communication patterns among objects. By doing so, these patterns increase flexibility in carrying out this communication." (</em><a href="https://en.wikipedia.org/wiki/Behavioral_pattern">Source</a>)</p>
</blockquote>
<p>Behavioral design patterns describe how objects interact and distribute responsibility, not just how they're structured. They focus on communication between objects: who talks to whom, and how much each side knows about the other.</p>
<p>There are 11 behavioral design patterns:</p>
<ol>
<li><p><strong>Chain of Responsibility</strong>: Passes a request along a chain of handlers, letting each one decide to handle it or pass it on.</p>
</li>
<li><p><strong>Command</strong>: Encapsulates a request as an object, letting you parameterize clients, queue actions, and support undo.</p>
</li>
<li><p><strong>Interpreter</strong>: Given a language, defines a representation for its grammar along with an interpreter that evaluates sentences in it.</p>
</li>
<li><p><strong>Iterator</strong>: Provides a way to access the elements of a collection sequentially without exposing how it's built underneath.</p>
</li>
<li><p><strong>Mediator</strong>: Defines an object that encapsulates how a set of objects interact, so they don't refer to each other directly.</p>
</li>
<li><p><strong>Memento</strong>: Captures an object's internal state so it can be restored later, without breaking encapsulation.</p>
</li>
<li><p><strong>Observer</strong>: Defines a one-to-many dependency so that when one object changes state, everything depending on it is notified automatically.</p>
</li>
<li><p><strong>State</strong>: Lets an object change its behaviour when its internal state changes, as if it had changed its class.</p>
</li>
<li><p><strong>Strategy</strong>: Defines a family of interchangeable algorithms and lets the client pick which one to use at runtime.</p>
</li>
<li><p><strong>Template Method</strong>: Defines the skeleton of an algorithm in a method, leaving some steps for subclasses to fill in.</p>
</li>
<li><p><strong>Visitor</strong>: Lets you define a new operation without changing the classes of the elements it operates on.</p>
</li>
</ol>
<h3 id="heading-1-the-chain-of-responsibility-design-pattern">1. The Chain of Responsibility Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of an expense approval process at a company. An employee submits a request to their director. If the amount is small enough, the director approves it and that's the end of it. If it's too large for the director to sign off on, it goes up to the vice president. If it's still too large, it goes up to the chief executive.</p>
<p>Each person in the chain only needs to know two things: what they're allowed to approve, and who to hand it to if they can't. The employee never needs to know who ends up approving it.</p>
<p>That's the Chain of Responsibility design pattern. A request travels along a chain of handlers until one of them deals with it, and each handler only cares about its own link in that chain.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if the sender had to know exactly who should handle the request? That would tie the sender to a specific handler and break the moment the approval structure changed. The chain lets the sender submit the request without knowing who will end up handling it.</p>
</li>
<li><p>What if one handler could only ever approve or reject, with no fallback? Requests that fell outside its authority would simply fail. The chain lets a handler pass what it can't deal with further along.</p>
</li>
<li><p>What if you needed to change the approval structure? Rewiring which handler comes after which is enough. Neither the sender nor the other handlers need to change.</p>
</li>
</ul>
<p>In simple terms, you pass a request along a chain of handlers. Each handler decides whether to deal with it or hand it off to the next one in line.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"In object-oriented design, the chain-of-responsibility pattern is a behavioral design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain."</em></p>
<p><strong>(</strong><a href="https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>Each <code>Approver</code> knows its own approval limit and holds a reference to the next approver in the chain. The <code>ExpenseRequest</code> is passed along until someone can approve it, or nobody can.</p>
<pre><code class="language-csharp">// The request that travels along the chain
public class ExpenseRequest
{
    public string Description { get; }
    public decimal Amount     { get; }

    public ExpenseRequest(string description, decimal amount)
    {
        Description = description;
        Amount      = amount;
    }
}
</code></pre>
<pre><code class="language-csharp">// The handler, every link in the chain implements this
public abstract class Approver
{
    private Approver? _next;

    public void SetNext(Approver next) =&gt; _next = next;

    public void Approve(ExpenseRequest request)
    {
        if (CanApprove(request))
        {
            Console.WriteLine($"{GetType().Name}: Approved '{request.Description}' (${request.Amount}).");
        }
        else if (_next is not null)
        {
            Console.WriteLine($"{GetType().Name}: Can't approve '{request.Description}' (${request.Amount}). Passing it up.");
            _next.Approve(request);
        }
        else
        {
            Console.WriteLine($"{GetType().Name}: No one left to approve '{request.Description}' (${request.Amount}). Request denied.");
        }
    }

    protected abstract bool CanApprove(ExpenseRequest request);
}
</code></pre>
<pre><code class="language-csharp">// Concrete handlers, each with its own approval limit
public class Director : Approver
{
    protected override bool CanApprove(ExpenseRequest request) =&gt; request.Amount &lt;= 1000;
}

public class VicePresident : Approver
{
    protected override bool CanApprove(ExpenseRequest request) =&gt; request.Amount &lt;= 20000;
}

public class Chief : Approver
{
    protected override bool CanApprove(ExpenseRequest request) =&gt; request.Amount &lt;= 50000;
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">Approver directorApprover = new Director();
Approver vpApprover       = new VicePresident();
Approver ceoApprover      = new Chief();

directorApprover.SetNext(vpApprover);
vpApprover.SetNext(ceoApprover);

directorApprover.Approve(new ExpenseRequest("Laptop", 800));
Console.WriteLine();
directorApprover.Approve(new ExpenseRequest("Team offsite", 12000));
Console.WriteLine();
directorApprover.Approve(new ExpenseRequest("New office lease", 90000));
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Director: Approved 'Laptop' ($800).

Director: Can't approve 'Team offsite' ($12000). Passing it up.
VicePresident: Approved 'Team offsite' ($12000).

Director: Can't approve 'New office lease' ($90000). Passing it up.
VicePresident: Can't approve 'New office lease' ($90000). Passing it up.
Chief: No one left to approve 'New office lease' ($90000). Request denied.
</code></pre>
<p>The employee only ever talked to the director. Whether the director, the vice president, or the chief ended up approving it, was decided by the chain itself, not the employee.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Chain of Responsibility when more than one object might handle a request, and the handler isn't known in advance.</p>
<p>It's also a good choice when you want to issue a request without specifying the receiver explicitly.</p>
<p>And it's helpful when the set of handlers, and their order, should be configurable rather than hard-coded.</p>
<h3 id="heading-2-the-command-design-pattern">2. The Command Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a universal remote control. Every button is programmed to do one specific thing: turn a light on, turn a light off, and so on. When you press a button, the remote doesn't know or care how the light actually works internally. It just triggers the action that button was set up to perform. And because each button's action is a self-contained thing, the remote can also press it in reverse, undoing what it just did.</p>
<p>That's the Command pattern. A request "turn the light on" is wrapped up as its own object. The thing that triggers it doesn't need to know anything about how it's carried out.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if the button had to know exactly how the light worked? Every button would need to be rewritten if the light's internals changed. Wrapping the action as a command means the remote never touches those details.</p>
</li>
<li><p>What if you wanted to undo the last action? Without a command object there's nothing to reverse, only a completed side effect. Wrapping the action gives you something you can also unwind.</p>
</li>
<li><p>What if you wanted to queue actions, log them, or trigger them later? A plain method call happens immediately and leaves nothing behind. A command is an object, so it can be stored, queued, and replayed.</p>
</li>
</ul>
<p>In simple terms, you wrap a request up as an object, so the thing that triggers it doesn't need to know how it's carried out, and the action itself can be queued, logged, or undone.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time." (</em><a href="https://en.wikipedia.org/wiki/Command_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p>The <code>RemoteControl</code> is the invoker it only knows about <code>ICommand</code>. <code>LightOnCommand</code> and <code>LightOffCommand</code> are the concrete commands, each wrapping the <code>Light</code> receiver and the action to perform on it.</p>
<pre><code class="language-csharp">// The command interface, every action implements this
public interface ICommand
{
    void Execute();
    void Undo();
}
</code></pre>
<pre><code class="language-csharp">// The receiver, the object that actually does the work
public class Light
{
    private readonly string _room;

    public Light(string room) =&gt; _room = room;

    public void On()  =&gt; Console.WriteLine($"{_room} light: turned on.");
    public void Off() =&gt; Console.WriteLine($"{_room} light: turned off.");
}
</code></pre>
<pre><code class="language-csharp">// Concrete commands, each wraps a receiver and an action
public class LightOnCommand : ICommand
{
    private readonly Light _light;

    public LightOnCommand(Light light) =&gt; _light = light;

    public void Execute() =&gt; _light.On();
    public void Undo()    =&gt; _light.Off();
}

public class LightOffCommand : ICommand
{
    private readonly Light _light;

    public LightOffCommand(Light light) =&gt; _light = light;

    public void Execute() =&gt; _light.Off();
    public void Undo()    =&gt; _light.On();
}
</code></pre>
<pre><code class="language-csharp">// The invoker, it holds a command and triggers it without knowing what it does
public class RemoteControl
{
    private ICommand? _command;

    public void SetCommand(ICommand command) =&gt; _command = command;

    public void PressButton() =&gt; _command?.Execute();
    public void PressUndo()   =&gt; _command?.Undo();
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">var livingRoomLight = new Light("Living Room");
var remote          = new RemoteControl();

remote.SetCommand(new LightOnCommand(livingRoomLight));
remote.PressButton();

remote.SetCommand(new LightOffCommand(livingRoomLight));
remote.PressButton();

Console.WriteLine();
Console.WriteLine("Undoing last action...");
remote.PressUndo();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Living Room light: turned on.
Living Room light: turned off.

Undoing last action...
Living Room light: turned on.
</code></pre>
<p>The remote never called <code>_light.On()</code> or <code>_light.Off()</code> directly. It called <code>Execute()</code> and <code>Undo()</code> on whatever command it was holding. That's the Command pattern: the request itself became an object.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use the Command pattern when you want to parameterize objects with an action to perform, rather than hard-coding it.</p>
<p>Reach for it when you need to queue, log, or support undo for requests.</p>
<p>And consider it when you want to decouple the object that invokes an action from the object that knows how to perform it.</p>
<h3 id="heading-3-the-interpreter-design-pattern">3. The Interpreter Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a basic calculator reading an expression like <code>(5 + 3) - 2</code>. Nobody hardcodes a single method that handles every possible expression. Instead, the expression is broken down into small pieces: numbers and operation buttons, each of which knows how to evaluate itself and combine with the others. <code>(5 + 3) - 2</code> becomes a subtraction of two things: the number 2, and the result of adding 5 and 3. Each piece only needs to know how to interpret itself.</p>
<p>That's the Interpreter pattern. A grammar is represented as a tree of small objects, and each one knows how to evaluate its own little piece of it.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if you tried to evaluate an entire expression in one big method? It would grow unmanageable the moment the grammar got more complex. Breaking the grammar into small classes, one per rule, keeps each piece simple.</p>
</li>
<li><p>What if the grammar needed to grow? Adding a new operation, like multiplication, is just a new class. The existing pieces don't need to change.</p>
</li>
<li><p>What if the same expression needed to be evaluated more than once, or in different contexts? Because each piece is just an object, the same tree can be interpreted again without rebuilding it.</p>
</li>
</ul>
<p>In simple terms, you represent a grammar as a tree of small objects, where each object knows how to interpret its own piece of the expression.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"In computer programming, the interpreter pattern is a design pattern that specifies how to evaluate sentences in a language. The basic idea is to have a class for each symbol (terminal or nonterminal) in a specialized computer language." (</em><a href="https://en.wikipedia.org/wiki/Interpreter_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p><code>Number</code> is the terminal expression, a plain value. <code>Add</code> and <code>Subtract</code> are non-terminal expressions, each combining two other expressions. Every node, terminal or not, knows how to <code>Interpret()</code> itself.</p>
<pre><code class="language-csharp">// The abstract expression, every node in the grammar implements this
public abstract class Expression
{
    public abstract int Interpret();
}
</code></pre>
<pre><code class="language-csharp">// A terminal expression, a plain number that needs no further interpretation
public class Number : Expression
{
    private readonly int _value;

    public Number(int value) =&gt; _value = value;

    public override int Interpret() =&gt; _value;
}
</code></pre>
<pre><code class="language-csharp">// Non-terminal expressions, each combines other expressions
public class Add : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;

    public Add(Expression left, Expression right)
    {
        _left  = left;
        _right = right;
    }

    public override int Interpret() =&gt; _left.Interpret() + _right.Interpret();
}

public class Subtract : Expression
{
    private readonly Expression _left;
    private readonly Expression _right;

    public Subtract(Expression left, Expression right)
    {
        _left  = left;
        _right = right;
    }

    public override int Interpret() =&gt; _left.Interpret() - _right.Interpret();
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">// (5 plus 3) minus 2
Expression expression = new Subtract(
    new Add(new Number(5), new Number(3)),
    new Number(2)
);

Console.WriteLine($"Result: {expression.Interpret()}");

// (10 minus 4) plus (2 plus 2)
Expression another = new Add(
    new Subtract(new Number(10), new Number(4)),
    new Add(new Number(2), new Number(2))
);

Console.WriteLine($"Result: {another.Interpret()}");
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Result: 6
Result: 10
</code></pre>
<p>Nothing ever evaluated the whole expression at once. <code>Subtract</code> asked its own <code>_left</code> and <code>_right</code> to interpret themselves, and those asked their own children, all the way down to plain numbers. That's the Interpreter pattern: the grammar interprets itself, one small piece at a time.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Interpreter when you have a simple language or grammar to evaluate, and representing it as a tree of expressions keeps it manageable.</p>
<p>It also works well when the grammar is relatively stable. For example, adding new rules means adding new classes, not rewriting existing ones.</p>
<p>And it's useful when you would rather have many small, focused classes than one large method trying to parse and evaluate everything at once.</p>
<h3 id="heading-4-the-iterator-design-pattern">4. The Iterator Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a bookshelf. You want to go through it one book at a time, from left to right, without needing to know whether the books are held in an array, multiple piles and stacks, or something else entirely. All you need is a way to ask "what's next?" and to know when you've reached the end. How the shelf actually stores its books internally is none of your concern.</p>
<p>That's the Iterator pattern. It gives you a consistent way to step through a collection, one element at a time, without exposing how that collection is built underneath.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if the client had to know how the collection was stored internally to loop over it? Any change to that internal structure would break every piece of code that loops over it. The iterator hides that structure behind a simple "get next" interface.</p>
</li>
<li><p>What if you needed more than one traversal in progress at the same time? A single shared position wouldn't work. Each iterator keeps its own position, so multiple traversals can happen independently.</p>
</li>
<li><p>What if you wanted to loop over the collection using the language's own <code>foreach</code>? Implementing the iterator interface the language expects means your custom collection gets that support for free.</p>
</li>
</ul>
<p>In simple terms, you give a collection a way to be walked through, one element at a time, without exposing how it's actually built underneath.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements."</em> <a href="https://en.wikipedia.org/wiki/Iterator_pattern">(Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p><code>Bookshelf</code> is the aggregate it exposes an <code>IEnumerator&lt;string&gt;</code> without revealing that it stores books in a <code>List&lt;string&gt;</code> internally. <code>BookshelfIterator</code> is the iterator that walks through them one at a time.</p>
<pre><code class="language-csharp">// The aggregate, exposes an iterator without revealing how books are stored
public class Bookshelf : IEnumerable&lt;string&gt;
{
    private readonly List&lt;string&gt; _books = new();

    public void Add(string title) =&gt; _books.Add(title);

    public IEnumerator&lt;string&gt; GetEnumerator() =&gt; new BookshelfIterator(_books);

    IEnumerator IEnumerable.GetEnumerator() =&gt; GetEnumerator();
}
</code></pre>
<pre><code class="language-csharp">// The iterator, walks the collection one book at a time
public class BookshelfIterator : IEnumerator&lt;string&gt;
{
    private readonly List&lt;string&gt; _books;
    private int _position = -1;

    public BookshelfIterator(List&lt;string&gt; books) =&gt; _books = books;

    public string Current =&gt; _books[_position];

    object IEnumerator.Current =&gt; Current;

    public bool MoveNext()
    {
        _position++;
        return _position &lt; _books.Count;
    }

    public void Reset() =&gt; _position = -1;

    public void Dispose() { }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">var bookshelf = new Bookshelf();
bookshelf.Add("Clean Code");
bookshelf.Add("The Pragmatic Programmer");
bookshelf.Add("Design Patterns");

foreach (var book in bookshelf)
{
    Console.WriteLine($"On the shelf: {book}");
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">On the shelf: Clean Code
On the shelf: The Pragmatic Programmer
On the shelf: Design Patterns
</code></pre>
<p>The <code>foreach</code> loop never touched the <code>List&lt;string&gt;</code> inside <code>Bookshelf</code> directly. It called <code>MoveNext()</code> and <code>Current</code> on the <code>BookshelfIterator</code>, one step at a time. That's the Iterator pattern: the traversal logic lives outside the collection itself.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Iterator when you want to traverse a collection without exposing its internal structure.</p>
<p>It's also a good choice when you need to support multiple simultaneous traversals over the same collection.</p>
<p>And try it when you want your custom collection to work with the language's built-in iteration syntax, like <code>foreach</code>.</p>
<h3 id="heading-5-the-mediator-design-pattern">5. The Mediator Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of an air traffic control tower. Planes don't radio each other directly to negotiate who lands first. That would be chaos: dozens of pilots all trying to coordinate with each other at once.</p>
<p>Instead, every plane talks only to the tower. The tower knows the state of the runway and tells each plane what to do. The planes never need to know how many other planes are around, or what they're doing.</p>
<p>That's the Mediator pattern. Instead of objects talking to each other directly, they all talk to one central object that coordinates them.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if every aircraft had to communicate directly with every other aircraft? The number of connections would explode as more aircraft joined, and each one would need to know about all the others. The mediator means each aircraft only needs to know about the tower.</p>
</li>
<li><p>What if the coordination logic was scattered across every object involved? Changing how landings get prioritised would mean touching every aircraft. With a mediator, that logic lives in one place.</p>
</li>
<li><p>What if you wanted to add a new aircraft to the system? It only needs to know how to talk to the tower. It doesn't need to be introduced to every other aircraft already in the sky.</p>
</li>
</ul>
<p>In simple terms, instead of letting objects talk to each other directly, you route all communication through one central object that knows how to coordinate them.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program's running behavior." (</em><a href="https://en.wikipedia.org/wiki/Mediator_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p><code>ControlTower</code> is the mediator. It's the only thing an <code>Aircraft</code> ever talks to. It decides whether a plane can land based on the state it holds, and no aircraft ever contacts another aircraft directly.</p>
<pre><code class="language-csharp">// The mediator interface
public interface IControlTower
{
    void RequestLanding(Aircraft requester);
}
</code></pre>
<pre><code class="language-csharp">// The concrete mediator, coordinates all the aircraft instead of letting them talk to each other
public class ControlTower : IControlTower
{
    private readonly List&lt;Aircraft&gt; _aircraft = new();
    private bool _runwayFree = true;

    public void Register(Aircraft aircraft) =&gt; _aircraft.Add(aircraft);

    public void RequestLanding(Aircraft requester)
    {
        if (_runwayFree)
        {
            _runwayFree = false;
            Console.WriteLine($"Tower: Runway clear. {requester.Name}, you are cleared to land.");
        }
        else
        {
            Console.WriteLine($"Tower: Runway occupied. {requester.Name}, please hold your position.");
        }
    }
}
</code></pre>
<pre><code class="language-csharp">// The colleague, only ever talks to the mediator, never to other aircraft directly
public class Aircraft
{
    public string Name { get; }

    private readonly IControlTower _tower;

    public Aircraft(string name, IControlTower tower)
    {
        Name   = name;
        _tower = tower;
    }

    public void RequestLanding()
    {
        Console.WriteLine($"{Name}: Requesting permission to land.");
        _tower.RequestLanding(this);
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">var tower = new ControlTower();

var flight101 = new Aircraft("Flight 101", tower);
var flight202 = new Aircraft("Flight 202", tower);

tower.Register(flight101);
tower.Register(flight202);

flight101.RequestLanding();
flight202.RequestLanding();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Flight 101: Requesting permission to land.
Tower: Runway clear. Flight 101, you are cleared to land.
Flight 202: Requesting permission to land.
Tower: Runway occupied. Flight 202, please hold your position.
</code></pre>
<p>Flight 101 and Flight 202 never spoke to each other. Neither one even knows the other exists. Both only ever talked to the tower, and the tower decided what happened next. That's the Mediator pattern.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Mediator when a group of objects communicate in complex, tangled ways, and you want to centralise that communication.</p>
<p>It's also helpful when you want to reuse objects independently, without them being locked together by direct references to each other.</p>
<p>And reach for it when the way objects interact changes often, and you'd rather change it in one place than in every object involved.</p>
<h3 id="heading-6-the-memento-design-pattern">6. The Memento Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of the undo history in a text editor. Every so often, the editor quietly takes a snapshot of what the document looks like. It doesn't ask the document to expose its internals to do this. It just captures a copy of the content at that moment.</p>
<p>When you press undo, the editor hands that snapshot back, and the document restores itself to exactly how it was. The history keeps a pile of these snapshots, but it never looks inside them or changes them. It only stores them and hands them back.</p>
<p>That's the Memento pattern. It lets you capture and restore an object's state without exposing how that state is structured internally.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if undo required exposing every private field of the document? That would break encapsulation, and any change to the document's internals would ripple out to whatever handles undo. The memento hides that structure inside an object only the document itself knows how to read.</p>
</li>
<li><p>What if the history needed to inspect or modify old snapshots? It shouldn't be able to. The caretaker only stores and returns mementos, it never reads or changes what's inside them.</p>
</li>
<li><p>What if you needed several restore points, not just one? Because each memento is just an object, they can be stacked, listed, or discarded, giving you as many restore points as you want to keep.</p>
</li>
</ul>
<p>In simple terms, you capture an object's state in a snapshot you can restore later, without exposing how that state is put together internally.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback)."</em></p>
<p><strong>(</strong><a href="https://en.wikipedia.org/wiki/Memento_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p><code>TextEditor</code> is the originator: it creates <code>EditorMemento</code> snapshots of itself and can restore from one. <code>History</code> is the caretaker: it stores mementos on a stack without ever looking inside them.</p>
<pre><code class="language-csharp">// The memento, an immutable snapshot of the editor's state
public class EditorMemento
{
    public string Content { get; }

    public EditorMemento(string content) =&gt; Content = content;
}
</code></pre>
<pre><code class="language-csharp">// The originator, creates and restores from mementos of its own state
public class TextEditor
{
    public string Content { get; private set; } = string.Empty;

    public void Write(string text) =&gt; Content += text;

    public EditorMemento Save() =&gt; new(Content);

    public void Restore(EditorMemento memento) =&gt; Content = memento.Content;
}
</code></pre>
<pre><code class="language-csharp">// The caretaker, stores mementos without ever looking inside them
public class History
{
    private readonly Stack&lt;EditorMemento&gt; _snapshots = new();

    public void Save(EditorMemento memento) =&gt; _snapshots.Push(memento);

    public EditorMemento Undo() =&gt; _snapshots.Pop();
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">var editor  = new TextEditor();
var history = new History();

editor.Write("Hello");
history.Save(editor.Save());

editor.Write(", world");
history.Save(editor.Save());

editor.Write("!!!");
Console.WriteLine($"Current: {editor.Content}");

editor.Restore(history.Undo());
Console.WriteLine($"After undo: {editor.Content}");

editor.Restore(history.Undo());
Console.WriteLine($"After undo: {editor.Content}");
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Current: Hello, world!!!
After undo: Hello, world
After undo: Hello
</code></pre>
<p><code>History</code> never read or changed the text inside a snapshot. It just pushed mementos on and popped them off. Only <code>TextEditor</code> knew what to do with the content inside one. That's the Memento pattern.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Memento when you need undo/redo functionality and want to capture state without exposing an object's internals.</p>
<p>It's also a good choice when taking a snapshot directly would break encapsulation by exposing private fields.</p>
<p>And it's helpful when you want the object that stores history to stay dumb: like holding snapshots without knowing or caring what's inside them.</p>
<h3 id="heading-7-the-observer-design-pattern">7. The Observer Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of subscribing to a YouTube channel. You don't sit there refreshing the page, checking if a new video has been uploaded. You subscribe once, and the moment the channel uploads something, you get notified automatically.</p>
<p>The channel doesn't know or care what each subscriber does with that notification. It just knows it has a list of subscribers, and when something changes, it tells all of them.</p>
<p>That's the Observer pattern. One object holds a list of dependents, and whenever its state changes, it notifies every one of them automatically.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if every subscriber had to keep checking the channel for updates? That would waste effort and add delay. The channel notifying its subscribers directly means they find out the moment it happens.</p>
</li>
<li><p>What if the channel had to know exactly what each subscriber wanted to do with a new video? It shouldn't need to. The channel only calls <code>Notify()</code>, each subscriber decides for itself what that means.</p>
</li>
<li><p>What if you wanted to add or remove subscribers at runtime? The channel doesn't need to change. It just keeps a list, and subscribing or unsubscribing only ever affects that list.</p>
</li>
</ul>
<p>In simple terms, one object keeps a list of dependents and automatically notifies all of them whenever its own state changes.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods."</em> <a href="https://en.wikipedia.org/wiki/Observer_pattern">(Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p><code>YouTubeChannel</code> is the subject: it keeps a list of <code>ISubscriber</code>s and notifies all of them whenever a video is uploaded. <code>Subscriber</code> is the concrete observer, deciding for itself what to do with that notification.</p>
<pre><code class="language-csharp">// The observer interface, every subscriber implements this
public interface ISubscriber
{
    void Notify(string channelName, string videoTitle);
}
</code></pre>
<pre><code class="language-csharp">// The concrete observer
public class Subscriber : ISubscriber
{
    private readonly string _name;

    public Subscriber(string name) =&gt; _name = name;

    public void Notify(string channelName, string videoTitle)
    {
        Console.WriteLine($"{_name}: {channelName} just uploaded '{videoTitle}'!");
    }
}
</code></pre>
<pre><code class="language-csharp">// The subject, keeps track of its subscribers and notifies them of changes
public class YouTubeChannel
{
    private readonly string _name;
    private readonly List&lt;ISubscriber&gt; _subscribers = new();

    public YouTubeChannel(string name) =&gt; _name = name;

    public void Subscribe(ISubscriber subscriber)   =&gt; _subscribers.Add(subscriber);
    public void Unsubscribe(ISubscriber subscriber) =&gt; _subscribers.Remove(subscriber);

    public void UploadVideo(string title)
    {
        Console.WriteLine($"{_name}: Uploaded '{title}'.");

        foreach (var subscriber in _subscribers)
        {
            subscriber.Notify(_name, title);
        }
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">var channel = new YouTubeChannel("Code With Isaiah");

var alice = new Subscriber("Alice");
var bob   = new Subscriber("Bob");

channel.Subscribe(alice);
channel.Subscribe(bob);

channel.UploadVideo("Design Patterns Explained");

channel.Unsubscribe(bob);
channel.UploadVideo("Understanding the Observer Pattern");
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Code With Isaiah: Uploaded 'Design Patterns Explained'.
Alice: Code With Isaiah just uploaded 'Design Patterns Explained'!
Bob: Code With Isaiah just uploaded 'Design Patterns Explained'!
Code With Isaiah: Uploaded 'Understanding the Observer Pattern'.
Alice: Code With Isaiah just uploaded 'Understanding the Observer Pattern'!
</code></pre>
<p>Once Bob unsubscribed, he stopped hearing about new uploads entirely. The channel never singled him out, it just no longer had him on the list it notifies. That's the Observer pattern.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Observer when a change to one object should automatically update an unknown number of others.</p>
<p>It's also useful when you want objects to stay loosely coupled: the subject only knows about an observer interface, never concrete details.</p>
<p>And it's a good option when the number of dependents can grow or shrink at runtime, such as subscribing and unsubscribing.</p>
<h3 id="heading-8-the-state-design-pattern">8. The State Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of an online order moving through its lifecycle: pending, then shipped, then delivered. What "moving to the next step" actually means is different at every stage. From pending it means handing the package to a courier. From shipped it means marking it as received. From delivered, there's nowhere left to go. Rather than one giant method full of <code>if</code> checks for every possible stage, each stage can just know what comes after it.</p>
<p>That's the State pattern. The object's behaviour changes based on its current state, and each state knows how to transition to the next one.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if one method had to handle every stage with a long chain of conditionals? It would grow harder to follow every time a new stage was added. Giving each stage its own class keeps the logic for that stage self-contained.</p>
</li>
<li><p>What if adding a new stage meant editing that same giant method? It's easy to introduce a bug in an unrelated stage while doing so. A new state is just a new class, dropped in alongside the others.</p>
</li>
<li><p>What if the object needed to behave completely differently depending on where it was in its lifecycle? Delegating to the current state object means the context doesn't need to know the details. It just asks the current state what to do.</p>
</li>
</ul>
<p>In simple terms, you let an object change its behaviour by changing which state object it's currently holding, so the object appears to change how it acts as its state changes.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The state pattern is a behavioral software design pattern that allows an object to alter its behavior when its internal state changes. This pattern is close to the concept of finite-state machines."</em> <strong>(</strong><a href="https://en.wikipedia.org/wiki/State_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p><code>Order</code> is the context: it holds whatever <code>IOrderState</code> it's currently in and delegates to it. Each concrete state, <code>PendingState</code>, <code>ShippedState</code>, <code>DeliveredState</code>, knows what the next state should be.</p>
<pre><code class="language-csharp">// The state interface, every state implements this
public interface IOrderState
{
    void Next(Order order);
    string Name { get; }
}
</code></pre>
<pre><code class="language-csharp">// The context, delegates behaviour to whatever state it currently holds
public class Order
{
    public IOrderState State { get; set; } = new PendingState();

    public void Next()
    {
        Console.WriteLine($"Order is currently: {State.Name}");
        State.Next(this);
    }
}
</code></pre>
<pre><code class="language-csharp">// Concrete states, each knows what comes after it
public class PendingState : IOrderState
{
    public string Name =&gt; "Pending";

    public void Next(Order order) =&gt; order.State = new ShippedState();
}

public class ShippedState : IOrderState
{
    public string Name =&gt; "Shipped";

    public void Next(Order order) =&gt; order.State = new DeliveredState();
}

public class DeliveredState : IOrderState
{
    public string Name =&gt; "Delivered";

    public void Next(Order order)
    {
        Console.WriteLine("Order has already been delivered. Nothing left to do.");
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">var order = new Order();

order.Next();
order.Next();
order.Next();
order.Next();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-json">Order is currently: Pending
Order is currently: Shipped
Order is currently: Delivered
Order has already been delivered. Nothing left to do.
</code></pre>
<p><code>Order</code> never checked "if pending, do this, if shipped, do that." It just asked its current state what to do next, and the state itself decided what came after. That's the State pattern.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use State when an object's behaviour depends on its state, and it must change that behaviour at runtime as the state changes.</p>
<p>It's also helpful when you have large conditional blocks that branch on the object's current state or type.</p>
<p>And choose it when transitions between states should be explicit and self-contained, rather than scattered across one big method.</p>
<h3 id="heading-9-the-strategy-design-pattern">9. The Strategy Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of checking out of an online store. You can pay by credit card, or you can pay through PayPal. The shopping cart doesn't care which one you pick. It just knows the total, hands it to whichever payment method you chose, and lets that method handle the details of actually charging you. Swap the payment method, and the cart's own code never changes.</p>
<p>That's the Strategy pattern. An algorithm (in this case "how to pay") is pulled out into its own interchangeable object, and the client just picks which one to use.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if the cart had a big <code>if/else</code> for every payment method? Adding a new one would mean editing that method every time. Pulling each payment method out into its own class means the cart never needs to change.</p>
</li>
<li><p>What if you wanted to swap the algorithm at runtime? A hardcoded method can't be swapped. A strategy object can simply be replaced with another one that implements the same interface.</p>
</li>
<li><p>What if two different payment methods needed to share a common interface but nothing else? Each one implements the strategy interface, but its internal details (a card number here, an email there) stay private to it.</p>
</li>
</ul>
<p>In simple terms, you pull an algorithm out into its own interchangeable object, so the class using it doesn't need to know or care which specific version is running.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The strategy pattern is a behavioral software design pattern that enables selecting an algorithm at runtime."</em> <a href="https://en.wikipedia.org/wiki/Strategy_pattern">(Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p><code>ShoppingCart</code> is the context: it holds an <code>IPaymentStrategy</code> and delegates the actual payment to it. <code>CreditCardPayment</code> and <code>PayPalPayment</code> are concrete strategies, each a different way to pay.</p>
<pre><code class="language-csharp">// The strategy interface, every payment method implements this
public interface IPaymentStrategy
{
    void Pay(decimal amount);
}
</code></pre>
<pre><code class="language-csharp">// Concrete strategies, each a different way to pay
public class CreditCardPayment : IPaymentStrategy
{
    private readonly string _cardNumber;

    public CreditCardPayment(string cardNumber) =&gt; _cardNumber = cardNumber;

    public void Pay(decimal amount)
    {
        Console.WriteLine($"Charged ${amount} to credit card ending in {_cardNumber[^4..]}.");
    }
}

public class PayPalPayment : IPaymentStrategy
{
    private readonly string _email;

    public PayPalPayment(string email) =&gt; _email = email;

    public void Pay(decimal amount)
    {
        Console.WriteLine($"Charged ${amount} via PayPal account {_email}.");
    }
}
</code></pre>
<pre><code class="language-csharp">// The context, holds a strategy and delegates the actual payment work to it
public class ShoppingCart
{
    private readonly decimal _total;
    private IPaymentStrategy? _paymentMethod;

    public ShoppingCart(decimal total) =&gt; _total = total;

    public void SetPaymentMethod(IPaymentStrategy method) =&gt; _paymentMethod = method;

    public void Checkout()
    {
        if (_paymentMethod is null)
        {
            Console.WriteLine("No payment method selected.");
            return;
        }

        _paymentMethod.Pay(_total);
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">var cart = new ShoppingCart(59.99m);

cart.SetPaymentMethod(new CreditCardPayment("4111 1111 1111 1111"));
cart.Checkout();

cart.SetPaymentMethod(new PayPalPayment("isaiah@example.com"));
cart.Checkout();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-json">Charged $59.99 to credit card ending in 1111.
Charged $59.99 via PayPal account isaiah@example.com.
</code></pre>
<p><code>ShoppingCart</code> never knew how a payment actually got processed. It just called <code>Pay()</code> on whatever strategy it was holding at the time. That's the Strategy pattern: the algorithm is swapped out, the class using it stays exactly the same.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Strategy when you have several variants of an algorithm, and want to switch between them at runtime.</p>
<p>It's also helpful when you want to avoid a class full of conditionals that pick behaviour based on a type or flag.</p>
<p>And it's a solid choice when related classes only differ in the behaviour they use, and that behaviour should be interchangeable.</p>
<h3 id="heading-10-the-template-method-design-pattern">10. The Template Method Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of making a hot drink, tea or coffee. Both follow the exact same basic steps: boil water, brew, pour into a cup, and add something to taste. What differs is only two of those steps: tea gets steeped, and coffee gets brewed through grounds. Tea gets lemon, and coffee gets sugar and milk. The overall recipe never changes, only the specific details of a couple of steps within it.</p>
<p>That's the Template Method pattern. A base class defines the fixed skeleton of an algorithm, and subclasses only fill in the steps that are actually allowed to vary.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if every beverage repeated the entire recipe from scratch? Boiling water and pouring into a cup would be duplicated in every single class. The template method keeps those steps in one place, written once.</p>
</li>
<li><p>What if a subclass could reorder the steps, or skip one entirely? That would let each beverage break the overall recipe. Because the algorithm's skeleton lives in the base class as a single method, the order and structure stay fixed.</p>
</li>
<li><p>What if you wanted to add a new beverage? Only the steps that differ, brewing and condiments, need to be written. Everything else is already handled by the base class.</p>
</li>
</ul>
<p>In simple terms, you define the fixed skeleton of an algorithm in a base class, and let subclasses fill in only the steps that are actually allowed to differ.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"In object-oriented programming, the template method is one of the behavioral design patterns identified by Gamma et al. in the book Design Patterns. The template method is a method in a superclass, usually an abstract superclass, and defines the skeleton of an operation in terms of a number of high-level steps." (</em><a href="https://en.wikipedia.org/wiki/Template_method_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p><code>Beverage</code> defines <code>Prepare()</code> as the template method: the fixed sequence of steps. <code>Tea</code> and <code>Coffee</code> only override <code>Brew()</code> and <code>AddCondiments()</code>, the two steps that are actually allowed to vary.</p>
<pre><code class="language-csharp">// The abstract class, defines the skeleton of the algorithm
public abstract class Beverage
{
    // The template method, the steps and their order never change
    public void Prepare()
    {
        BoilWater();
        Brew();
        PourInCup();
        AddCondiments();
    }

    private void BoilWater() =&gt; Console.WriteLine("Boiling water.");
    private void PourInCup() =&gt; Console.WriteLine("Pouring into cup.");

    // Steps left for subclasses to fill in
    protected abstract void Brew();
    protected abstract void AddCondiments();
}
</code></pre>
<pre><code class="language-csharp">// A concrete class, fills in the steps specific to tea
public class Tea : Beverage
{
    protected override void Brew() =&gt; Console.WriteLine("Steeping the tea bag.");
    protected override void AddCondiments() =&gt; Console.WriteLine("Adding lemon.");
}

// Another concrete class, fills in the steps specific to coffee
public class Coffee : Beverage
{
    protected override void Brew() =&gt; Console.WriteLine("Brewing the coffee grounds.");
    protected override void AddCondiments() =&gt; Console.WriteLine("Adding sugar and milk.");
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">Beverage tea    = new Tea();
Beverage coffee = new Coffee();

tea.Prepare();
Console.WriteLine();
coffee.Prepare();
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Boiling water.
Steeping the tea bag.
Pouring into cup.
Adding lemon.

Boiling water.
Brewing the coffee grounds.
Pouring into cup.
Adding sugar and milk.
</code></pre>
<p>Both drinks boiled water and poured into a cup in exactly the same way, because <code>Prepare()</code> in the base class handled that. Only brewing and condiments changed, because those were the steps each subclass was actually responsible for. That's the Template Method pattern.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Template Method when several classes share the same overall algorithm, but differ in a few specific steps.</p>
<p>It's a good choice when you want to enforce a fixed sequence of steps, while still letting subclasses customise parts of it.</p>
<p>And it's helpful when you want to avoid duplicating the parts of an algorithm that never change across every subclass.</p>
<h3 id="heading-11-the-visitor-design-pattern">11. The Visitor Design Pattern</h3>
<h4 id="heading-real-world-example">Real World Example</h4>
<p>Think of a shopping cart with different kinds of items: books and electronics, each taxed differently at checkout. You don't want to bake pricing logic into the <code>Book</code> and <code>Electronic</code> classes themselves, especially if you'll need other operations on them later too, like generating a shipping label or a warranty summary. Instead, each item just accepts a visitor and hands itself over. The visitor is the one that actually knows how to price a book differently from an electronic.</p>
<p>That's the Visitor pattern. The operation lives outside the objects it acts on, and each object just lets the visitor know what it needs to know: what type of thing it actually is.</p>
<h4 id="heading-problems-it-solves">Problems it solves:</h4>
<ul>
<li><p>What if pricing logic was written directly inside <code>Book</code> and <code>Electronic</code>? Every new operation (tax, shipping, warranty) would mean editing both classes again and again. The visitor keeps each new operation in its own self-contained class instead.</p>
</li>
<li><p>What if you needed to add a new operation without touching the existing item classes? Normally that means modifying every class the operation applies to. A new visitor is a new class, while <code>Book</code> and <code>Electronic</code> never change.</p>
</li>
<li><p>What if a generic loop had to guess the concrete type of each item? That usually means a chain of type checks. <code>Accept()</code> calling <code>Visit(this)</code> lets the compiler pick the right overload automatically, without a single <code>if</code> or type check.</p>
</li>
</ul>
<p>In simple terms, you move an operation out of the objects it acts on and into its own class. Each object just accepts a visitor and lets it know what concrete type it is.</p>
<p>Wikipedia describes it like this:</p>
<blockquote>
<p><em>"The visitor design pattern is a way of separating an algorithm from an object structure on which it operates." (</em><a href="https://en.wikipedia.org/wiki/Visitor_pattern">Source</a>)</p>
</blockquote>
<h4 id="heading-programming-example">Programming Example:</h4>
<p><code>Book</code> and <code>Electronic</code> both implement <code>IItem</code> and simply call <code>visitor.Visit(this)</code>. <code>PricingVisitor</code> implements <code>IVisitor</code> with an overload for each concrete type, so the right pricing logic runs automatically.</p>
<pre><code class="language-csharp">// The element interface, every item in the cart implements this
public interface IItem
{
    void Accept(IVisitor visitor);
}
</code></pre>
<pre><code class="language-csharp">// Concrete elements, each accepts a visitor and hands itself over
public class Book : IItem
{
    public string Title { get; }
    public decimal Price { get; }

    public Book(string title, decimal price)
    {
        Title = title;
        Price = price;
    }

    public void Accept(IVisitor visitor) =&gt; visitor.Visit(this);
}

public class Electronic : IItem
{
    public string Name { get; }
    public decimal Price { get; }

    public Electronic(string name, decimal price)
    {
        Name  = name;
        Price = price;
    }

    public void Accept(IVisitor visitor) =&gt; visitor.Visit(this);
}
</code></pre>
<pre><code class="language-csharp">// The visitor interface, one Visit overload per concrete element
public interface IVisitor
{
    void Visit(Book book);
    void Visit(Electronic electronic);
}
</code></pre>
<pre><code class="language-csharp">// A concrete visitor, adds a new operation without touching Book or Electronic
public class PricingVisitor : IVisitor
{
    public decimal Total { get; private set; }

    public void Visit(Book book)
    {
        Console.WriteLine($"Book: {book.Title} — ${book.Price:F2} (no tax).");
        Total += book.Price;
    }

    public void Visit(Electronic electronic)
    {
        var priceWithTax = electronic.Price * 1.15m;
        Console.WriteLine($"Electronic: {electronic.Name} — ${priceWithTax:F2} (with 15% tax).");
        Total += priceWithTax;
    }
}
</code></pre>
<p>Now let's see it in action:</p>
<pre><code class="language-csharp">var cart = new List&lt;IItem&gt;
{
    new Book("Design Patterns", 45.00m),
    new Electronic("Headphones", 120.00m)
};

var pricingVisitor = new PricingVisitor();

foreach (var item in cart)
{
    item.Accept(pricingVisitor);
}

Console.WriteLine($"Total: ${pricingVisitor.Total:F2}");
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-yaml">Book: Design Patterns — $45.00 (no tax).
Electronic: Headphones — $138.00 (with 15% tax).
Total: $183.00
</code></pre>
<p>Neither <code>Book</code> nor <code>Electronic</code> contained a single line of pricing logic. Each one only knew how to <code>Accept()</code> a visitor. <code>PricingVisitor</code> was the one that actually decided how each type gets priced. That's the Visitor pattern: the operation lives outside the object structure, not inside it.</p>
<h4 id="heading-when-to-use-it">When to Use it</h4>
<p>Use Visitor when you need to perform operations across a group of unrelated classes, without polluting each class with that logic.</p>
<p>It's also helpful when you want to add new operations often, but the object structure itself rarely changes.</p>
<p>And it's a good choice when you'd otherwise need type checks or casting to figure out what to do with each object in a collection.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That covers all 23 classic design patterns across the three families: Creational, Structural, and Behavioral.</p>
<p>None of them are rules you must follow. They're answers to problems that show up again and again in software: how to create objects without hard-coding their exact type, how to compose bigger structures out of smaller ones, and how to let objects communicate without being tightly bound to each other.</p>
<p>A few things worth remembering:</p>
<ul>
<li><p>You won't use most of these patterns most of the time. Recognising <em>when a problem calls for one</em> is the actual skill. Forcing a pattern onto a problem that doesn't need it usually makes the code harder to follow, not easier.</p>
</li>
<li><p>The real world analogies exist to build intuition, not to be taken literally. Once a pattern's shape clicks in a story you understand, spotting it in real code becomes far easier.</p>
</li>
<li><p>Patterns compose. A Factory Method might produce objects that are themselves Decorators. A Composite tree might be built with a Builder. Real systems mix and layer patterns rather than using them in isolation.</p>
</li>
<li><p>The language doesn't matter. Every example here is in C#, but the same shapes exist in Python, Java, TypeScript, Go, Rust, and beyond. If you understand the <em>problem</em> a pattern solves, translating it to any language is straightforward.</p>
</li>
</ul>
<p>The goal isn't to memorise 23 names. It's to recognise the recurring problems underneath them, so that when one shows up in your own code, you already know a proven shape for solving it.</p>
<blockquote>
<p><em>"Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice."</em></p>
<p><strong>Source:</strong> Christopher Alexander, <em>A Pattern Language</em> — the architectural work that originally inspired software design patterns.</p>
</blockquote>
<p>If this handbook was useful, the source lives at <a href="https://github.com/Clifftech123/design-patterns-handbook">github.com/Clifftech123/design-patterns-handbook</a>. Star it, fork it, or open a PR with a pattern you think is missing.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Builder Design Pattern: A Better Approach to Complex Object Construction ]]>
                </title>
                <description>
                    <![CDATA[ Some objects are simple, like a string, number, or boolean. You create them in one line and move on. Other objects aren't simple at all, like a carousel widget that needs an item count, an item builde ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-builder-design-pattern-a-better-approach-to-complex-object-construction/</link>
                <guid isPermaLink="false">6a98992c210435845c785935</guid>
                
                    <category>
                        <![CDATA[ builder pattern ]]>
                    </category>
                
                    <category>
                        <![CDATA[ creational patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design and architecture ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwaseyi Fatunmole ]]>
                </dc:creator>
                <pubDate>Wed, 02 Sep 2026 21:46:20 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/3f61ce13-e9fd-4cc5-96a7-6c68b56048ef.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Some objects are simple, like a string, number, or boolean. You create them in one line and move on.</p>
<p>Other objects aren't simple at all, like a carousel widget that needs an item count, an item builder function, a controller, a height, a viewport fraction, autoplay settings, page change callbacks, and infinite scroll configuration. Or like an HTTP request that needs a URL, headers, authentication tokens, a body, a timeout, and retry logic. Or a notification that needs a title, body, icon, channel, priority, sound, vibration, and action buttons.</p>
<p>When you need to construct objects like these, the naïve approach is a constructor with many parameters. It works, but it creates problems that compound as the object grows more complex. Parameters become hard to tell apart. Optional parameters require null checks everywhere. The order of arguments matters and is easy to get wrong. The constructor call becomes a wall of values that nobody wants to read or maintain.</p>
<p>The Builder Design Pattern solves this. It separates the construction of a complex object from its representation, allowing the same construction process to create different configurations through a readable, step-by-step interface.</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-what-is-the-builder-pattern">What is the Builder 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-method-chaining-the-fluent-interface">Method Chaining: The Fluent Interface</a></p>
</li>
<li><p><a href="#heading-real-world-example-one-flutter-carousel-builder">Real World Example One: Flutter Carousel Builder</a></p>
</li>
<li><p><a href="#heading-real-world-example-two-http-request-builder">Real World Example Two: HTTP Request Builder</a></p>
</li>
<li><p><a href="#heading-the-builder-pattern-in-c">The Builder Pattern in C#</a></p>
</li>
<li><p><a href="#heading-builder-vs-constructor-vs-factory">Builder vs Constructor vs Factory</a></p>
</li>
<li><p><a href="#heading-when-to-use-the-builder-pattern">When to Use the Builder 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-prerequisites">Prerequisites</h2>
<p>Before reading this article, you should be comfortable with:</p>
<ul>
<li><p>Object-oriented programming: classes, constructors, and methods</p>
</li>
<li><p>What a design pattern is at a conceptual level</p>
</li>
<li><p>Basic Dart or C# syntax</p>
</li>
</ul>
<p>You don't need prior experience with design patterns. This article introduces the Builder pattern from first principles.</p>
<h2 id="heading-what-is-the-builder-pattern">What is the Builder Pattern?</h2>
<p>The Builder Pattern is a creational design pattern. Creational patterns deal with how objects are created. The Builder pattern specifically deals with the construction of complex objects that require many configuration steps.</p>
<p>The pattern separates two concerns that are often tangled together in simpler code: what an object is, and how it's built. The object holds its own data and behavior. The Builder holds the construction logic and accumulates the configuration step by step before producing the final object.</p>
<p>The result is a construction process that reads like a description of what you're building rather than a list of values to pass to a constructor.</p>
<h2 id="heading-the-problem-it-solves">The Problem It Solves</h2>
<p>Here's what constructing a complex widget looks like without the Builder pattern:</p>
<pre><code class="language-csharp">// constructing a carousel directly — hard to read, easy to get wrong
CarouselSlider.builder(
  options: CarouselOptions(
    height: 200,
    viewportFraction: 0.97,
    enableInfiniteScroll: false,
    autoPlayCurve: Curves.easeIn,
    enlargeCenterPage: true,
    pauseAutoPlayOnManualNavigate: true,
    onPageChanged: onPageChanged,
    autoPlay: false,
  ),
  itemBuilder: (context, index, realIndex) =&gt; AdCard(ad: ads[index]),
  itemCount: ads.length,
)
</code></pre>
<p>This works. But look at what happens when you need to create two different carousels in the same screen: one for ads and one for account balances. Both need different heights, viewport fractions, item builders, and item counts. You copy the entire construction block, modify the values, and now you have two walls of configuration that are visually similar but subtly different.</p>
<p>When a new requirement comes in to add autoplay to the ads carousel but not the balance carousel, you have to find the right block, modify it carefully, and hope you're modifying the right one.</p>
<p>The deeper problem is that the construction logic is scattered across the codebase. Every place a carousel is created knows all the details of carousel construction. There's no single place where that knowledge lives.</p>
<p>The Builder pattern collects construction knowledge into one place and exposes it through a clean interface.</p>
<h2 id="heading-core-components">Core Components</h2>
<p>The Builder pattern has three components.</p>
<h3 id="heading-the-product">The Product</h3>
<p>The complex object being built. It doesn't know about the Builder. It holds its configuration and has behavior based on that configuration. The Product is often constructed with a private constructor so it can only be created by its Builder.</p>
<h3 id="heading-the-builder">The Builder</h3>
<p>The class responsible for accumulating configuration and producing the Product. Each method on the Builder configures one aspect of the Product and returns the Builder itself. This return is what enables method chaining. The final method on the Builder produces the completed Product.</p>
<h3 id="heading-the-director-optional">The Director (optional)</h3>
<p>A class that knows how to use a Builder to produce specific pre-configured Products. The Director encodes the knowledge of how to build common configurations so callers don't need to know the details. In practice, a Factory method often serves this role.</p>
<h2 id="heading-method-chaining-the-fluent-interface">Method Chaining: The Fluent Interface</h2>
<p>Method chaining is the technique that makes Builder code read naturally. Each Builder method returns <code>this</code> (the Builder itself) so the next method call can follow immediately on the same line or the next line.</p>
<pre><code class="language-csharp">// without method chaining
final builder = RequestBuilder();
builder.setUrl('https://api.example.com/users');
builder.setMethod('POST');
builder.addHeader('Authorization', 'Bearer $token');
builder.setBody({'name': 'John'});
final request = builder.build();

// with method chaining
final request = RequestBuilder()
    .setUrl('https://api.example.com/users')
    .setMethod('POST')
    .addHeader('Authorization', 'Bearer $token')
    .setBody({'name': 'John'})
    .build();
</code></pre>
<p>Both produce exactly the same result. The chained version reads like a sentence describing the request. The unchained version is a sequence of imperative statements.</p>
<p>Method chaining is sometimes called a Fluent Interface. The name comes from how the code reads: fluently, like natural language, from left to right or top to bottom.</p>
<h2 id="heading-real-world-example-one-flutter-carousel-builder">Real World Example One: Flutter Carousel Builder</h2>
<p>This is a real production implementation from a Flutter fintech application. The app needs to show two different carousels on the dashboard: one for promotional ads and one for account balances. Each carousel has a different configuration but shares the same underlying construction mechanism.</p>
<h3 id="heading-the-configuration-objects">The Configuration Objects</h3>
<pre><code class="language-csharp">import 'package:flutter/widgets.dart';
import 'package:equatable/equatable.dart';
import 'package:carousel_slider/carousel_slider.dart';

class CarouselArgs extends Equatable {
  final CarouselSliderController? carouselController;
  final int itemCount;
  final Widget Function(BuildContext, int, int) itemBuilder;
  final CarouselOptions options;

  const CarouselArgs({
    this.carouselController,
    required this.itemCount,
    required this.itemBuilder,
    required this.options,
  });

  @override
  List&lt;Object?&gt; get props =&gt; [
        carouselController,
        itemCount,
        itemBuilder,
        options,
      ];
}

class CarouselOptions extends Equatable {
  final double? height;
  final double? viewPortFraction;
  final bool? enableInfiniteScroll;
  final bool? enlargeCenterPage;
  final bool? pauseAutoPlayOnManualNavigate;
  final bool? autoplay;
  final Curve? autoplayCurve;
  final void Function(int, CarouselPageChangedReason)? onPageChanged;

  const CarouselOptions({
    this.height,
    this.viewPortFraction,
    this.enableInfiniteScroll,
    this.enlargeCenterPage,
    this.pauseAutoPlayOnManualNavigate,
    this.autoplay,
    this.autoplayCurve,
    this.onPageChanged,
  });

  @override
  List&lt;Object?&gt; get props =&gt; [
        height,
        viewPortFraction,
        enableInfiniteScroll,
        enlargeCenterPage,
        pauseAutoPlayOnManualNavigate,
        autoplay,
        autoplayCurve,
        onPageChanged,
      ];
}
</code></pre>
<p><code>CarouselArgs</code> and <code>CarouselOptions</code> are the configuration objects. They hold all the data needed to construct a carousel. They're simple data containers with no construction logic of their own.</p>
<h3 id="heading-the-product">The Product</h3>
<pre><code class="language-csharp">import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart' as n;

class CustomCarousel extends StatelessWidget {
  final CarouselArgs dto;

  // private constructor only the Builder can create this widget
  CustomCarousel._builder(CustomCarouselBuilder builder)
      : dto = builder._dto!;

  @override
  Widget build(BuildContext context) {
    return n.CarouselSlider.builder(
      options: n.CarouselOptions(
        height: dto.options.height,
        viewportFraction: dto.options.viewPortFraction!,
        enableInfiniteScroll: dto.options.enableInfiniteScroll!,
        enlargeCenterPage: dto.options.enlargeCenterPage,
        onPageChanged: dto.options.onPageChanged,
        autoPlayCurve: dto.options.autoplayCurve!,
        autoPlay: dto.options.autoplay!,
      ),
      itemBuilder: dto.itemBuilder,
      itemCount: dto.itemCount,
    );
  }
}
</code></pre>
<p><code>CustomCarousel</code> is the Product. Its constructor is private: <code>CustomCarousel._builder</code>. The underscore prefix and the named constructor ensure that nobody outside this class can instantiate a <code>CustomCarousel</code> directly. The only way to create one is through the Builder.</p>
<p>This is intentional. It enforces that all carousel construction goes through the Builder, where the configuration is validated and assembled consistently.</p>
<h3 id="heading-the-builder">The Builder</h3>
<pre><code class="language-csharp">class CustomCarouselBuilder {
  BuildContext? _context;
  CarouselArgs? _dto;

  CustomCarouselBuilder setArgs(BuildContext context, CarouselArgs value) {
    _context = context;
    _dto = value;
    return this;
  }

  Widget get buildCarousel =&gt;
      CustomCarousel._builder(this).build(_context!);
}
</code></pre>
<p><code>CustomCarouselBuilder</code> is the Builder. It accumulates the <code>BuildContext</code> and the <code>CarouselArgs</code> through <code>setArgs</code>. The <code>setArgs</code> method returns <code>this</code>, enabling the call to be chained.</p>
<p><code>buildCarousel</code> is the terminal step. It calls the private constructor of <code>CustomCarousel</code>, passing itself as the argument, and then calls <code>build</code> to produce the final widget. The carousel can't be built until both the context and the args have been provided.</p>
<h3 id="heading-the-director-a-factory-that-uses-the-builder">The Director: A Factory That Uses the Builder</h3>
<pre><code class="language-csharp">import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:carousel_slider/carousel_slider.dart' as n;

class CarouselWidgetFactory {
  static CustomCarouselBuilder showCarouselAds(
    BuildContext context, {
    void Function(int, n.CarouselPageChangedReason)? onPageChanged,
  }) =&gt;
      CustomCarouselBuilder().setArgs(
        context,
        CarouselArgs(
          itemCount: context.read&lt;DashboardLogic&gt;().dashboardAds.length,
          itemBuilder: (context, index, realIndex) =&gt; DashboardAdsList(
            dto: context.read&lt;DashboardLogic&gt;().dashboardAds[index],
          ),
          options: CarouselOptions(
            height: 200,
            viewPortFraction: 0.97,
            enableInfiniteScroll: false,
            autoplayCurve: Curves.easeIn,
            enlargeCenterPage: true,
            pauseAutoPlayOnManualNavigate: true,
            onPageChanged: onPageChanged,
            autoplay: false,
          ),
        ),
      );

  static CustomCarouselBuilder showCarouselAccountBalance(
    BuildContext context, {
    void Function(int, n.CarouselPageChangedReason)? onPageChanged,
  }) =&gt;
      CustomCarouselBuilder().setArgs(
        context,
        CarouselArgs(
          itemCount: context.read&lt;DashboardLogic&gt;().allBalances.length,
          itemBuilder: (context, index, realIndex) =&gt; BalanceList(
            dto: context.read&lt;DashboardLogic&gt;().allBalances[index],
          ),
          options: CarouselOptions(
            height: 230,
            viewPortFraction: 1,
            enableInfiniteScroll: false,
            autoplayCurve: Curves.decelerate,
            enlargeCenterPage: true,
            pauseAutoPlayOnManualNavigate: true,
            onPageChanged: onPageChanged,
            autoplay: false,
          ),
        ),
      );
}
</code></pre>
<p><code>CarouselWidgetFactory</code> is the Director. It knows exactly how to configure the Builder for each specific carousel type. The knowledge of what a dashboard ads carousel looks like (height 200, viewport 0.97, easeIn curve) lives in one place. The knowledge of what an account balance carousel looks like (height 230, viewport 1, decelerate curve) lives in one place.</p>
<p>A developer who needs to add a new carousel type adds one static method to <code>CarouselWidgetFactory</code>. They don't need to understand the internals of <code>CustomCarouselBuilder</code> or <code>CustomCarousel</code>. They describe what they want using the Factory.</p>
<h3 id="heading-using-it">Using It</h3>
<pre><code class="language-csharp">class DashboardPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // the factory creates the right builder configuration
        // buildCarousel produces the final widget
        CarouselWidgetFactory.showCarouselAds(context).buildCarousel,
        const SizedBox(height: 16),
        CarouselWidgetFactory.showCarouselAccountBalance(context).buildCarousel,
      ],
    );
  }
}
</code></pre>
<p>Two carousels, two lines. The calling code has zero knowledge of carousel configuration. It doesn't know about viewport fractions, autoplay curves, or item builders. It calls the Factory, which uses the Builder, which produces the Product. Each layer knows only what it needs to know.</p>
<h2 id="heading-real-world-example-two-http-request-builder">Real World Example Two: HTTP Request Builder</h2>
<p>The carousel example shows Builder for widget construction. This second example shows it for non-UI object construction: building HTTP requests. This is a common pattern in data layers and API clients.</p>
<h3 id="heading-the-product">The Product</h3>
<pre><code class="language-csharp">class ApiRequest {
  final String url;
  final String method;
  final Map&lt;String, String&gt; headers;
  final Map&lt;String, dynamic&gt;? body;
  final Duration timeout;
  final int maxRetries;

  // private constructor — only the Builder can create ApiRequest
  ApiRequest._({
    required this.url,
    required this.method,
    required this.headers,
    this.body,
    required this.timeout,
    required this.maxRetries,
  });
}
</code></pre>
<p><code>ApiRequest</code> holds everything needed to make an HTTP request. Its private constructor ensures it's always created through the Builder, where defaults are applied and validation happens.</p>
<h3 id="heading-the-builder">The Builder</h3>
<pre><code class="language-csharp">class ApiRequestBuilder {
  String? _url;
  String _method = 'GET';
  final Map&lt;String, String&gt; _headers = {};
  Map&lt;String, dynamic&gt;? _body;
  Duration _timeout = const Duration(seconds: 30);
  int _maxRetries = 0;

  ApiRequestBuilder url(String url) {
    _url = url;
    return this;
  }

  ApiRequestBuilder method(String method) {
    _method = method;
    return this;
  }

  ApiRequestBuilder header(String key, String value) {
    _headers[key] = value;
    return this;
  }

  ApiRequestBuilder bearerToken(String token) {
    _headers['Authorization'] = 'Bearer $token';
    return this;
  }

  ApiRequestBuilder contentType(String type) {
    _headers['Content-Type'] = type;
    return this;
  }

  ApiRequestBuilder body(Map&lt;String, dynamic&gt; body) {
    _body = body;
    return this;
  }

  ApiRequestBuilder timeout(Duration timeout) {
    _timeout = timeout;
    return this;
  }

  ApiRequestBuilder withRetries(int maxRetries) {
    _maxRetries = maxRetries;
    return this;
  }

  ApiRequest build() {
    if (_url == null || _url!.isEmpty) {
      throw ArgumentError('URL is required to build an ApiRequest');
    }

    return ApiRequest._(
      url: _url!,
      method: _method,
      headers: Map.unmodifiable(_headers),
      body: _body,
      timeout: _timeout,
      maxRetries: _maxRetries,
    );
  }
}
</code></pre>
<p>Each method on <code>ApiRequestBuilder</code> sets one configuration value and returns <code>this</code>. The <code>build()</code> method is the terminal step. It validates that required fields are present and constructs the immutable <code>ApiRequest</code>.</p>
<p>Notice that <code>_method</code>, <code>_timeout</code>, and <code>_maxRetries</code> all have sensible defaults. A caller doesn't need to specify these unless they want to override the defaults. This is one of the key advantages of the Builder over a constructor: optional configuration is genuinely optional, with no null checks or default parameter workarounds.</p>
<h3 id="heading-using-it">Using It</h3>
<pre><code class="language-csharp">// a standard authenticated POST request
final createUserRequest = ApiRequestBuilder()
    .url('https://api.example.com/users')
    .method('POST')
    .bearerToken(authToken)
    .contentType('application/json')
    .body({'name': 'Oluwaseyi', 'email': 'seyi@example.com'})
    .timeout(const Duration(seconds: 15))
    .build();

// a GET request with retry logic
final getUserRequest = ApiRequestBuilder()
    .url('https://api.example.com/users/$userId')
    .bearerToken(authToken)
    .withRetries(3)
    .build();

// a request with custom headers for a third-party service
final webhookRequest = ApiRequestBuilder()
    .url('https://webhook.example.com/events')
    .method('POST')
    .header('X-API-Key', apiKey)
    .header('X-Webhook-Secret', webhookSecret)
    .contentType('application/json')
    .body(eventPayload)
    .timeout(const Duration(seconds: 5))
    .build();
</code></pre>
<p>Each request reads like a description of itself. The URL, the method, the authentication, the body, and the timeout. You can read any of these and understand immediately what kind of request it is and what it contains.</p>
<p>Compare this to calling a constructor directly:</p>
<pre><code class="language-csharp">// without Builder — hard to read, parameter order matters
final request = ApiRequest._(
  url: 'https://api.example.com/users',
  method: 'POST',
  headers: {
    'Authorization': 'Bearer $authToken',
    'Content-Type': 'application/json',
  },
  body: {'name': 'Oluwaseyi', 'email': 'seyi@example.com'},
  timeout: const Duration(seconds: 15),
  maxRetries: 0,
);
</code></pre>
<p>The constructor version requires you to know every field and its order. The Builder version lets you specify only what you need and reads like documentation.</p>
<h2 id="heading-the-builder-pattern-in-c">The Builder Pattern in C#</h2>
<p>The same pattern in C# demonstrates that this is a universal design principle, not a Dart-specific technique. C# is particularly expressive for Builder implementations because of its method chaining conventions.</p>
<h3 id="heading-http-request-builder-in-c">HTTP Request Builder in C#</h3>
<pre><code class="language-csharp">public class ApiRequest
{
    public string Url { get; }
    public string Method { get; }
    public Dictionary&lt;string, string&gt; Headers { get; }
    public object? Body { get; }
    public TimeSpan Timeout { get; }
    public int MaxRetries { get; }

    // private constructor
    private ApiRequest(
        string url,
        string method,
        Dictionary&lt;string, string&gt; headers,
        object? body,
        TimeSpan timeout,
        int maxRetries)
    {
        Url = url;
        Method = method;
        Headers = headers;
        Body = body;
        Timeout = timeout;
        MaxRetries = maxRetries;
    }

    public static ApiRequestBuilder Create() =&gt; new ApiRequestBuilder();
}

public class ApiRequestBuilder
{
    private string? _url;
    private string _method = "GET";
    private readonly Dictionary&lt;string, string&gt; _headers = new();
    private object? _body;
    private TimeSpan _timeout = TimeSpan.FromSeconds(30);
    private int _maxRetries = 0;

    public ApiRequestBuilder Url(string url)
    {
        _url = url;
        return this;
    }

    public ApiRequestBuilder Method(string method)
    {
        _method = method;
        return this;
    }

    public ApiRequestBuilder Header(string key, string value)
    {
        _headers[key] = value;
        return this;
    }

    public ApiRequestBuilder BearerToken(string token)
    {
        _headers["Authorization"] = $"Bearer {token}";
        return this;
    }

    public ApiRequestBuilder ContentType(string contentType)
    {
        _headers["Content-Type"] = contentType;
        return this;
    }

    public ApiRequestBuilder Body(object body)
    {
        _body = body;
        return this;
    }

    public ApiRequestBuilder Timeout(TimeSpan timeout)
    {
        _timeout = timeout;
        return this;
    }

    public ApiRequestBuilder WithRetries(int maxRetries)
    {
        _maxRetries = maxRetries;
        return this;
    }

    public ApiRequest Build()
    {
        if (string.IsNullOrEmpty(_url))
            throw new ArgumentException("URL is required");

        return new ApiRequest(
            _url!,
            _method,
            new Dictionary&lt;string, string&gt;(_headers),
            _body,
            _timeout,
            _maxRetries
        );
    }
}
</code></pre>
<h3 id="heading-using-it-in-c">Using It in C#</h3>
<pre><code class="language-csharp">// authenticated POST request
var createUserRequest = ApiRequest.Create()
    .Url("https://api.example.com/users")
    .Method("POST")
    .BearerToken(authToken)
    .ContentType("application/json")
    .Body(new { name = "Oluwaseyi", email = "seyi@example.com" })
    .Timeout(TimeSpan.FromSeconds(15))
    .Build();

// GET with retry logic
var getUserRequest = ApiRequest.Create()
    .Url($"https://api.example.com/users/{userId}")
    .BearerToken(authToken)
    .WithRetries(3)
    .Build();
</code></pre>
<p>The pattern is identical. The method names are capitalized following C# conventions. The <code>Build()</code> method is the terminal step. The private constructor is enforced. The result reads exactly like its Dart equivalent.</p>
<h3 id="heading-a-ui-builder-in-c-aspnet">A UI Builder in C# (ASP.NET)</h3>
<p>The Builder pattern also appears naturally in .NET for constructing complex objects in backend systems. Here's a notification builder:</p>
<pre><code class="language-csharp">public class Notification
{
    public string Title { get; }
    public string Body { get; }
    public string? ImageUrl { get; }
    public NotificationPriority Priority { get; }
    public Dictionary&lt;string, string&gt; Data { get; }
    public bool Silent { get; }

    private Notification(
        string title,
        string body,
        string? imageUrl,
        NotificationPriority priority,
        Dictionary&lt;string, string&gt; data,
        bool silent)
    {
        Title = title;
        Body = body;
        ImageUrl = imageUrl;
        Priority = priority;
        Data = data;
        Silent = silent;
    }

    public static NotificationBuilder Builder(string title, string body)
        =&gt; new NotificationBuilder(title, body);
}

public class NotificationBuilder
{
    private readonly string _title;
    private readonly string _body;
    private string? _imageUrl;
    private NotificationPriority _priority = NotificationPriority.Default;
    private readonly Dictionary&lt;string, string&gt; _data = new();
    private bool _silent = false;

    internal NotificationBuilder(string title, string body)
    {
        _title = title;
        _body = body;
    }

    public NotificationBuilder WithImage(string imageUrl)
    {
        _imageUrl = imageUrl;
        return this;
    }

    public NotificationBuilder WithPriority(NotificationPriority priority)
    {
        _priority = priority;
        return this;
    }

    public NotificationBuilder WithData(string key, string value)
    {
        _data[key] = value;
        return this;
    }

    public NotificationBuilder AsSilent()
    {
        _silent = true;
        return this;
    }

    public Notification Build() =&gt; new Notification(
        _title,
        _body,
        _imageUrl,
        _priority,
        new Dictionary&lt;string, string&gt;(_data),
        _silent
    );
}

// usage
var notification = Notification
    .Builder("New Transaction", "You received NGN 50,000")
    .WithPriority(NotificationPriority.High)
    .WithData("transaction_id", "txn_001")
    .WithData("type", "credit")
    .Build();

var silentNotification = Notification
    .Builder("Background Sync", "")
    .AsSilent()
    .WithData("sync_type", "full")
    .Build();
</code></pre>
<h2 id="heading-builder-vs-constructor-vs-factory">Builder vs Constructor vs Factory</h2>
<p>Understanding when to reach for a Builder versus a constructor or a Factory method requires understanding what problem each one solves.</p>
<p>A <strong>constructor</strong> is the right choice when the object is simple enough that all its parameters can be understood at a glance and there are few optional configurations. A User with an id, name, and email doesn't need a Builder.</p>
<p>A <strong>Factory method</strong> is the right choice when you need to control which type of object is created, or when creation requires logic that determines which concrete type to instantiate. A Repository.create() that returns either a SqlRepository or a HiveRepository based on the environment is a Factory.</p>
<p>A <strong>Builder</strong> is the right choice when the object has many optional or complex configuration parameters, when the construction requires multiple steps, when you want to prevent the creation of invalid objects by deferring construction until all required parameters are present, or when you want construction code to read clearly and be self-documenting.</p>
<p>The carousel example uses both Builder and Factory together deliberately. The Factory provides named, pre-configured entry points (showCarouselAds, showCarouselAccountBalance). The Builder handles the step-by-step construction of the complex configuration. Each pattern does its job.</p>
<h2 id="heading-when-to-use-the-builder-pattern">When to Use the Builder Pattern</h2>
<p>There are various solid use cases for the Builder pattern.</p>
<p>Use it when the object being constructed has many parameters, especially many optional ones. Named constructors with ten optional parameters are hard to read and easy to misconfigure.</p>
<p>It's also a good choice when the construction requires multiple steps that should be validated before the object is created. A Builder can enforce that required fields are present before calling <code>build()</code>.</p>
<p>Choose it when you want the construction code to be self-documenting. Method chaining with descriptive names reads like documentation. A reader can understand what is being built without knowing the internals.</p>
<p>It's helpful when you need different representations of the same object. The same Builder can be used to create a test request, a staging request, and a production request by changing which methods are called, without modifying the Request class itself.</p>
<p>And it works well when you want to prevent the creation of invalid objects. By making the Product's constructor private and putting validation in the Builder's <code>build()</code> method, you ensure that invalid objects simply can't be created.</p>
<h2 id="heading-when-not-to-use-it">When Not to Use It</h2>
<p>Avoid the Builder pattern when the object is simple and its constructor is already clear. Adding a Builder to a class with two required parameters is over-engineering that adds complexity without adding value.</p>
<p>It's also not a great choice when immutability isn't a concern and the object can be configured after creation through property setters. Some objects benefit from simple post-construction configuration rather than Builder-pattern construction.</p>
<p>And it's best to avoid it when the construction steps have strict ordering that a linear Builder can't represent. If step B absolutely must know the result of step A before it can run, a different pattern may be more appropriate.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The Builder Design Pattern addresses a problem that every developer encounters as their objects grow more complex. Constructors with many parameters become walls of values that are hard to read, hard to maintain, and easy to misconfigure. Optional parameters require null checks and default value workarounds that obscure the intent of the code.</p>
<p>The Builder separates the construction of a complex object from the object itself. Configuration accumulates step by step through descriptive method calls. Construction happens in one terminal step that validates and produces the final object. The Product's private constructor ensures that bypassing the Builder isn't possible.</p>
<p>The carousel example from a real Flutter fintech application shows this in a UI context: a Builder that accumulates widget configuration, a Factory that provides pre-configured Builder calls for specific carousel types, and a Product that can only be constructed through its Builder. Adding a new carousel type means one new Factory method. Changing carousel configuration means changing the relevant Factory method. The calling code never touches carousel internals.</p>
<p>The HTTP Request Builder shows the same pattern in a data layer context: step by step configuration through method chaining, sensible defaults for optional values, validation before construction, and an immutable Product that can't be created in an invalid state.</p>
<p>Method chaining is what makes Builder code readable. Each method call returns the Builder, enabling the next call to follow immediately. The chain reads from left to right or top to bottom like a description of what's being built. This isn't just aesthetics. It's what makes Builder code self-documenting and maintainable over time.</p>
<p>Complex object construction is a problem every codebase will encounter. The Builder pattern is how experienced engineers solve it.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Chain of Responsibility Design Pattern: Decoupling Complex Business Rules, One Handler at a Time ]]>
                </title>
                <description>
                    <![CDATA[ Every system, at some point, ends up with a function that nobody wants to touch. It starts small: a simple validation check, an if statement here, another there. Then requirements grow and more condit ]]>
                </description>
                <link>https://www.freecodecamp.org/news/chain-of-responsibility-design-pattern-decoupling-complex-business-rules/</link>
                <guid isPermaLink="false">6a88b8d9225da88c02eee6f5</guid>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Behavioral Design Pattern ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ chain of responsibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ clean code ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwaseyi Fatunmole ]]>
                </dc:creator>
                <pubDate>Fri, 21 Aug 2026 20:45:13 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/779882b9-29ec-4337-b3ab-96ccf752638c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Every system, at some point, ends up with a function that nobody wants to touch.</p>
<p>It starts small: a simple validation check, an if statement here, another there. Then requirements grow and more conditions get added. The function gets longer. Someone adds a comment that says "don't modify without reading the full thing first." The function becomes a rite of passage. New developers are warned about it during onboarding.</p>
<p>This is what happens when complex business rules pile up in one place without a deliberate structure to contain them.</p>
<p>The Chain of Responsibility pattern exists to prevent exactly this. Instead of one method that knows everything and does everything, you build a chain of focused handlers. Each handler knows one rule and checks whether the request passes its rule. If it does, the request moves forward to the next handler. If it doesn't, the chain stops right there.</p>
<p>No handler knows how long the chain is. No handler knows what comes before or after it. Each one just does its job and decides: stop here, or pass it forward.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-the-chain-of-responsibility-pattern">What is the Chain of Responsibility 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-real-world-example-one-transaction-approval-flow">Real World Example One: Transaction Approval Flow</a></p>
</li>
<li><p><a href="#heading-real-world-example-two-user-onboarding-validation">Real World Example Two: User Onboarding Validation</a></p>
</li>
<li><p><a href="#heading-what-makes-these-two-examples-interesting-together">What Makes These Two Examples Interesting Together</a></p>
</li>
<li><p><a href="#heading-when-to-use-the-chain-of-responsibility-pattern">When to Use the Chain of Responsibility 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-chain-of-responsibility-pattern">What is the Chain of Responsibility Pattern?</h2>
<p>The Chain of Responsibility is a behavioral design pattern that lets you pass a request along a chain of handlers. Each handler in the chain decides either to process the request and stop the chain, or to pass the request to the next handler.</p>
<p>The pattern gives you three things that matter in production systems.</p>
<p>First, it decouples the sender of a request from its receivers. The code that initiates a transaction validation doesn't know which handler will ultimately process it or stop it. It just starts the chain.</p>
<p>Second, it gives you a single responsibility per handler. Each handler owns exactly one business rule. When that rule changes, you modify one class. Nothing else changes.</p>
<p>Third, it makes the chain configurable. You can add, remove, or reorder handlers without touching existing handler code. A new compliance requirement becomes a new handler plugged into the chain, not a new branch inside an existing method.</p>
<h2 id="heading-the-problem-it-solves">The Problem It Solves</h2>
<p>Here's what transaction validation looks like without the pattern:</p>
<pre><code class="language-dart">void handleTransaction(Transaction transaction) {
  if (transaction.isFraud) {
    // block transaction
    return;
  }

  if (!transaction.isKycVerified) {
    // reject transaction
    return;
  }

  if (!transaction.isAccountActive) {
    // reject transaction
    return;
  }

  if (transaction.amount &lt; 50000) {
    // junior officer approval
    return;
  }

  if (transaction.amount &lt;= 200000) {
    // mid level approval
    return;
  }

  if (transaction.amount &lt;= 1000000) {
    // manager approval
    return;
  }

  // executive approval
}
</code></pre>
<p>This works today. Tomorrow your compliance team adds a credit score check. Your fraud team adds a velocity check. Your legal team adds a sanctions screening step. Your product manager adds a daily limit check.</p>
<p>Every new rule goes into this same method. The method grows to fifty lines. Then a hundred. The conditions interact in ways that are hard to reason about. Testing it requires setting up every possible combination of flags. A bug in one condition can affect every other condition below it.</p>
<p>The Chain of Responsibility pattern says: each rule gets its own handler. Chain the handlers together. The method that starts the chain doesn't need to know any of the rules. It just starts the chain and gets out of the way.</p>
<h2 id="heading-core-components">Core Components</h2>
<p>The pattern has three building blocks.</p>
<h3 id="heading-1-the-handler-interface">1. The Handler Interface</h3>
<p>This is the contract every handler in the chain must implement. It declares the method for handling a request and provides the mechanism for linking handlers together. Every concrete handler extends or implements this.</p>
<h3 id="heading-2-the-concrete-handlers">2. The Concrete Handlers</h3>
<p>These are the actual implementations. Each one owns exactly one business rule. It checks whether the request satisfies its rule. If the rule fails, it stops the chain and handles the failure. If the rule passes, it calls the next handler and passes the request forward.</p>
<h3 id="heading-3-the-chain">3. The Chain</h3>
<p>This isn't a class. It's the act of connecting handlers together using the <code>setNext</code> method. You build the chain in your composition root or your dependency injection setup. The order you connect them is the order they execute.</p>
<h2 id="heading-real-world-example-one-transaction-approval-flow">Real World Example One: Transaction Approval Flow</h2>
<p>A fintech platform processes thousands of transactions daily. Before any transaction is approved, it must pass through several validation and approval gates. Each gate is independent. Each one has a single responsibility.</p>
<p>The gates in order:</p>
<ol>
<li><p>Fraud check: is this transaction flagged as fraudulent?</p>
</li>
<li><p>KYC verification: has the user completed identity verification?</p>
</li>
<li><p>Account status: is the account active and in good standing?</p>
</li>
<li><p>Approval level: which officer tier has the authority to approve this amount?</p>
</li>
</ol>
<h3 id="heading-the-transaction-model">The Transaction Model</h3>
<pre><code class="language-dart">class Transaction {
  final num amount;
  final bool isFraud;
  final bool isKycVerified;
  final bool isAccountActive;

  const Transaction({
    required this.amount,
    required this.isFraud,
    required this.isKycVerified,
    required this.isAccountActive,
  });
}
</code></pre>
<p>The transaction model carries all the data each handler needs to make its decision. It owns the data and nothing else. No validation logic lives here.</p>
<h3 id="heading-the-handler-interface">The Handler Interface</h3>
<pre><code class="language-dart">abstract class TransactionHandler {
  TransactionHandler? _next;

  void setNext(TransactionHandler handler) {
    _next = handler;
  }

  void handle(Transaction transaction);

  void passToNext(Transaction transaction) {
    if (_next != null) {
      _next!.handle(transaction);
    } else {
      print('End of chain reached with no handler stopping the transaction');
    }
  }
}
</code></pre>
<p><code>TransactionHandler</code> is the contract every handler implements.</p>
<p><code>_next</code> is nullable because the last handler in the chain has no next handler. Making it nullable and checking before calling prevents a null pointer crash at the end of the chain.</p>
<p><code>setNext</code> connects one handler to the next. You call this when building the chain.</p>
<p><code>passToNext</code> is a helper method that every concrete handler calls when its rule passes. It checks whether a next handler exists before calling it. If we reach the end of the chain without any handler stopping the transaction, we log it. In a real system, this would trigger an alert because it means the chain wasn't configured correctly.</p>
<h3 id="heading-the-concrete-handlers">The Concrete Handlers</h3>
<pre><code class="language-dart">class FraudHandler extends TransactionHandler {
  @override
  void handle(Transaction transaction) {
    if (transaction.isFraud) {
      print('Transaction blocked: fraud detected');
      return;
    }
    print('Fraud check passed');
    passToNext(transaction);
  }
}
</code></pre>
<p><code>FraudHandler</code> is the first gate. If the transaction is flagged as fraudulent, it prints a rejection message and returns. The chain stops here. No other handler sees this transaction. If the fraud check passes, it calls <code>passToNext</code> and the transaction moves to the next handler.</p>
<pre><code class="language-dart">class KycHandler extends TransactionHandler {
  @override
  void handle(Transaction transaction) {
    if (!transaction.isKycVerified) {
      print('Transaction blocked: KYC verification incomplete');
      return;
    }
    print('KYC check passed');
    passToNext(transaction);
  }
}
</code></pre>
<p><code>KycHandler</code> checks whether the user has completed identity verification. If they haven't, the chain stops. If they have, the transaction moves forward. This handler knows nothing about fraud checks. It knows nothing about account status. It owns one rule.</p>
<pre><code class="language-dart">class AccountHandler extends TransactionHandler {
  @override
  void handle(Transaction transaction) {
    if (!transaction.isAccountActive) {
      print('Transaction blocked: account is not active');
      return;
    }
    print('Account status check passed');
    passToNext(transaction);
  }
}
</code></pre>
<p><code>AccountHandler</code> checks account status. Same pattern, one rule: stop or pass.</p>
<pre><code class="language-dart">class ApprovalHandler extends TransactionHandler {
  @override
  void handle(Transaction transaction) {
    if (transaction.amount &lt; 50000) {
      print('Approved by Junior Officer — amount: ${transaction.amount}');
      return;
    }

    if (transaction.amount &lt;= 200000) {
      print('Approved by Mid-Level Officer — amount: ${transaction.amount}');
      return;
    }

    if (transaction.amount &lt;= 1000000) {
      print('Approved by Manager — amount: ${transaction.amount}');
      return;
    }

    print('Escalated to Executive Approval — amount: ${transaction.amount}');
    passToNext(transaction);
  }
}
</code></pre>
<p><code>ApprovalHandler</code> is the final gate. It routes the transaction to the appropriate approval tier based on amount. Transactions below 50,000 are approved by a junior officer. Up to 200,000 go to a mid-level officer. Up to 1,000,000 go to a manager. Above that, the transaction is escalated further.</p>
<p>Note that this handler can still call <code>passToNext</code> if the amount exceeds the manager threshold, allowing you to add an executive handler to the chain later without touching <code>ApprovalHandler</code>.</p>
<h3 id="heading-building-and-running-the-chain">Building and Running the Chain</h3>
<pre><code class="language-dart">void main() {
  // create the handlers
  final fraud = FraudHandler();
  final kyc = KycHandler();
  final account = AccountHandler();
  final approval = ApprovalHandler();

  // build the chain
  fraud.setNext(kyc);
  kyc.setNext(account);
  account.setNext(approval);

  // test with a fraudulent transaction
  print('Test 1: Fraudulent Transaction');
  final fraudulentTransaction = Transaction(
    amount: 100000,
    isFraud: true,
    isKycVerified: true,
    isAccountActive: true,
  );
  fraud.handle(fraudulentTransaction);

  // test with unverified KYC
  print('Test 2: KYC Not Verified');
  final unverifiedTransaction = Transaction(
    amount: 50000,
    isFraud: false,
    isKycVerified: false,
    isAccountActive: true,
  );
  fraud.handle(unverifiedTransaction);

  // test with a valid transaction
  print('Test 3: Valid Transaction');
  final validTransaction = Transaction(
    amount: 150000,
    isFraud: false,
    isKycVerified: true,
    isAccountActive: true,
  );
  fraud.handle(validTransaction);

  // test with a high value transaction
  print('Test 4: Executive Level Transaction');
  final executiveTransaction = Transaction(
    amount: 2000000,
    isFraud: false,
    isKycVerified: true,
    isAccountActive: true,
  );
  fraud.handle(executiveTransaction);
}
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">Test 1: Fraudulent Transaction
Transaction blocked: fraud detected

Test 2: KYC Not Verified
Fraud check passed
Transaction blocked: KYC verification incomplete

Test 3: Valid Transaction
Fraud check passed
KYC check passed
Account status check passed
Approved by Mid-Level Officer — amount: 150000

Test 4: Executive Level Transaction
Fraud check passed
KYC check passed
Account status check passed
Escalated to Executive Approval — amount: 2000000
End of chain reached with no handler stopping the transaction
</code></pre>
<p>Test 1 stops at the first handler. Test 2 passes fraud but stops at KYC. Test 3 passes all validation handlers and gets routed to the correct approval tier. Test 4 exceeds the manager threshold and gets escalated.</p>
<p>Notice that the calling code always starts from <code>fraud.handle(transaction)</code>. It doesn't know how many handlers exist. It doesn't know which handler will stop the chain. And it doesn't know what the approval tiers are. It just hands the transaction to the first handler and the chain takes over.</p>
<p>When your compliance team adds a User Indemnity check next month, you create a UserIdemnityCheck, add it to the chain, and nothing else changes:</p>
<pre><code class="language-dart">final indemnity = UserIndemnityStatus();

fraud.setNext(indemnity);
indemnity.setNext(kyc);
kyc.setNext(account);
account.setNext(approval);
</code></pre>
<p>One new class and one updated chain setup. Every existing handler untouched.</p>
<h2 id="heading-real-world-example-two-user-onboarding-validation">Real World Example Two: User Onboarding Validation</h2>
<p>A user fills in a registration form and submits it. Before the account is created, the request must pass through several validation steps. If any step fails, the user gets a specific error explaining exactly what went wrong.</p>
<p>The steps in order:</p>
<ol>
<li><p>Email validation: is the email format valid?</p>
</li>
<li><p>Password strength: does the password meet security requirements?</p>
</li>
<li><p>Age verification: is the user old enough to register?</p>
</li>
<li><p>Duplicate account check: does an account already exist with this email?</p>
</li>
<li><p>Account creation: all checks passed, create the account</p>
</li>
</ol>
<h3 id="heading-the-registration-request-model">The Registration Request Model</h3>
<pre><code class="language-dart">class RegistrationRequest {
  final String email;
  final String password;
  final int age;

  const RegistrationRequest({
    required this.email,
    required this.password,
    required this.age,
  });
}
</code></pre>
<h3 id="heading-the-handler-interface">The Handler Interface</h3>
<pre><code class="language-dart">abstract class RegistrationHandler {
  RegistrationHandler? _next;

  void setNext(RegistrationHandler handler) {
    _next = handler;
  }

  void handle(RegistrationRequest request);

  void passToNext(RegistrationRequest request) {
    if (_next != null) {
      _next!.handle(request);
    }
  }
}
</code></pre>
<p>This is the same structure as before. Nullable next, SetNext to build the chain, and PassToNext to move the request forward.</p>
<h3 id="heading-the-concrete-handlers">The Concrete Handlers</h3>
<pre><code class="language-dart">class EmailValidationHandler extends RegistrationHandler {
  @override
  void handle(RegistrationRequest request) {
    final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');

    if (!emailRegex.hasMatch(request.email)) {
      print('Registration failed: invalid email format — ${request.email}');
      return;
    }

    print('Email validation passed');
    passToNext(request);
  }
}
</code></pre>
<p><code>EmailValidationHandler</code> checks the email format using a regex. If the format is invalid, it stops the chain immediately with a specific message. If it's valid, the request moves forward.</p>
<pre><code class="language-dart">class PasswordStrengthHandler extends RegistrationHandler {
  @override
  void handle(RegistrationRequest request) {
    final password = request.password;
    final hasMinLength = password.length &gt;= 8;
    final hasUppercase = password.contains(RegExp(r'[A-Z]'));
    final hasNumber = password.contains(RegExp(r'[0-9]'));
    final hasSpecialChar = password.contains(RegExp(r'[!@#\$%^&amp;*]'));

    if (!hasMinLength || !hasUppercase || !hasNumber || !hasSpecialChar) {
      print('Registration failed: password does not meet security requirements');
      print('Requirements: 8+ characters, uppercase, number, special character');
      return;
    }

    print('Password strength check passed');
    passToNext(request);
  }
}
</code></pre>
<p><code>PasswordStrengthHandler</code> enforces four password rules in one place. Minimum length, at least one uppercase letter, at least one number, and at least one special character. If any of these fail, the user gets a clear message explaining all the requirements. If all pass, the request moves forward.</p>
<pre><code class="language-dart">class AgeVerificationHandler extends RegistrationHandler {
  final int minimumAge;

  AgeVerificationHandler({this.minimumAge = 18});

  @override
  void handle(RegistrationRequest request) {
    if (request.age &lt; minimumAge) {
      print('Registration failed: user must be at least $minimumAge years old');
      return;
    }

    print('Age verification passed');
    passToNext(request);
  }
}
</code></pre>
<p><code>AgeVerificationHandler</code> checks the user's age against a minimum threshold. Notice that this handler accepts the minimum age as a constructor parameter. This makes it configurable without modifying the class. If the minimum age requirement changes from 18 to 16 for a specific product, you just pass a different value when building the chain.</p>
<pre><code class="language-dart">class DuplicateAccountHandler extends RegistrationHandler {
  final Set&lt;String&gt; existingEmails;

  DuplicateAccountHandler({required this.existingEmails});

  @override
  void handle(RegistrationRequest request) {
    if (existingEmails.contains(request.email)) {
      print('Registration failed: an account already exists with ${request.email}');
      return;
    }

    print('Duplicate account check passed');
    passToNext(request);
  }
}
</code></pre>
<p><code>DuplicateAccountHandler</code> checks whether an account already exists with the provided email. In a real system, this would call a repository or database. Here we use a Set of existing emails to keep the example focused on the pattern.</p>
<pre><code class="language-dart">class AccountCreationHandler extends RegistrationHandler {
  @override
  void handle(RegistrationRequest request) {
    print('All validation passed');
    print('Creating account for: ${request.email}');
    // call account creation service
    print('Account created successfully');
  }
}
</code></pre>
<p><code>AccountCreationHandler</code> is the final handler. It only runs if every previous handler passed the request forward. By the time execution reaches here, the request has been validated on every dimension. This handler simply creates the account.</p>
<h3 id="heading-building-and-running-the-chain">Building and Running the Chain</h3>
<pre><code class="language-dart">void main() {
  final existingEmails = {'existing@seyi.com', 'taken@seyi.com'};

  // create the handlers
  final emailValidation = EmailValidationHandler();
  final passwordStrength = PasswordStrengthHandler();
  final ageVerification = AgeVerificationHandler(minimumAge: 18);
  final duplicateCheck = DuplicateAccountHandler(existingEmails: existingEmails);
  final accountCreation = AccountCreationHandler();

  // build the chain
  emailValidation.setNext(passwordStrength);
  passwordStrength.setNext(ageVerification);
  ageVerification.setNext(duplicateCheck);
  duplicateCheck.setNext(accountCreation);

  // test with invalid email
  print('Test 1: Invalid Email');
  emailValidation.handle(RegistrationRequest(
    email: 'notanemail',
    password: 'SecureP@ss1',
    age: 25,
  ));

  // test with weak password
  print('Test 2: Weak Password');
  emailValidation.handle(RegistrationRequest(
    email: 'user@example.com',
    password: 'weak',
    age: 25,
  ));

  // test with underage user
  print('Test 3: Underage User');
  emailValidation.handle(RegistrationRequest(
    email: 'young@example.com',
    password: 'SecureP@ss1',
    age: 16,
  ));

  // test with duplicate account
  print('Test 4: Duplicate Account');
  emailValidation.handle(RegistrationRequest(
    email: 'existing@example.com',
    password: 'SecureP@ss1',
    age: 25,
  ));

  // test with valid registration
  print('Test 5: Valid Registration');
  emailValidation.handle(RegistrationRequest(
    email: 'newuser@example.com',
    password: 'SecureP@ss1',
    age: 25,
  ));
}
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">Test 1: Invalid Email
Registration failed: invalid email format — notanemail

Test 2: Weak Password
Email validation passed
Registration failed: password does not meet security requirements
Requirements: 8+ characters, uppercase, number, special character

Test 3: Underage User
Email validation passed
Password strength check passed
Age verification passed
Registration failed: user must be at least 18 years old

Test 4: Duplicate Account
Email validation passed
Password strength check passed
Age verification passed
Duplicate account check passed
Registration failed: an account already exists with existing@example.com

Test 5: Valid Registration
Email validation passed
Password strength check passed
Age verification passed
Duplicate account check passed
All validation passed
Creating account for: newuser@example.com
Account created successfully
</code></pre>
<p>Each test stops at exactly the right handler. Each failure message is specific. The valid registration flows through all five handlers and creates the account.</p>
<p>When a new requirement arrives, say a phone number verification step before account creation, you create a <code>PhoneVerificationHandler</code> and plug it into the chain between duplicate check and account creation. Five existing handlers remain completely untouched.</p>
<h2 id="heading-what-makes-these-two-examples-interesting-together">What Makes These Two Examples Interesting Together</h2>
<p>The transaction flow and the onboarding flow look similar on the surface, but they represent two different ways the pattern gets used in production.</p>
<p>The transaction flow combines validation handlers and routing handlers in one chain. Fraud, KYC, and account handlers are gates. The approval handler is a router. The chain validates first, then routes. This is common in payment and compliance systems where every transaction must pass multiple independent checks before being directed to the appropriate authority.</p>
<p>The onboarding flow is a pure validation chain. Every handler is a gate. The final handler is the action that runs only if all gates pass. This is common in form processing, API request validation, and any multi-step verification flow.</p>
<p>Both use the same pattern and are configured the same way. The difference is just in what the handlers do when they let the request through.</p>
<h2 id="heading-when-to-use-the-chain-of-responsibility-pattern">When to Use the Chain of Responsibility Pattern</h2>
<p>Use it when you have a request that must pass through multiple independent checks or processing steps.</p>
<p>It's also a good fit when the number of checks or their order might change over time. Adding a new step or reordering existing steps should not require modifying existing handler code.</p>
<p>It works well when each check or processing step has genuinely independent logic. If the steps are deeply interdependent and need to share a lot of state, a single class might be cleaner.</p>
<p>It does well when you want each step to be independently testable. With the chain pattern, testing <code>FraudHandler</code> means creating one handler, calling handle with a transaction, and checking the output. No other handler is involved.</p>
<p>And it's great when different configurations of the chain might be needed in different contexts. A junior officer's system might have a shorter chain than an executive's system. The same handlers, configured differently.</p>
<h2 id="heading-when-not-to-use-it">When Not to Use It</h2>
<p>Avoid the pattern when you only have one or two checks. The overhead of defining an abstract class and multiple concrete classes is not worth it for simple validation.</p>
<p>It's also not the best when the order of processing steps is fixed and will never change. If the chain will always be the same, a simpler sequential function call might be clearer.</p>
<p>Don't use it when handlers need to communicate results back to each other. The pattern works best when each handler makes an independent decision. If Handler B needs to know what Handler A found, consider a different approach.</p>
<p>And it's not a good choice when you need guaranteed execution of all handlers regardless of earlier results. The Chain of Responsibility stops when a handler handles the request. If you need all steps to always run, a middleware pipeline or decorator pattern might suit you better.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The Chain of Responsibility pattern solves the problem that every growing system eventually faces. Business rules accumulate and validation logic expands. A method that started as ten lines becomes a hundred. The conditions interact in ways nobody fully understands anymore. Nobody wants to touch it.</p>
<p>The pattern gives you a way out. Each business rule gets its own handler. Each handler owns one responsibility and makes one decision: stop here, or pass it forward. The chain is built once in the configuration layer. The handlers never need to know about each other.</p>
<p>In the transaction approval flow, adding a new compliance rule means one new handler class. In the onboarding flow, adding a new verification step means one new handler class. In both cases, nothing else changes.</p>
<p>That's the promise of the pattern. Complexity that grows by addition, not by modification. Business rules that are isolated, testable, and replaceable. A system that can absorb new requirements without accumulating more debt every time.</p>
<p>Applying this behavioral pattern helps to bring some level of organization and scalability to your codes and makes it easy to manage based on further business rules to come in the nearest future.</p>
<p>Happy Coding!!</p>
 ]]>
                </content:encoded>
            </item>
        
            <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;" 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>
        
    </channel>
</rss>
