Spring AOP - Feature Switches

Handle feature switch checks with AOP.

  1. The Aspect

One idea I recently happened upon is handling feature switches in an aspect. Essentially, we want to externally control whether certain code should execute or not.

This is really a cross-cutting concern, and thereby a top contender for AOP! One way to identify candidates for AOP is looking for those small bits of code that are scattered around your code base which do not really contribute to the single responsibility of the class.

For feature switches it is annoying having to always inject some feature manager and start things off with:

val isEnabled = featureSwitchManager.isEnabled(feature)

if (isEnabled) {
    // actual code
}

This is a kind of meta capability that our code shouldn’t have to worry about. Additionally, we are usually happier testing the code without all this extra noise.

The Aspect

The source is available here.

@Aspect
@Component
class FeatureAspect(
    private val featureSwitchManager: FeatureSwitchManager
) {

    @Pointcut(value = "execution(public * *(..))")
    fun anyPublicMethod() = Unit

    @Around("anyPublicMethod() && @annotation(annotation)")
    fun proceedIfEnabled(joinPoint: ProceedingJoinPoint, annotation: FeatureSwitch): Any? {
        val featureName = annotation.name.takeIf(String::isNotBlank)
            ?: throw FeatureNameBlankException(joinPoint.signature)

        return when (featureSwitchManager.isEnabled(featureName)) {
            true -> joinPoint.proceed()
            false -> null
        }
    }
}

The implementation is super simple. We define a pointcut for any public methods with the @FeatureSwitch annotation, and pass the provided feature name to the FeatureSwitchManager, implementing the actual feature check.

An example of a consuming service:

@Service
class MessageService {

    @FeatureSwitch("MessageService")
    fun run() {
        logger.info("Running message service!")
    }
}

Now the run method can be enabled by passing true in the application properties:

MessageService:
  enabled: true

And there you have it. A simple AOP solution, taking care of feature switch boilerplate code.


© 2024. All rights reserved.