見出し画像

フロントエンドでDDDした話

こんにちは、株式会社POLでエンジニアをしているミズノです。今年はコードを書く方向で頑張っています。最近はフロントエンド側のビルド環境をWebpackからRollup.jsに移行、合わせてesbuildを試してみたりとキャッチアップに時間を割いています。
ちょうどDDDも試してみたいと考えていたので、簡単に導入してみました。

DDDとレイヤードアーキテクチャ

POLのフロントエンドはレイヤードアーキテクチャを採用しているので、ドメインオブジェクトの定義もdomain層でまとめています。ロジックも同じです。
まずは難しいことは置いておいて、簡単なdomainモデルの定義を試してみました。例えばメールアドレス入力の判定です。

hooksの複雑化

これまで入力の値はhooks内で保持し、そこでvalidationを書いて判定することが多かったのですが、どうしてもhooksが巨大化しやすく、複数の責務で複雑化します。今回は以下のように制限を設けてみました。

  • 入力の値を管理するだけ

  • ロジックは持たない

例えば以下のようなものです。

// 雑に書いてるので型定義や、importは省略してます。
const useInput = () => {
    const [email, setEmail] = useState("");
    const handle = useCallback((e) => {
        setEmail(e.target.value);
    },[setEmail]);

    const submit = useCallback(async () => {
        // 判定処理をusecaseの方に回す。
        const result = usecase.handle(email);
        // resultに成功・失敗の値が入っている。
    }, [usecase, email]);

    return {
        handle
    }
}

usecase.handleというメソッドに入力値を渡しています。判定ロジックはhooksに持たせないので、記述がシンプルになります。ややこしい判定処理はdomain層に任せてしまいます。

usecaseに渡すときにはドメインモデルのオブジェクトを渡しません。ドメインモデルのオブジェクトはapplication層で利用します。以下のようにEmailオブジェクトを作ってdomain層に渡します。

handle(emailTxt: string): Promise<Result<AuthResponse, Error>> {
    return this._repository.login(new Email(emailTxt));
}

ドメインモデルの定義

実際にEmailの判定はどこでやってるかというと、domain層で値を取り出すときに自動的におこないます。以下のようにドメインモデルに持たせる感じですね。

export class Email {
  private readonly _value: string;

  constructor(value: string) {
    this._value = value;
  }

  get value(): Result<string, Error> {
    return this.valid();
  }

  private valid(): Result<string, Error> {
    if (this._value.length < 1)
      return Result.Err(new Error("EmailAdressIsRequired"));

    // 正規表現チェックここにEmailの正規表現入ります。
    const pattern =  /^/;
     
    if (!pattern.test(this._value))
      return Result.Err(new Error("EmailAddressNotValid"));
    return Result.Ok(this._value);
  }
}

ドメインモデルから値を取り出すときにResult型で返します。これがあるので、パターンマッチで簡単に正しい場合、失敗の場合の処理がかけます。失敗した場合はhooks内にエラーを返すだけです。ユニットテストも簡単に記述できます。

ドメインモデルを利用するときはこんな感じです。

login(mail: Email): Promise<Result<AuthResponse, Error>> {
 

    // domainのエラーチェックをここで行う
    if (mail.value.isError())
      return Promise.resolve(Result.Err(mail.value.error()));
    
    return this._driver.post(
      params.email.value.unwrap(),
    );
  }

データの型をプリミティブ型から、ドメインの型に変更するだけでも恩恵は大きいです。今後は本格的にDDDのいいとこ取りをしてアップデートしていきたいと思います。

POLでは一緒にフロントエンドのの開発をしてくれるエンジニアをとても求めています。ビルド環境、レイヤードアーキテクチャ、DDD、WebAssemblyなどのキーワードに心躍る人がいれば是非カジュアル面談でお話ししましょう。
技術の話だけでも大丈夫です。お待ちしています!

この記事が気に入ったらサポートをしてみませんか?