Implement feature switches with spring
A feature toggle is a technique in software development that attempts to provide an alternative to maintaining multiple branches in source code, such that a software feature can be tested even before it is completed and ready for release. Feature toggle is used to hide, enable or disable the feature during runtime.
It often happens in our code that we need to provide extra calls to a function or an external system based on certain coditions. These conditions can be: * when a system property is set * depending on the environment we are in * on a condition in a parameter of a request
The code then becomes bloated with constructs like:
if (feature.isActive()) callExternalSystem();
if (object.isOfType()) doSomething(object); else doSomethingElse(object);
if (env == Env.DEMO) doSomething(object);
This complexifies the code and is not easy to maintain.
... comes the strategy pattern!
Strategy pattern
We'll implement a feature to scan files only when this feature is turned on. We'll use the document repository as an exmple.
@Service public class VirusScanService { @Overide boolean scan(byte[] toScan) { // scan the data for mischievous content return false; } }
And the service - DocumentService - that calls our virus scan:
@Autorwired private MetadataRepository metadataRepository; @Autorwired private VirusScanService virusScanService; @Autorwired private FileRepository fileRepository; @Transactional public uploadDocument(Metadata metadata, byte[] data) { String documentId = metadataRepository.save(metadata); virusScanService.scan(data); fileRepository.save(documentId, data); }
In this example, the virusScanService is always called, let's strategify it.
Implement the strategy
The first step is to introduce an interface
public interface VirusScanService { boolean scan(byte[] toScan); }
Then we'll provide two implementations, a null implementation that does nothing and our actual implementation
@Service public class NullVirusScanService implements VirusScanService { @Overide boolean scan(byte[] toScan) { // do nothing return false; } }
@Service public class UvscanVirusScanService implements VirusScanService { @Overide boolean scan(byte[] toScan) { // call the command line 'uvscan' command return false; } }
Now, if nothing else changes, spring will not be able to chose which implementation of our VirusScanService to inject in DocumentService.
Sprinkle some spring powder
We'll configure the application to inject NullVirusScanService by default and turn on the UvscanVirusScanService with the application property feature.virus-scan: uvscan.
@Service @ConditionalOnProperty(name = "feature.virus-scan", havingValue = "null", matchIfMissing = true) public class NullVirusScanService implements VirusScanService { }
@Service @ConditionalOnProperty(name = "feature.virus-scan", havingValue = "uvscan") public class UvscanVirusScanService implements VirusScanService { }
Notice the matchIfMissing in NullVirusScanService that tells spring to use this implementation if no property feature.virus-scan is found.
You can now configure which implementation you want with a simple configuration, this can also be useful to provide fake implementations for your tests.
Happy coding !!















