Spring BootでFeatureToggleを実現するライブラリff4jの紹介

# Feature Toggleとは

※ 基本的な話なのでご存知の方はすっ飛ばしてください
FeatureToggleとは、簡単に言えばある機能をオンにしたりオフにしたりする条件分岐のことです。FeatureFlagと呼ばれたりもします。

例えば下記のように、ある新しいアルゴリズムを使うために`useNewAlgorithm`というboolean変数を用意し、`useNewAlgorithm`のtrue/falseに応じて新アルゴリズムと旧アルゴリズムを使い分けるような場面で使います。

```javascript
  function reticulateSplines(){
    var useNewAlgorithm = false;
    // useNewAlgorithm = true; // UNCOMMENT IF YOU ARE WORKING ON THE NEW SR ALGORITHM
  
    if( useNewAlgorithm ){
      return enhancedSplineReticulation();
    }else{
      return oldFashionedSplineReticulation();
    }
  }
  
  function oldFashionedSplineReticulation(){
    // current implementation lives here
  }
  
  function enhancedSplineReticulation(){
    // TODO: implement better SR algorithm
  }
```

FeatureFlagはリリースタイミングが決まっている新機能の提供時やカナリアリリース、A/Bテストなどでよく使われます。

# FF4J

https://ff4j.github.io/
<img width="1003" alt="スクリーンショット 2020-01-07 22.29.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62495/a15afcb1-f3bf-b6d6-8808-9b461d3aff63.png">

FF4jとはFeatureToggleを簡単に実現できるJavaライブラリになります。

- Web Console, REST APIによるリアルタイムなToggle切り替え
- 多様なストレージを利用した状態の永続化
- Featureの利用状況のモニタリング

といった様々な機能が提供されています。

## 導入手順

### プロジェクトの作成

みんな大好き[Spring Initializr](https://start.spring.io/)からSpring Reactive Web, lombokだけを選んでプロジェクトを作成します。

### FF4jの導入

pom.xmlに依存関係を追加します。

```xml:pom.xml
<dependency>
    <groupId>org.ff4j</groupId>
    <artifactId>ff4j-spring-boot-starter</artifactId>
    <version>1.8</version>
</dependency>
```

`FF4j`のBean定義 & Featureの作成を行います。

```java:FF4JConfiguration.java
import org.ff4j.FF4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FF4JConfiguration {
    @Bean
    public FF4j ff4j() {
        FF4j ff4j = new FF4j()
                .createFeature(awesomeFeature())
                .createFeature(greatFeature())
                .createFeature(excellentFeature());
        return ff4j;
    }

    private Feature awesomeFeature() {
        return new Feature("AwesomeFeature", true);
    }

    private Feature greatFeature() {
        return new Feature("GreatFeature", false);
    }

    private Feature excellentFeature() {
        return new Feature("ExcellentFeature", false);
    }
}
```

今回は`AwesomeFeature`, `GreatFeature`, `ExcellentFeature`の3つのFeatureを定義し、`AwesomeFeature`だけオンにしています。

FF4jを利用したControllerを実装します。単純なメッセージだけを返すControllerで、定義した3つのFeatureがオンになっている場合そのFeatureに応じてメッセージに追記しています。

```java:GreetingController.java
@RestController
@RequiredArgsConstructor
public class GreetingController {

    // コンストラクタインジェクション
    private final FF4j ff4j;

    @GetMapping
    public String greeting() {
        List<String> features = new ArrayList<>();
        addFeatures(features);
        String greeting = String.format("Hello, %s World!!", String.join(" ", features));

        return greeting;
    }

    // 各Featureの処理
    private void addFeatures(List<String> features) {
        if(ff4j.check("AwesomeFeature")) {
            features.add("Awesome");
        }

        if(ff4j.check("GreatFeature")) {
            features.add("Great");
        }

        if(ff4j.check("ExcellentFeature")) {
            features.add("Excellent");
        }
    }
}
```

アプリケーションを起動し、http://localhost:8080 にアクセスします。
<img width="500" alt="スクリーンショット 2019-11-28 0.13.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62495/a4b2cd76-6e51-a768-7af5-f1cc12e2cd5b.png">

デフォルトでは`AwesomeFeature`が有効になっているため、メッセージは`Hello, Awesome World!`と表示されます。


### WebConsoleの導入

続いて、FF4jの管理コンソールを導入します。さきほどまでの状態だとソースコード上でFeatureのオン/オフを切り替える必要がありましたが、WebConsoleを利用することでアプリケーションの起動中にFeatureのオン/オフを切り替えることが可能となります。

pom.xmlに依存関係を追加します。

```xml
<dependency>
	<groupId>org.ff4j</groupId>
	<artifactId>ff4j-web</artifactId>
	<version>1.8</version>
</dependency>
<dependency>
	<groupId>org.thymeleaf</groupId>
	<artifactId>thymeleaf</artifactId>
	<version>2.1.4.RELEASE</version>
</dependency>
```

新たにBean定義を追加します。

```java
@Configuration
@ConditionalOnClass({ConsoleServlet.class, FF4jDispatcherServlet.class})
@AutoConfigureAfter(FF4JConfiguration.class)
public class FF4JWebConfiguration extends SpringBootServletInitializer {

    @Bean
    public ServletRegistrationBean<FF4jDispatcherServlet> ff4jDispatcherServletRegistrationBean(FF4jDispatcherServlet ff4jDispatcherServlet)
    {
        ServletRegistrationBean<FF4jDispatcherServlet> bean = new ServletRegistrationBean<>(ff4jDispatcherServlet, "/ff4j-web-console/*");
        bean.setName("ff4j-console");
        bean.setLoadOnStartup(1);
        return bean;
    }

    @Bean
    @ConditionalOnMissingBean
    public FF4jDispatcherServlet getFF4jDispatcherServlet(FF4j ff4j) {
        FF4jDispatcherServlet ff4jDispatcherServlet = new FF4jDispatcherServlet();
        ff4jDispatcherServlet.setFf4j(ff4j);
        return ff4jDispatcherServlet;
    }

}
```

起動して http://localhost:8080/ff4j-web-consoleにアクセスします。
<img width="1000" alt="スクリーンショット 2019-11-28 0.15.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62495/3e75bc79-55e2-6d05-f4ff-27114dfa5135.png">

「Features」メニューで現在定義されているFeatureを確認することができ、
トグルをオン/オフするのに連動してFeatureがオン/オフになります。

<img width="1000" alt="スクリーンショット 2019-11-28 0.17.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62495/ebb4019c-8e2d-b0a2-0a3a-6ebe241793f9.png">

## GIFアニメーションデモ

上記で説明した内容をGIFアニメーションで撮ったデモも貼っておきます。ご参考程度に。
![ff4j-demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62495/10f7768e-387b-bb8c-4e68-83e4fb9439ae.gif)

# まとめ

FF4jの簡単な紹介をしました。Feature Toggleは強力なテクニックですが運用方針を定めないとカオスなコードベースが生まれてしまうので気をつけて運用したいです。ProfileごとにDIするFeatureをわける、コーディング規約とレビューポイントを定めるといった枠組みが必要になります。

# 参考

martinFowler.com - FeatureToggle: https://martinfowler.com/articles/feature-toggles.html
FF4j: https://ff4j.github.io/
今回使ったリポジトリ：https://github.com/IshinFUKUOKA/ff4j-demo