1

We use domain objects to encapsulate business logic. Sometimes, we need to change the behavior of an entity due to new system requirements. A common approach is to modify the method or property by adding an if statement that checks a feature flag:

public class Entity : IEntity
{
    public void SomeMethod() 
    {
        if (Feature.NewRequirement())
        {
            DoTheNewStuff();
        }
        else
        {
            DoTheOldStuff();
        }
    }
}

The feature flag determines when the new behavior should be enabled and allows us to turn it off if necessary. Typically, after the feature has been running in production for a few weeks or months, we remove the flag and keep only the new implementation.

Problems With This Approach

  • Feature Flag as a Static Dependency – The Feature class is static and depends on a cache holding the feature flag configuration. This makes unit testing difficult since shared state in tests can cause flakiness and prevent parallel execution.
  • Injecting the Feature Flag? – We could pass the feature flag as a constructor argument to Entity or as a method parameter, but we prefer to keep domain objects as pure as possible, without dependencies on infrastructure. Most of the time, entities are either created by an aggregate or fetched from a database via an ORM.

How can we make our domain entities behave differently based on feature flags while minimizing dependencies and avoiding static calls inside the entity?

2 Answers 2

0

You can use the strategy pattern to encapsulate different behaviors based on the feature flag.

  • Interface

    public interface ISomeMethodStrategy
    {
        void Execute();
    }
    
  • Implementations

    public class OldSomeMethodStrategy : ISomeMethodStrategy
    {
        public void Execute() => DoTheOldStuff();
    }
    
    public class NewSomeMethodStrategy : ISomeMethodStrategy
    {
        public void Execute() => DoTheNewStuff();
    }
    
  • New domain entity with strategy

    public class Entity : IEntity
    {
        private readonly ISomeMethodStrategy _strategy;
    
        public Entity(ISomeMethodStrategy strategy)
        {
            _strategy = strategy;
        }
    
        public void SomeMethod() => _strategy.Execute();
    }
    

And then, before creating the entity, determine which strategy to use:

var strategy = featureService.IsNewRequirementEnabled() 
    ? new NewSomeMethodStrategy() 
    : new OldSomeMethodStrategy();

var entity = new Entity(strategy);
Sign up to request clarification or add additional context in comments.

Comments

0

If you want avoid the static in domain object or injection in domain object, you can just have 2 methods differents (follow the rule of extensibility) to implement new and old feature, like :

public class Bar
{  
    public void DoNewFeature() 
    {
        // ...
    }

    public void DoOldFeature() 
    {
        // ...
    } 
}

And after you use a feature service to choose which method to use in your use case, like :

public class BarService(IFeatureFlagService feature)
{  
    public void DoSomething(int idBar) 
    {
        var myBar = this.GetById(idBar);
        if (feature.Enabled("barNew")
        {
            myBar.DoNewFeature();
        }
        else 
        {
            myBar.DoOldFeature();
        }
    } 
}

The feature flipping isn't a domain object rule, so it's better to have this in service.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.