はじめに
こんにちは、Mikatus のエンジニアの野田です。2020年は大変な年になりました。Mikatus のエンジニア陣も2月の終わりごろからリモートワークをしています。
今年は11月にリリースした「新・会計データインポート」機能の開発をずっとやっていました。無事リリースすることができて本当に良かったです。
今回はその時の開発のことを書きます。
フロントエンドの複雑化を避けるために
昨今の Web アプリケーションはフロントエンドが複雑になりがちです。フロントエンドが複雑化したアプリケーションを運用していくことはとても苦労します。複雑化を解決するための技術や方法論(React.js, Vue.js, Flux など)が確立されてきました。
Mikatus でもフロントエンドの複雑化を避けるための技術選定、アーキテクチャの検討をしています。今年の11月にリリースされた Mikatus の新サービスである経営指標システム「YOSOD(ヨソッド)」の開発チームではフロントエンドの複雑化を避けるためのアーキテクチャを確立しました。RxJS を使った独自の状態管理の仕組みを作り、複雑化するフロントエンドを回避するアプローチを取っていて、とても興味深いです。
今回はその話はしません。
本稿では、私が所属している「新・会計データインポート」チームでは、フロントエンドを複雑にしないために「なるべくバックエンドでやっていこう」というゆるい方針を立てて開発を進めた話を書きます。
アプリケーションの構成
アプリケーションの主な構成、技術的なキーワードは以下です。
- フロントエンド
- TypeScript
- Vue.js, Vuex
- SPA
- バックエンド
- Scala, Akka HTTP
- DDD, Clean Architecture
- REST API
やったこと
以下、「なるべくバックエンドでやっていこう」という方針でやったことの例です。
(以下にでてくる API パス、データ構造、項目名等は実際のものと若干変えています)
ステータスを更新するための API を用意した
例えば、DBのテーブルにステータスを表すカラムを持っていて、ユーザーの操作を契機にそのステータスを更新するシチュエーションはよくありますが、それを実現するための方法は複数あります。
まず、考えられるのは対象の1レコードを更新する API を提供することです。いわゆる PUT 系の API です。バックエンドが、1つのレコードを更新することができる API を提供し、フロントエンドは JSON のステータスを表す項目の内容を書き換えてリクエストします。
この方法は複数のステータス更新のシチュエーションがある場合でも1つの Web API を用意するだけで実現可能です。しかしながら、例えば「ステータスを更新するタイミングで他のカラムの内容も変更する」という仕様があった場合、フロントエンド側でリスエストボディの json の内容を書き換えてリクエストする必要があります。業務ロジックがフロントエンド側に染み出してしまっています。
また、ステータス更新を契機に他のテーブルのデータを更新するような業務ロジックがある場合、上の方法だとフロントエンドから別の API をリクエストする必要が出てきます。
今回の開発では、ステータス変更を行うためのAPIを以下のようなパスで作成しました。
- POST /v1/resources/:id/completing (完了状態にする API)
- POST /v1/resources/:id/discarding (破棄済みにする API)
ステータス更新のスチュエーションの数だけ上記のような形のAPIを作り、フロントエンド側は各操作でそれをリクエストする。バックエンド側でそのリクエストごとに処理をしていく形です。フロントエンドに業務ロジックが染み出すこともなく、バックエンドのドメイン層に適切な形で処理を記述することができるようにしました。
DB に永続化しないデータモデルを API で返すようにした
実際の例を書きますと、「新・会計データインポート」には勘定科目データと仕訳データという概念があります。2つのデータは DB に永続化します。勘定科目データと仕訳データの関係性は以下です。
仕訳データには「借方勘定科目コード」「貸方勘定科目コード」という項目があり、勘定科目の情報は勘定科目データに存在し、科目コードで関連付けられています。
ユーザーがアップロードしたデータによっては、仕訳データに「借方科目コード」「貸方科目コード」として登録されているのに、勘定科目データにはその科目コードが登録されていないというシチュエーションがあります。仕訳データには登録されているので、勘定科目データには存在しないデータを「未登録勘定科目データ」として画面に表示する仕様があります。
その「未登録勘定科目データ」の抽出方法も複数のやり方が考えられます。一つは永続化された2つのデータ群をフロントエンド側から API 経由で取得して、そのデータを比較して抽出する方法です。以下のような、APIをフロントエンドから呼び出すイメージです。
- GET /v1/accounts (勘定科目データ一覧取得)
- GET /v1/journals (仕訳データ一覧取得)
この方法も業務仕様をフロントエンド側で記述していると言えます。
やはり、バックエンド側で2つのデータを取得し、比較して、抽出した未登録勘定科目データをレスポンスとして返却する
API を作成する方が良さそうなので、この方法を取りました。
- GET /v1/unregistered-accounts (未登録勘定科目一覧取得)
複数項目にまたがる妥当性チェックはバックエンドで行うようにした
データを取り込む前に、その内容に妥当性があるかを判定する処理を入れます。いわゆる「バリデーション」と言われるものです。妥当性のない項目はエラーメッセージとともに入力欄の枠を赤くしたりして、ユーザーにその旨を伝えます。
複雑なデータを扱う Web アプリケーションの妥当性チェックは複雑になりがちです。それの全てをフロントエンドで行うとフロントエンド側のコードが増大してしまいがちです。しかしながら、リッチな UI を提供する昨今の Web アプリケーションでは入力途中に入力内容に不備がある旨を伝えるなど、全てをバックエンドで行うことはできません。
今回の開発では「単一項目のバリデーションはフロントエンドで、複数項目にまたがるバリデーションはバックエンドで」という方針で進めました。
(バックエンドでも、単一項目のバリデーションは実施しています。念の為)
さいごに
上のような方針を掲げて、設計・実装を進めてみました。設計フェーズで、かっちりやり方を固められたものもあれば、実装フェーズで「あーでもないこーでもない」と変更を加えながら進めたものもありました。
リリースしたばかりなので、上のような方針でやってきたことが成功だったかどうかの見極めはこれからになります。
今年もお疲れ様でした。来年も頑張りましょう!!