LoginSignup
2
3

More than 5 years have passed since last update.

VuexでCQRSを導入しようとしたけどやっぱりやめた。自分用メモ

Posted at

はじめに

読み込みと書き込みを分解して、セキュアにテストできたらなと思いまして、CQRSに目をつけました。用途としては、store moduleでcommandを叩こうかなと思っていました。
しかし、いざ書いてみると結構なボリュームで、とてもじゃないですが、少人数での開発をする場合、コードの綺麗さ以上にボリュームが多いことによりスピードダウンが感じられました。
また、Specパターンを適用しないと、やはりテストやら検証やら恩恵が少ないと感じましたので、これ以上増えるのはキツイかなと思ったので没になりましたが、いつか実践できたらと思っています。
以下の例はバリデーションをSpecパターンを適用しないで肥大化した例 and それのせいで最終validateがめんどくさくなった例です。疑似コードですが、import axios より上はコピペでブラウザのコンソールとかで動くので、よければ軽く触って遊んでみてください。

疑似コード

class OverrideError extends Error {
  constructor(...params) {
    super(...params);
    this.name = 'CommandError';
  }
}

// commands.js
class Command {
  isValid() {
    // LaravelのFacadeで使われてた手法
    throw new OverrideError('isValidメソッドをオーバーライドしてください!!!');
  }
}

class CommandError extends Error {
  constructor(...params) {
    super(...params);
    this.name = 'CommandError';
  }
}

// commands/.js
class CandidateCommandError extends CommandError {
  constructor(...params) {
    super(...params);
    this.name = 'CandidateCommandError';
  }
}

// commands/candidate.js
class Candidate extends Command {
  constructor(params) {
    super();
    // getter は console.log で見えなく、forでも回せないので、
    // デバッグしやすいようにundefinedを突っ込んでおく
    this._name = undefined;
    this._age = undefined;
    this._skillLevel = undefined;
    if (!params) return;
    if ('name' in params) this.name = params.name;
    if ('age' in params) this.age = params.age;
    if ('skillLevel' in params) this.skillLevel = params.skillLevel;
  }

  isValid() {
    return Boolean(this.name && this.age && this.skillLevel);
  }

  get name() {
    return this._name;
  }

  set name(name) {
    // 名前は1文字以上の文字列
    if (typeof name === 'string' && name.length > 0) {
      this._name = name;
    } else {
      throw new CandidateCommandError('名前は1文字以上の文字列である必要があります。');
    }
  }

  get age() {
    return this._age;
  }

  set age(age) {
    // 未成年は禁止
    if (typeof age === 'number' && age >= 18) {
      this._age = age;
    } else {
      throw new CandidateCommandError('年齢は18歳以上である必要があります。');
    }
  }

  get skillLevel() {
    return this._skillLevel;
  }

  set skillLevel(skillLevel) {
    // スキルレベルは 0 ~ 10 の間
    if (typeof skillLevel === 'number' && skillLevel > 0 && skillLevel <= 10) {
      this._skillLevel = skillLevel;
    } else {
      throw new CandidateCommandError('スキルレベルは 0 ~ 10 の間である必要があります。');
    }
  }
}

// exception.js
import * as axios from 'axios';

class CommandHandlerError extends Error {
  constructor(...params) {
    super(...params);
    this.name = 'CommandHandlerError';
  }
}

// commandHandlers/candidateHandler.js
async function CandidateHandler(candidate) {
  if (!(candidate instanceof Candidate)) {
    throw new CommandHandlerError('Candidateインスタンスではありません。');
  }
  if (!candidate.isValid()) {
    throw new CommandHandlerError('Candidateの仕様を満たしていません。');
  }
  // 本来はrepositoryを使うが、イメージでお願いします
  const response = await axios.post('/api/candidate', candidate).catch(e => e.response);
  return response;
}

最後に

大規模開発ではCQRSをして、整理したいですね〜〜〜
CQRSの場合、query store として、 firebase database や firestore とかととても相性が良い気がしてます。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3