JavaScriptフレームワークとビジネスロジックを分離してみる

こんにちは。最近、WebアプリだけでなくKubernetesによる環境構築もしている弓場です。

私が現在参加しているプロジェクトでフロントエンドにNuxt.jsを使用して画面とデータ取得・操作專門のAPIと分離していますが、その際にAPIの型が定義されていないためコード品質が低下していっているのではないかと不安に感じています。
その中で今回コード品質を維持させやすくするための設計として画面描画とビジネスロジック(通信処理やデータ加工等)をそれぞれJavaScriptとTypeScriptに分離した設計について可能性を感じ始めましたので今回ご紹介したいと思います。

ビジネスロジックと画面描画をJavaScriptとTypeScriptに分離するのか

ここでいう画面描画というのはVue.jsやReact.js等のJavaScriptフレームワークのことを指します。 具体的には以下のような図になります。

f:id:AdwaysEngineerBlog:20180802121201p:plain:w450

ビジネスロジックと画面描画を分離するのにはいくつかの理由があります。
その中で主な理由となるのが画面描画側のTypeScriptへの相性がまだ悪い点とTypeScriptは型を定義してくれるおかげで保守がしやすい言語であるという点をうまいとこに落とし込むための設計になります。

なぜ画面描画をTypeScriptにしないのか

JavaScriptのフレームワークはものによってclass化が出来なかったり、フレームワークの関数の型が公式で提供されていないものがあるのと、JavaScriptでの実装を前提しているとこがあるので無理にTypeScriptに合わせたコードに変更しようとすると変更する労力で疲弊することがあるためです。

ちなみに全てTypeScriptで実装されたAngularJSはメジャーバージョンアップの頻度が多いので、本番導入をすると1年くらいで最新版と互換性が無くなるコードになってしまうのではないかと懸念しているためです。

TypeScriptを採用するメリットとは?

1つ目はJavaScriptフレームワークでAPIリクエストを行う際に期待値の型が定義出来るので、自分以外の人が保守をする際にAPIリクエストの期待値が共有しやすくなるという点があります。
また、VSCodeやWebStormなどを使えばコード補完で即座に変数や関数を見ることも出来ます。

以下の画像は実際に通信処理の期待値をWebStorm(筆者はIntelliJ IDEAを使用)でコード補完した際のものです。期待値の変数に型定義をしなくても呼び出し側の返却値の型を定義していればわざわざどんな期待値が返ってくるのかをconsole.logで確認する必要がなくなり、配列の関数も即座に見ることが出来ます。

f:id:AdwaysEngineerBlog:20180802120653j:plain

2つ目はフレームワークに依存しなければ、その分テストコードも書きやすくなるという点があります。

例えば、Vue.jsのソースコードvueファイルに通信処理などの処理を埋め込んでしまうとテストコードもVuexのmockを用意したり、documentやwindowを用意したりと煩雑になってしまいがちです。
ですが、TypeScriptのビジネスロジックだけであれば以下のようなコードで通信処理とビジネスロジックのテストコードを書くことができます。
今回の検証ではテストフレームワークにJestを使用しました。

// user-service.test.ts
import UserService from "./user-service";
import Axios from "axios";
import MockAdapter from 'axios-mock-adapter';
import User from "./model/user";
 
describe('ApiRequestSample', () => {
  it('basic', async () => {
    const axios = Axios.create({});
    const mock = new MockAdapter(axios);
    const repo = new UserService(axios);
    mock.onGet('./sample').reply(200,[{"id": 1, "name": "テスト"}]);
 
    const result: User = await repo.getAllUser();
    expect(result[0].name]).toEqual("テスト"); 
  });
})

画面描画側もTypeScript化出来ればコーディングが楽に

JavaScriptのフレームワークをTypeScriptにすると動かなくなることもありますが可能な限りTypeScriptに変更することで画面描画側のコーディングも楽になります。

TypeScript化をするだけでしたらwebpackを変更するだけで対応出来るものも多いはずです。私のプロジェクトではNuxt.jsを使用しておりますが、cliのテンプレートが用意されておりました。
https://github.com/nuxt-community/typescript-template

ここで注意すべきこととして class化などをさせずに公式のJavaScriptによるコードを維持させることを意識しておくこと が大事です。
また、TypeScriptの型が用意されていないなどでTypeScript化が出来ない場合は無理に変更する必要もありません。

まとめ

今回はTypeScriptのメリットを活かしつつ、JavaScriptフレームワーク側でのTypeScript化の苦労をなるべく回避する方法の一例を取り上げてみました。

JavaScriptだけのソースコードだと「このAPIの期待値ってなんだっけ?」となりconsole.logで調べたり、JavaScriptのフレームワークはテストコードを書くと少しコード量が多い気がするといった問題を感じていました。

ですが、TypeScriptを採用したりビジネスロジックをJavaScriptフレームワークから切り離せば、継続した開発も少しは楽になるのでは?と思い今回の設計を思いつきました。

特に画面描画に対して単体テストのコードを書くというのは労力の割にメリットが薄いというのもあったりしましたので、画面はE2Eによる最小限な画面遷移や画面操作のみにして本来しっかりとテストすべきビジネスロジックの単体テストを継続的にテストすれば画面側のデグレも減らせていけるのではないでしょうか。