のんラボ

フロントに処理を書くとき、フレームワーク内で処理は書かない方がいい気がする

2020/06/17 2020/06/17 フロントに処理を書くとき、フレームワーク内で処理は書かない方がいい気がする

フロントに処理を書くとき、フレームワーク内で処理は書かない方がいい気がする

こんにちはNonです。
今回はこれまでと変わってフロントに着目したいと思います。

前提

  • (laravel-mixでの)Vue

フレームワーク内で処理を書くとこうなる

copied.export default {
  name: 'ApplicationComponent',
  data() {
    return {
      food: {
        name: '',
        price: 0,
        num: 0,
      },
      drink: {
        name: '',
        price: 0,
        num: 0,
      },
      sum: {
        price: 0,
        num: 0,
      },
      tax: 0.08,
    };
  },
  created() {
    this.food.name = 'おにぎり';
    this.food.price = 100;
    this.food.num = 3;
    this.drink.name = 'お茶';
    this.drink.price = 150;
    this.drink.num = 1;
  },
  computed: {
    sum() {
      return {
        price: this.calcTax(this.food.price, this.food.num) + this.calcTax(this.drink.price * this.drink.num),
        num: this.food.num + this.drink.num,
      };
    }
  },
  methods: {
    calcTax(price, num) {
      return price * (1 + this.tax) * num;
    },
  }
};

コンポーネント単位でこのような処理を書くと、違うコンポーネントのロジックを書くときに、計算方法を間違えそうになるかもしれません。

そこで、計算処理、つまりビジネスロジックを持つモデルを外部のjsで持つことでロジックの重複を防ぎます。

フレームワーク内はレンダリングに注力し、処理は他のパッケージで持つ

フレームワーク内はレンダリング、つまり値の監視や、コンポーネントの管理などはフレームワーク(ここではVue)で行い、フロントで行うべき処理であるビジネスロジックはモデルとして外部に持つようにします。すると下記のようになります。

copied.export default {
  name: 'ApplicationComponent',
  data() {
    return {
      food: {
        name: '',
        price: 0,
        num: 0,
      },
      drink: {
        name: '',
        price: 0,
        num: 0,
      },
      sum: {
        price: 0,
        num: 0,
      },
      tax: 0.08,
    };
  },
  created() {
    this.food.name = 'おにぎり';
    this.food.price = 100;
    this.food.num = 3;
    this.drink.name = 'お茶';
    this.drink.price = 150;
    this.drink.num = 1;
  },
  computed: {
    sum() {
      const productCollection = new ProductCollection([
        new Product(this.food.name, this.food.price, this.food.num),
        new Product(this.drink.name, this.drink.price, this.drink.num),
      ]);
      return {
        price: productCollection.sumPrice(),
        num: productCollection.sumNum(),
      };
    }
  }
};
copied.export class ProductCollection
{
  constructor(items = []) {
    this.items = items;
  }

  sumPrice() {
    return this.items.reduce((previous, current) => {
      return previous.sumPrice() + current.sumPrice();
    });
  }

  sumNum() {
    return this.items.reduce((previous, current) => {
      return previous.num + current.num;
    });
  }
}
copied.export class Product
{
  static get TAX() {
    return 0.08;
  }

  constructor(name, price, num) {
    this.name = name;
    this.price = price;
    this.num = num;
  }

  calcTax() {
    return this.price * (1 + this.TAX);
  }

  sumPrice() {
    return this.calcTax() * this.num;
  }
}

このように、商品のビジネスロジックをクラスの中に封じ込め、更にフレームワーク依存しない箇所に集めることで、使い回すことを可能にしています。

さらに、フレームワーク依存しないので、他のフレームワークに移行するときにも重宝します。

Vueの場合、mixinに書けばいいじゃん?

もちろん、Vueに依存することを前提にしている場合はそれでもいいですが、HelperやUtilのような役回りが多いmixinにビジネスロジックを書くのはいかがなものでしょうか?

〇〇mixinが多くなってしまうのが、目に見えます。

クラス(オブジェクト)のパワーを借りることができる。

例えば、バリデーターを自作したとして、このようなクラス作成します。

copied.export class StringValidator
{
  constructor(value) {
    this.value = value;
    this.result = [];
    this.messages = [];
  }

  max(length) {
    this.result.push(this.value.length > length);
    this.messages.push(length + '文字以下で入力してください。');
    return this;
  }

  min(length) {
    this.result = this.value.length < length;
    this.messages.push(length + '文字以上で入力してください。');
    return this;
  }

  number() {
    this.result = Number.isInteger(this.value);
    this.messages.push('半角数字のみで入力してください。');
    return this;
  }

  invalid() {
    return this.result.filter((item) => {
      return item === true;
    }).length > 0;
  }
}

このようにすれば、jQueryだろうがReactだろうが使用できるようになるでしょう。
今回の場合はVueで書いてみます。

copied.export default {
  name: 'ApplicationComponent',
  data() {
    return {
      name: '',
      errors: [],
    };
  },
  methods: {
    submit() {
      const validator = new StringValidator(this.name);
      const invalid = validator
        .max(10)
        .min(1)
        .invalid();
      if (invalid) {
        this.errors = validator.messages;
        return false;
      }

      // 処理

    }
  }
};

これならバリデーションの内容を外部に任せることができます。
もちろん、入力中のバリデーションやVueで行いたい基本的なバリデーションをmixinなどに書いてリアルタイムにエラーを出力することなどもできますので、そちらはVue側に記述して、ビジネスロジックに関するバリデーションは外部に持つというのが、いい気がします。

最後に

今回はフロントにロジックを記載するときについて書いてみました。

PHPでWebアプリを作成するときは少し前まではフロントを書くときjQueryを採用することが多かったです。

こうなるとどうしてもロジックをスクリプトにチョチョイと書いてしまう実装は多いのではないでしょうか?

フロント言語がしっかりとしてきた今、フロントのコードもしっかりと設計していきたいものです。

今は社内でスクラムの勉強会をしているので、その記事も書くかもしれません。

そのときはよしなに。

.