The Cynefin framework with Domain-driven Design

turalsuleymani

Tural Suleymani

Posted on November 25, 2024

The Cynefin framework with Domain-driven Design

I first met the term ‘Cynefin’ when learning Domain-driven Design. I have seen multiple mentions of this framework, but not all of them were related to software development, especially DDD. The thing here is that the Cynefin framework is not directly related to the software industry. It is more than that. In simple terms, the Cynefin framework, developed by Dave Snowden back in 1999, is a decision-making and sense-making tool designed to help leaders and organizations deal with complex problems and make better decisions.

I know you may have tried to learn it before this article, and because of its complexity and complex explanations, you may fail. This article aims to try to explain it as simply as possible.

If you learn DDD, you may notice that understanding the domain is important and trying to understand how complex the domain is really complex.

Cynefin framework and domain-driven design(DDD) address complexity, deal with complex systems, and aim to provide frameworks for managing uncertainty. Cynefin categorizes problems to determine how to address them. On the other hand, DDD focuses on managing complexity in business domains by modeling them effectively.

Using Cynefin, we can identify which domain(s) in a system are Complex or Complicated, influencing how DDD might be applied.

Cynefin’s primary purpose is to guide decision-makers in understanding the context of a situation and applying appropriate strategies.

So far, so good. Now, we have some basic understanding of the Cynefin framework, and it is time to dive into the five realms of the framework.

cynefin framework

The Cynefin framework categorizes problems into five domains, each representing a different type of system or context. It doesn’t matter if you’re a developer, manager, or a non-IT guy, throughout the working process you always deal with some problems. While the Cynefin framework helps categorize problems and provides general guidance on how to approach them, it does not describe the exact “steps of operations” to deal with issues. Instead, it offers principles or strategies adapted to the characteristics of each domain. You should remember that Cynefin is about adapting strategies to fix the context, not about following strict, predefined steps. While a “step-by-step” procedure might work in the Clear domain of Cynefin, in the Chaotic domain, quick action to stabilize the situation is prioritized.

The first domain in Cynefin is the Clear/Obvious/Simple domain. This domain typically deals with well-defined, predictable problems where ”best practices” are sufficient. In contrast, DDD is designed for complex and complicated domains, where understanding and modeling the domain requires collaboration, expertise, and iterative refinement. The main approach in this domain is “sense-categorize-respond”.

Sense: Before categorizing the problem, we observe and recognize the situation to identify the problem. In the Clear domain, the situation is easily understandable because it fits into established patterns or rules. You don’t need to apply deep analysis or experimentation. These sorts of problems don’t even require a domain expert. A simple example may be an email validation. We observe that the system needs to validate an email address provided by a user. The problem is really simple: ensure the email format meets a standard pattern.

Categorize: We have a category of “solutions” here, and we match the observed situation to a known category or pattern. We choose the corresponding best practice or standard procedure to address the issue. In terms of email validation, this problem is a well-understood problem and has established patterns and best practices. We need to categorize the issue as “input validation using predefined standard” and apply a regular expression or use a library function for validation.

Respond: Based on categorization, we need to implement the pre-defined solution. There is no need for innovative thinking- just follow the existing rules or steps. In terms of email validation, use the best practice for email validation(Simple domain). Implement a regex or library-based validator in the code and provide feedback to the user if the email doesn’t match the expected format.

using System;
using System.Text.RegularExpressions;class Program
{
    static void Main()
    {
        Console.WriteLine("Enter an email address:");
        string email = Console.ReadLine();if (IsValidEmail(email))
        {
            Console.WriteLine("The email is valid.");
        }
        else
        {
            Console.WriteLine("The email is invalid.");
        }
    }

    static bool IsValidEmail(string email)
    {
        if (string.IsNullOrWhiteSpace(email))
        {
            return false;
        }
        // Simple email validation regex
        string emailPattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";
        return Regex.IsMatch(email, emailPattern);
    }
}
Enter fullscreen mode Exit fullscreen mode

A real-world example of a clear-domain problem involving a car wash where the water supply runs out might look like this:

Sense: Recognize the problem: The car wash system detects that the water supply has been depleted (e.g., through a water level sensor or manual observation). The cause is obvious and easily identifiable: there is no water left to continue operations.

Example : The water tank for the car wash is empty, and the system cannot proceed with washing cars.

Categorize: Match the situation to a known category: Categorize the problem as “resource depletion.”

Determine the standard operating procedure (SOP) for refilling the water tank. This is a routine task with established steps.

Example : Check the water tank level. Confirm it needs refilling. Identify the nearest water source or supplier for replenishment.

Respond: Apply the known solution: Follow the established procedure to resolve the problem. Notify the operator to refill the tank or automatically trigger the water refilling mechanism if the system is automated.

Example : Refill the water tank from the designated source. Ensure the tank is full. Resume car wash operations once the water is replenished.

The next domain is the Complicated domain. The main approach here is “sense-analyze-respond”. A complicated domain represents situations where problems are more challenging than in the Clear domain, but they are still solvable with expertise and analysis. The cause-and-effect relationships exist, but they are not immediately obvious and require deeper investigation.

Any characteristics of the Complicated Domain? Of course:

  1. We don’t have best practices. Instead of this, we have multiple solutions -> good practices.
  2. It requires expertise and specialists.
  3. The system is stable enough to allow analysis, but understanding it requires more effort than simple categorization.
  4. The Recommended Approach for Complicated domains is Sense-Analyze-Respond.

Sense: Gather data and observe the situation to identify the problem.
Analyze: Use expertise, tools, or frameworks to investigate the cause-and-effect relationships. Explore different options and evaluate their potential outcomes.
Respond: Implement the solution deemed most effective based on the analysis.
In terms of the car domain, say we need to design a car’s engine. To build a car engine, we need expertise in engineering, as there are many variables to consider, like fuel efficiency, durability, emissions, etc.

Differences between domains:

cynefin framework domain differences

In terms of DDD, we can/should apply DDD for such types of problems. A complicated domain requires expert input to understand and analyze problems when DDD centers on domain experts collaborating with developers to create models that accurately represent the business domain. Long story short, the relationship between the Complicated domain and DDD lies in their shared focus on dealing with complex problem-solving scenarios.The both:

  1. Emphasize the need for expert input.
  2. In the complicated domain, good practices guide decision-making, and solutions are not obvious but discoverable. By its nature, DDD introduces structured methodologies to tackle complexity.
  3. The iterative analysis in the complicated domain aligns well with DDD’s emphasis on continuously evolving the domain model.
  4. In a complicated domain, experts might segment the problem into understandable parts to apply solutions effectively. Similarly, DDD introduces bounded contexts to isolate complexities within specific subdomains. As an example, the tax domain can be a complicated domain example in C#. Besides the language, we need to understand the tax domain. It means you need to involve someone from this domain to understand and solve the problem:
using System;
using System.Collections.Generic;
class Program
{
    static void Main()
    {
        Console.WriteLine("Enter your income:");
        decimal income = decimal.Parse(Console.ReadLine());
        Console.WriteLine("Enter the number of dependents:");
        int dependents = int.Parse(Console.ReadLine());
        Console.WriteLine("Are you self-employed? (yes/no):");
        bool isSelfEmployed = Console.ReadLine()?.Trim().ToLower() == "yes";
        var taxCalculator = new TaxCalculator();
        decimal tax = taxCalculator.CalculateTax(income, dependents, isSelfEmployed);
        Console.WriteLine($"Your calculated tax is: {tax:C}");
    }
}
public class TaxCalculator
{
    private readonly List<ITaxRule> _taxRules;
    public TaxCalculator()
    {
        _taxRules = new List<ITaxRule>
        {
            new BasicIncomeTaxRule(),
            new DependentDeductionRule(),
            new SelfEmploymentTaxRule()
        };
    }
    public decimal CalculateTax(decimal income, int dependents, bool isSelfEmployed)
    {
        decimal tax = 0;
        foreach (var rule in _taxRules)
        {
            tax += rule.Apply(income, dependents, isSelfEmployed);
        }
        return Math.Max(tax, 0); // Ensure tax is not negative
    }
}
public interface ITaxRule
{
    decimal Apply(decimal income, int dependents, bool isSelfEmployed);
}
public class BasicIncomeTaxRule : ITaxRule
{
    public decimal Apply(decimal income, int dependents, bool isSelfEmployed)
    {
        // Example: Flat 20% tax for incomes above 50,000
        if (income > 50_000)
        {
            return (income - 50_000) * 0.20M;
        }
        return 0;
    }
}
public class DependentDeductionRule : ITaxRule
{
    public decimal Apply(decimal income, int dependents, bool isSelfEmployed)
    {
        // Example: $2,000 deduction per dependent
        return -dependents * 2000;
    }
}
public class SelfEmploymentTaxRule : ITaxRule
{
    public decimal Apply(decimal income, int dependents, bool isSelfEmployed)
    {
        if (isSelfEmployed)
        {
            // Example: 15% self-employment tax on all income
            return income * 0.15M;
        }
        return 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

The third domain is the** Complex domain.** The complex domain involves systems where the cause-and-effect relationship can only be understood in retrospect. Solutions arise through experimentation, probing, and feedback loops. There are no best and good practices. You must experiment with small changes to understand how the system behaves and adapt accordingly. Collaboration is very important in identifying emerging patterns. The main approach here is “probe-sense-respond”. Because relationships between actions and outcomes are not directly observable or predictable. The solution evolves over time through experimentation and feedback. The other important fact is: that complex domains often require inputs from multiple disciplines to approach solutions. You must be careful because a small change in one part of the system can lead to significant or unpredictable effects elsewhere.

Long story short, here is the most understandable approach:

  1. Introduce small, safe-to-fail experiments to observe how the system behaves.
  2. Observe and analyze the outcomes of the experiments.
  3. Adapt and take action based on what you’ve learned from probing and sensing. If you think about complexity in DDD, it addresses it through Bounded contexts, clearly defined Ubiquitous language inside the context, and applying tactical design inside bounded context. Breaking the domain into Bounded Contexts helps teams focus on smaller, more manageable areas, enabling gradual understanding and refinement. DDD does not assume that all requirements or domain insights can be known upfront, making it a natural fit for complex systems. In the end, DDD enables teams to build systems that adapt and thrive in unpredictable environments.

clear differences in cynefin framework

The chaos domain in the Cynefin Framework represents situations where cause-and-effect relationships are completely unclear and immediate. Decisive action is required to stabilize the system. Unlike other domains, the chaotic domain is marked by extreme unpredictability and the need for rapid response to avoid further damage. Our main approach is “act-sense-respond”. It’s impossible to identify a pattern or determine how actions influence outcomes.

There’s no time for analysis or experimentation; No way to apply DDD. Quick action is necessary to bring the situation under control.

Our main purpose is to move the situation into a more manageable domain (e.g., complex or complicated). Without decisive action, the situation may spiral out of control.

While DDD is not inherently designed for chaotic environments, aspects of its principles can help navigate and transition chaos into manageable domains.

While DDD typically operates in complex and complicated domains, its principles can indirectly support efforts to manage and recover from chaos:

In chaotic situations, maintaining focus on a system’s Core Domain is critical. DDD emphasizes prioritizing the core business logic, ensuring that during a chaotic event, resources and actions target the most business-critical systems or processes.

Bounded Contexts help isolate parts of a system. In chaos, this separation enables teams to address and stabilize affected areas without disrupting unrelated parts.

Chaos often leads to miscommunication under pressure. DDD’s emphasis on a Ubiquitous Language fosters clear communication among developers, domain experts, and other stakeholders, ensuring alignment during chaotic responses.

DDD mostly applies event-driven architecture, which supports handling chaos. Systems designed with event sourcing and CQRS can respond to unexpected situations more flexibly. Also, DDD principles can guide the transition from chaos to the complex domain, where experimentation and adaptive solutions are possible. In the end, After an initial stabilization of chaotic failures, teams can use DDD techniques like Event Storming to model and understand emergent patterns, enabling iterative improvement.

**The disorder domain **in the Cynefin framework represents situations where it’s unclear which of the other four domains applies. This domain is marked by confusion and lack of clarity.

When the domain is poorly understood, and there is no agreement on the business goals or technical approach, you may end up with a disordered domain in the context of DDD. Well, then, how can DDD help in the disorder domain?

The disorder domain then arises from miscommunication and inconsistent terminology. UL will guide you in fixing this problem. Also, trying to solve too many problems at once or overlapping responsibilities can be a good “reason” to be in the disorder domain. From the DDD perspective, splitting the domain into well-defined Bounded Contexts can help us to move from disorder to other domains.

The other reason for being in the disorder domain is the lack of clarity on which parts of the domain are most important. In the context of DDD, strategic design helps identify core domains to prioritize and focus efforts where they bring the most value.

Conclusion
The Cynefin Framework and Domain-Driven Design (DDD) complement each other by providing a structured way to navigate complexity in software systems and business domains. While Cynefin offers a lens to categorize problems and determine suitable decision-making approaches, DDD provides concrete practices for solving problems in complicated and complex domains. Together, they form a powerful toolkit for building robust, adaptable, and business-aligned software systems.

💖 💪 🙅 🚩
turalsuleymani
Tural Suleymani

Posted on November 25, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related