状態遷移図をコードで表現してみる

今回は状態遷移図をコードで表現してみたらどうなるかを書きます。

状態遷移図

お題となる状態遷移図は、以下です。

会員登録サービスにおける入退会のイメージです。 f:id:mmm-mao:20150507213432j:plain

クラス図

これに対応したクラス図は、以下です。

状態遷移図の遷移(矢印)を表現したクラスが会員イベントクラス。

状態遷移図の状態を表現したクラスが会員ステータスクラス。

f:id:mmm-mao:20150507213232j:plain

会員イベントクラス側に、

  • イベント発生時に実行可能なステータス一覧を定義(possibleStatusList変数)

  • イベント発生後のステータスを定義(toStatus変数)

の2つから、状態遷移図を表現します。

具体的なコード

それぞれのクラスに対応したコードは、以下です。

楽をするために、Lombokを利用しています。

会員イベントクラス
@ToString(includeFieldNames = false)
@AllArgsConstructor
public enum UserEvent {

    JOIN("入会する", new ArrayList<UserStatus>(), UserStatus.JOINED),
    WITHDRAW("退会する", Arrays.asList(UserStatus.JOINED), UserStatus.WITHDRAWAL_RESERVED),
    WITHDRAW_CANCEL("退会キャンセルする", Arrays.asList(UserStatus.WITHDRAWAL_RESERVED), UserStatus.JOINED);

    private final String name;
    private final List<UserStatus> possibleStatusList;

    @Getter
    private final UserStatus toStatus;

    public void verify(UserStatus status){

        if(possibleStatusList.contains(status)){
            return;
        }

        throw new UnsupportedOperationException(name + "イベント実行時に不正なステータスです。");

    }

}
会員ステータスクラス
@ToString(includeFieldNames = false)
@AllArgsConstructor
public enum UserStatus {

    JOINED("入会済"),
    WITHDRAWAL_RESERVED("退会予約中"),
    WITHDREW("退会済");

    private final String name;

}

簡単な状態遷移図なら、上のようなシンプルなコードで表現できます。

ただ、今回のお題も当てはまるんですが、ステータスはアプリケーションコードとは別に、DBなどで永続化していると思います。その際、アプリケーションとDBでステータスがマッチしない状況が出てきます。

具体的に言うと、退会予約中→退会済の遷移は退会日が過ぎたらの条件なので、

  • アプリケーション側は、退会予約中と退会済の2つの状態がある

  • DB側は、退会という1つの状態がある

のような状態になります。

同じステータスでも、アプリケーションとDBで異なれば、誤認識の元になるので、できれば避けたいです。

対応案としては、

  • 退会確定の会員イベントを増やす。→退会確定まで数秒〜数分の誤差が出るので、厳密性が求められるサービスだと厳しい。

  • DB側にステータスを保持しない。→DBに保持しないので、マッチしないことを考慮する必要なし。

が挙げられると思います。

僕の実際のプロダクトだと、「DB側にステータスを保持しない。」で対応しています。

この話は別の機会で記事を書きます。

ということで、今回は以上です。

シンプルな状態遷移図なら、今回のコードでも対応できるので、ぜひ参考にしてもらえたらと思います。