こんにちは。ブロックチェーンチームのソフトウェアエンジニアの id:odan3240 です。
ブロックチェーンチームでは NFT を販売するためのUniqysマーケットプレイス(以下、ユニマ)と、その NFT を販売するための管理画面(以下、管理画面)を開発しています。ユニマはブロックチェーン上の NFT を日本円で売買可能なマーケットプレイスです。
ユニマの開発はブロックチェーンに関するサービスなので、ブロックチェーンに関する技術をゴリゴリに使って開発しているのでは?と考える方がいるかもしれません。しかし実際はそうではなく Web アプリケーションに関する開発は普通の技術スタックで開発しています。もちろん実装するロジックによってはブロックチェーンに関する知識が必要となる場合もありますが、マネージドサービスやフレームワークやツールを活用してコア機能の実装に集中できるように技術スタックを選択しています。
この記事ではユニマと管理画面の主に Web アプリケーションに関する技術スタックと、その採用理由について紹介します。
技術スタック
TypeScript
ユニマと管理画面のフロントエンドとバックエンドの全てで TypeScript を使用しています。モバファクはプロダクトのバックエンドには Perl が採用されることが多い企業でしたが、開発の効率化のために静的な型がほしいのと文法が JavaScript とかなり近くメンバーの学習コストも低いという点から、2018年に立ち上がったブロックチェーンチームでは立ち上げ当初から各プロダクトに TypeScript が採用されることが多いです。
ユニマと管理画面もこの流れをくんで TypeScript を採用しました。
NuxtJS
ユニマと管理画面のフロントエンドの実装に NuxtJS を使用しています。Vue.js は他のチームでの採用実績があり社内のノウハウやリソースを流用できそうな点と、プロダクトの立ち上げ当初はエンジニアに比べるとデザイナのリソースが浮くことがたまにあり、いざとなればデザイナもフロントエンドの実装に参加できるように書き方が HTML/CSS に近い点が採用理由です。
TypeScript との相性を少しでもよくするために Vue.extend
を使用してコンポーネントを定義して、VSCode の拡張機能に Vetur を使用しています。Vue.js は TypeScript と相性が悪いと言われますが拡張機能に Volar を利用することで、SFC の template の型推論やコンポーネントの props の指定の有無を静的にチェックしてくれます。また、Vue.js の v2 でも Composition API の defineComponent を使用すれば v3 と同様の開発体験を得ることができそうで、Vetur からの置き換えを検討中です。
ユニマは SNS からの流入が想定されているのでOGP のために NuxtJS を使って Lambda で SSR しています。 一方で、管理画面はどの画面もログインして使用するページであり、SNS からの流入も想定されていないため、SPA として S3 に静的ファイルをホスティングしています。SPA でもユニマとの開発体験を揃える、ビルド周りのシステムを隠蔽するなどの理由で NuxtJS を選択しています。
Vuetify
管理画面のフロントエンドの実装に UI ライブラリの Vuetify を使用しています。ユニマのフロントエンドは自由度の高いデザインを実現するために Atomic Design をベースにフルスクラッチで UI コンポーネントを実装しています。一方で、管理画面に求められるのは管理画面的な UI なので、Vuetify を採用してフロントエンドの実装コストを削減することを目的としています。
NestJS
ユニマと管理画面のバックエンドは同じサーバを参照していて、 NestJS を使用して実装しています。
NestJS は生の Express を採用するよりも、規約の従って開発が可能なこと、DI の仕組みによって依存関係をモックしやすくテストが書きやすいこと、周辺のモジュールが整っていてコアの開発に集中できそうなこと、などが採用理由です。
OpenAPI
フロントエンド向けに REST API を提供しています。NestJS にはバックエンドのコントローラーから OpenAPI の定義書を自動生成するモジュールがあります1。自動生成された定義書は openapitools/openapi-generator-cli を使用してフロントエンド向けの TypeScript のコードをさらに自動生成しています。つまり、バックエンドでコントローラーを定義すると、それを呼び出すフロントエンドのコードが自動生成される仕組みを用意しています。GraphQL を採用しなかった理由は、バックエンドの API を SaaS 用の API として公開する計画がプロジェクトの立ち上げ当初からあったからです。公開用の REST API とフロントエンドで使用する GraphQL の両方を実装、管理するより、公開用の REST API を使用してフロントエンドを実装するほうが作成するべき API が1つだけになるので、メンテナンスのコストが下がると判断しました2。
TypeORM
ORM には TypeORM を使用しています。過去のプロジェクトで採用実績があったのと、NestJS に TypeORM のモジュール3があったので採用しました。TypeORM を採用した感想は、マイグレーションの仕組みが用意されていたりと便利ですが、レコードを select してくるときの型安全性がないことや、テーブルを多数 join すると SQL の実行速度とは別でパフォーマンスが劣化するなどの問題4が知られていて一長一短だなと思いました。特に select 時の型安全性がないのは、TypeScript を採用しているメリットを下げることにつながるので、次に新しいプロジェクトが立ち上がるなら他のライブラリを検討すると思います。
Terraform
AWS のリソースの IaC 化に Terraform を使用しています。IaC 化の手段として、フロントエンドとバックエンドと言語を揃えるために AWS CDK も候補にありました。しかし、Terraform が会社の横断的にインフラを担当しているチームによって全社的に導入されそうな雰囲気があったことと、terraform import
コマンドの存在から Terraform を採用しました。
terraform import
は AWS にある既存のリソースを Terraform 管理下にするためのコマンドです。ユニマの開発におけるリリースの流れは、ステージング環境で動作確認を行った後に本番環境にデプロイを行う流れです。新しくインフラのリソースを作成するときは、AWS のマネージドコンソールでステージング環境にリソースを作成して terraform import
で Terraform 化した後に、この Terraform をベースに本番環境にリソースを作成します。この terraform import
を使用した一連の流れで作業を行うことで、ステージング環境と本番環境が限りなく一致する、マネージドコンソールから直感的にリソースを作成できる、といった利点があります。
MySQL
データストアには MySQL (Amazon Aurora) を使用しています。まず最初に「Ethereum 上の一部データをデータストアに取り込み、集計処理を行う」という用途がメインになると考えて RDB を選択しました。そして社内の他のチームでの採用実績もあり、MySQL の設定や運用のノウハウを参考にできると考えて MySQL を選択しました。
バックエンドにマルチテナント機能を実装するときになって PostgreSQL の Row Level Security 機能を知って「PostgreSQL の方が良かったのでは?」と後悔しました。次データストアを選ぶときは PostgreSQL の機能も含めて検討したいです。
Truffle/TypeChain
これらはどちらも Ethereum 上で動くスマートコントラクトの実装を補助するツールです。Truffle はコントラクトのデプロイやマイグレーションの管理を行い、TypeChain はスマートコントラクトのメソッドを TypeScript から呼び出すときに、型を自動生成するためのツールです。
Truffle は当時はコントラクトの管理を行うツールのデファクトスタンダードでしたが、最近は Hardhat がその座を奪いつつあるという印象です。Hardhat はデバッグの容易さや多数のプラグインがコミュニティによって開発されており、コントラクト実装時の開発体験の改善が期待できます。機会を見て Truffle を Hardhat に置き換えるか、新規プロジェクトでは Hardhat を採用したいと考えています。
TypeChain に関しては以前に紹介記事を書いたのでそちらを御覧ください。
Infura/IPFS/Torus
これらはユニマで使用しているブロックチェーン関係の SaaS やプロトコルです。
- Infura はマネージドな Ethereum API を提供する SaaS
- IPFS は分散してファイルを管理できるプロトコル
- Torus は非カストディに秘密鍵を管理する SaaS
ブロックチェーン関係の技術スタックについて後日別の記事で詳細を紹介できればと考えています。
終わりに
ユニマと管理画面の技術スタックの紹介をしました。
これらのスタックに興味を持った方やもっと詳しく話を聞いてみたい方はカジュアル面談をしてみませんか?以下からエントリーしてフリーコメント欄に「アドベントカレンダーを見た」と記載してエントリーしてください。
-
OpenAPI (Swagger) | NestJS - A progressive Node.js framework↩
-
Auth0 の API ドキュメントの
anything that can be done through the Auth0 dashboard (and more) can also be done through this API
という状態が理想です↩ -
Performance of query with many
relations
· Issue #3857 · typeorm/typeorm↩