ドメインモデルで実装してみる

# はじめに

本記事は [現場で役立つシステム設計の原則 変更を楽で安全にするオブジェクト指向の実践技法 　著：増田 亨](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E5%BD%B9%E7%AB%8B%E3%81%A4%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E8%A8%AD%E8%A8%88%E3%81%AE%E5%8E%9F%E5%89%87-%E5%A4%89%E6%9B%B4%E3%82%92%E6%A5%BD%E3%81%A7%E5%AE%89%E5%85%A8%E3%81%AB%E3%81%99%E3%82%8B%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%81%AE%E5%AE%9F%E8%B7%B5%E6%8A%80%E6%B3%95-%E5%A2%97%E7%94%B0-%E4%BA%A8/dp/477419087X) を4章まで読んで、学んだことを実践してみた内容です。

学んだ内容は以下にまとめています。

https://qiita.com/Regpon/items/1116679adadd8fb76f3f

# これまで学んだ内容を早速実践

業務で「メンテナンス時にメンテナンス用の文言を各所に掲載する」という機能追加の実装をする必要が出たので、ドメインモデルでの実装を実践してみた。

# 仕様の整理

今回の実装で必要な業務データと機能は以下のとおり

**データ**

- メンテナンスの開始日時
- メンテナンスの終了日時
- メンテナンス告知文のタイトル
- メンテナンス告知文の本文

**機能**

- メンテナンス日時であればメンテナンス告知の文言を掲示する

上記の機能を業務データを使った業務ロジックに落とし込むと、

**ロジック**

- 現在日時がメンテナンス中に当たるかどうか
- メンテナンス中であればタイトルを取得
- メンテナンス中であれば本文を取得

以上を踏まえて実装をしてみた。

## 実装

日時についてのロジックが多いので、まずはメンテナンス日時についての業務ロジックやデータを持つドメインオブジェクトを作成する。

```Java
/**
 * メンテナンス日付に関するドメインオブジェクト
 */
class MaintenanceDateTime {
    private final LocalDateTime startDateTime;

    private final LocalDateTime endDateTime;

    /**
     * コンストラクタ
     * @param startDateTime メンテナンス日付時刻の始点
     * @param endDateTime メンテナンス日付時刻の終点
     */
    MaintenanceDateTime(String startDateTime, String endDateTime) {
        try {
            this.startDateTime = LocalDateTime.parse(startDateTime);
            this.endDateTime = LocalDateTime.parse(endDateTime);
            if (this.startDateTime.isAfter(this.endDateTime))
                throw new RuntimeException("メンテナンス開始日が終了日よりも後になっています。");
        } catch (DateTimeParseException dEx) {
            logger.warn("メンテナンス日付の設定に誤りがあります。");
            throw dEx;
        }
    }

    /**
     * 指定した日時がメンテナンス期間中かどうか
     * @param target 確認したい日時
     * @return メンテナンス中ならtrue
     */
    boolean onMaintenance(LocalDateTime target) {
        return startDateTime.isBefore(target) && endDateTime.isAfter(target);
    }
}
```

次にそのドメインオブジェクトを使ったメンテナンスにまつわるロジックを持つドメインモデルを作成する

```Java
/**
 * メンテナンスドメインモデル
 */
public class Maintenance {

    private final MaintenanceDateTime maintenanceDateTime;

    private final String title;

    private final String body;

    /**
     * コンストラクタ
     * @param startDateTime メンテナンス日付の始点
     * @param endDateTime メンテナンス日付の終点
     * @param title メンテナンスタイトル
     * @param body メンテナンス本文
     */
    public Maintenance(String startDateTime, String endDateTime, String title, String body) {
        this.maintenanceDateTime = new MaintenanceDateTime(startDateTime, endDateTime);
        this.title = title;
        this.body = body;
    }

    /**
     * メンテナンス中なら設定されたメンテナンス用のタイトルを取得する。
     * そうでないなら空文字を返す。
     * @return メンテナンス告知文のタイトル
     */
    public String getTitleIfOnMaintenance() {
        return nowOnMaintenance() ? title : "";
    }

    /**
     * メンテナンス中なら設定されたメンテナンス用の本文を取得する。
     * そうでないなら空文字を返す。
     * @return メンテナンス告知文の本文
     */
    public String getBodyIfOnMaintenance() {
        return nowOnMaintenance() ? body : "";
    }

    /**
     * 現在日時がメンテナンス期間中かどうか
     * @return メンテナンス中ならtrue
     */
    public boolean nowOnMaintenance() {
        return onMaintenance(LocalDateTime.now());
    }

    /**
     * 指定した日時がメンテナンス期間中かどうか
     * @param target 確認したい日時
     * @return メンテナンス中ならtrue
     */
    public boolean onMaintenance(LocalDateTime target) {
        return maintenanceDateTime.onMaintenance(target);
    }
}

```

こうすることで、呼び出し側は

```Java
// 今現在メンテナンス中かどうか知りたい時
if (maintenance.nowOnMaintenance())

// メンテナンスのタイトルを使いたい時
String title = maintenance.getTitleIfOnMaintenance();

// 明日の今の時間がメンテナンスかどうか知りたい時
if (maintenance.onMaintenance(LocalDateTime.now().plusDays(1)))
```

のように使えるので、業務ロジックを実装する必要がなくなる。
また、上記を使ったロジックがふえてきたらドメインモデルにロジックを写すことを考えればいい。


# 考察

明らかに今までと実装の仕方が変わりました。

出来上がったものを見れば、至極当然な実装に思えますが、そう思える実装ってなかなかできてなかったんだなと実感しました。

ただ悩んだのは、メンテナンスの文言をメンテナンス期間中である時だけ取得して、そうでない時は空文字を返す実装にしたところ。空文字にする必要があるかどうか迷ったのだが、メンテナンス中でもないのにメンテナンスの文言が取得できるのは事故の元かなと思い。そのような実装にしました。少なくともメソッド名でなんとなくそういう仕様っぽいのを汲み取ってもらえることを意識することで、混乱は回避できているでしょうか？

これがありがた迷惑なのかどうか、ご意見があれば伺いたいです。