Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is "static Domain helper class" Ambient Context Anti-Pattern?

As defined in the Manning book by Steven van Deursen and Mark Seemann, "Dependency Injection Principles, Practices, and Patterns" (which I recommend as must-read), there is one specific anti-pattern that was interesting to me, and that is Ambient Context Anti-Pattern. It is also described here. General issue is that if you put some volatile dependency behind the static class, you are coupling multiple parts of code-base to that volatile dependency.

My confusion begins with definition of volatile dependency. One of the definitions of volatile dependency is that this dependency is still under development or not developed. So my question is: If that dependency is some well defined domain knowledge, such as some domain-specific email validator, placed in Domain layer, and it is still under some code-change, is it considered an Anti-Pattern if we put it behind the static class and use it all over the code-base? (We are talking about Onion Architecture where Domain is in the center and everything is referencing toward the Domain layer)

Edit 1

Here is an simplified example Domain class:

public class EmailValidationService
{
   private static readonly Regex _emailValidationRegex = /*some regex*/;

   public static EmailValidationStatus IsValid(string email)
   {
     var isValid = _emailValidationRegex.Match(email).Length > 0; ;

     return isValid;
   }
}

Cosumers of this validator are placed in multiple layers, such as user input validations, file import validations etc. Here are two simplified consumers:

public class RawDataImporter{

    private Employee ProcessEmployee(EmployeeRowData data)
    {
        if (string.IsNullOrEmpty(data.Identifier)) { return new InvalidEmployee(); }
    
        if (EmailValidationService.IsValid(data.Identifier) { return new Employee(data.Identifier); }
    
    }
}

public class EmailAttribute :
        ValidationAttribute
{
    public override bool IsValid(object value)
    {
        var status = EmailValidationService.IsValid((string)value);
        if (status.IsValid)
        {
            return true;
        }

        return false;
    }
}
like image 723
Marko Krstic Avatar asked Dec 01 '25 20:12

Marko Krstic


1 Answers

One of the definitions of volatile dependency is that this dependency is still under development or not developed.

To my horror, this statement in the book (I'm the coauthor) isn't really elaborated on. This is what the book says:

A Dependency should be considered volatile if [...] [t]he Dependency doesn’t yet exist, or is still in development.

That's it. There's no more information given. The first edition, fortunately, contains a bit more information. In that edition, this previous statement is followed by the following sentence:

The obvious symptom of such dependencies is the inability to do parallel development.

I had to dig to find out why we removed this, but feedback from our proofreaders indicated that this second sentence was a bit confusing. But instead of improving that line, we seem to have (accidentally?) removed it. Keeping the notion of "parallel development" in that bullet point, would have been useful, because it refers back to section 1.2.2 that notes the following about parallel development:

When a software development project grows to a certain size, it becomes necessary to have multiple developers work in parallel on the same code base. At a larger scale, it’s even necessary to separate the development team into multiple teams of manageable sizes. Each team is often assigned responsibility for an area of the overall application. To demarcate responsibilities, each team develops one or more modules that will need to be integrated into the finished application. Unless the areas of each team are truly independent, some teams are likely to depend on functionality developed by other teams.

Let me retry to correct this book's omission and elaborate on why dependencies still under development could be considered Volatile.

When developing an application with more people, non-developed functionally implicitly becomes volatile when its non-existence or its inability to develop it in parallel hinders development. The bigger the team, or the more teams working on a piece of software, the more these under-development parts can hinder the developer performance.

Most developers who have worked on code bases that are developed in parallel by multiple teams have experienced that you are more often blocked by others, experience more merge conflicts, see more errors caused by incorrectly merged code, more code and tests break by changes of code you depend on. These problems can be, in part, caused by taking hard dependencies on other classes (although, admittedly, there are many more reasons why this can happen). This risk can be mitigated by hiding these more-risky parts behind abstractions. This means that the structure and size of your organization can have an impact on the design and structure of your code base. You should choose a design that is most cost efficient (in the long run).

From this statement you can conclude that whether something is a Volatile or a Stable dependency is not always a hard science. This is quite different from dependencies containing nondeterministic behavior; they are always Volatile.

NOTE: I'll take a look whether we can put some of this information in the book's errata. Updating the text in the next print is unfortunately not possible, because it would cause cascading (sweeping) changes to the page layout and numbering, which our publisher does not allow.

If that dependency is some well defined domain knowledge, such as some domain-specific email validator, placed in Domain layer, and it is still under some code-change, is it considered an Anti-Pattern if we put it behind the static class and use it all over the code-base?

Your e-mail validator is a nice example of something that I would put in the gray category; it could go either way. Its behavior is deterministic, because: given the same input it would always yield the same output. Another question you might ask, though, is whether you need loose coupling here? This would happen if you wanted to be able to replace one e-mail validator with another, or perhaps more generally, would the addition of more validation checks require the e-mail validator's consumers to change? That would be a good indication that you're -again- talking about a Volatile Dependency.

Now let's get back to your question about the Ambient Context anti-pattern. Assume that you figured this validation is a Volatile Dependency; that still doesn't make it necessarily an Ambient Context. One of the characteristics of an Ambient Context is that the behavior can be replaced. Look, for instance, at following example that demonstrates an Ambient Context; it is a simplified version of listing 5.11 of the book:

public static class TimeProvider
{
    public static ITimeProvider Current { get; set; }
        = new DefaultTimeProvider();
}

The static TimeProvider class gives access to the ITimeProvider dependency. The fact that you wish to be able to replace the time-provider behavior is a really-really strong indication that this a Volatile Dependency.

Without the ability to replace (in one way or another) the Volatile Dependency with another dependency, there is no Ambient Context. Your validator would become an Ambient Context, for instance, when you would allow the underlying Regex to be replaced, as demonstrated here:

public class EmailValidationService
{
   // Ambient Context
   public static Regex EmailValidationRegex { get; set; } = /*some regex*/;

   public static EmailValidationStatus IsValid(string email)
   {
     var isValid = EmailValidationRegex.Match(email).Length > 0; ;

     return isValid;
   }
}

Regex is a concrete class -not an abstraction-, but that doesn't change the fact that it is still an example of an Ambient Context.

Instead, in case you determined that this static class consists of volatile behavior, and this class is directly called by consumers, the book considers this instead an implementation of the Control Freak anti-pattern:

The Control Freak anti-pattern occurs every time you depend on a Volatile Dependency in any place other than a Composition Root. It’s a violation of the Dependency Inversion Principle [...]

like image 96
Steven Avatar answered Dec 03 '25 12:12

Steven



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!