2019/08/25

[Kails] Koa2を使ったRails風のnode.jsオープンソースフレームワーク

オリジナル文書は Embbnuxさんのブログです。再掲載にあたっては元記事へ参照とリンクを保持してください。
This article was first published in Blog of Embbnux. For reprinting, please indicate the source of the original text and keep the link of the original text.
https://www.embbnux.com/2016/09/04/kails_with_koa2_like_ruby_on_rails
直接の翻訳元はhttps://developpaper.com/kails-an-open-source-project-of-nodejs-similar-to-rails-based-on-koa2/ です。

私たちはkoa2フレームワークを調査し、ミドルウェアというアイデアがとても気に入りました。ミドルウェアで簡単にいろんなサービスを利用できるのは便利ですが、Webサイトを高速に開発できるようなフルスタックのフレームワークからは少し離れていることも分かりました。
そこで一般的なrailsフレームワークを参考に、koa2ベースのrails風のフレームワークとしてkailsを作りました。
postgresとredisを使用し、MVCフレームワークと、フロントエンドwebpack、およびisomorphic フロントエンド開発が含まれています。ここでは主に、kailsの構築におけるさまざまな技術スタックとアイデアを紹介します。

koaは、元々expressを開発していたチームが開発しています。ミドルウェアのアイデアに基づいた新しいフレームワークの実装で、主にES6のジェネレーター機能を使用しています。ただ、koaはexpressとは異なり、Webサイト開発に対応するためのフレームワークを提供しようとはしていません。基本機能のモジュールや、Webサイトを作るための多くのモジュールを導入する必要があります。そのため、モジュールの選択によって様々なKoaプロジェクトがあります。kailsはRuby on Railsに似せたkoaプロジェクトで、その名前の由来もrailsにあります。

プロジェクトURL: https://github.com/embbnux/kails (Pull Requestを歓迎します)

kailsのディレクトリ構成です。
├── app
│   ├── assets
│   │   ├── images
│   │   ├── javascripts
│   │   └── stylesheets
│   ├── controllers
│   ├── helpers
│   ├── models
│   ├── routes
│   ├── services
│   ├── views
│   └── index.js
├── config
│   ├── config.js
│   └── webpack
│       ├── base.js
│       ├── development.js
│       └── production.js
├── db
│   └── migrations
├── index.js
├── package.json
├── public
└── test

2019/08/19

Sequelize-autoでリレーションも付ける(NodeJSでER図先行開発)

NodeJSのRDB向けのORMである、Sequelizeを使ってER図先行開発をする方法を紹介します。Sequelizeの使い方ではなくSequelizeで使うためのモデルクラスを、既存のDBのテーブルから自動生成する方法です。
これができると、

  1. ツールを使ってER図を描く
  2. SQL(AlterSQL)を発行してテーブルを作る
  3. モデルクラスを自動生成してコーディングする

という開発手順を回せます。この手順を確立するとプロジェクトファイルにER図が常に正しい状態で存在することになり、開発の継続性能がぜんぜん違います。
もし説明を長々と読むのが嫌な場合、erf_devのmodels/index.jsを見てください。また、これのためにモデルを生成するコマンドはpackage.jsonのdb:modelsを見てください。
既存のテーブルからモデルを自動生成するためにSequelize-autoを使います。Sequelize-autoはモデルクラスを自動生成するツールなので、贅沢を言わなければ「MySQL Workbench」などのER図ツールからSQLを発行してテーブルを構築し、Sequelize-autoでモデルクラスを生成すれば終わりです。ですがこれだけだと外部キーによる子テーブルのデータ取得ができず、ORMとしては致命的です。

Sequelizeでリレーションを扱うには、モデルクラスを定義した後でそれぞれのモデルに対し、hasMany()やbelongsTo()などのリレーションを設定します。
Sequelize-autoではそこまでのコードは生成しないようなので、ややトリッキーなことをします。

① 1対1の関係、1対多の関係を設定する

元々のアイデアはsequelize-auto/issues/34 にあるもので、例えばuserテーブルとuser_bookテーブルが「1対多」の関係の場合、user_tableにはuserのidとして「user_id」があるはずです。

実際には「user_id」という名前でないカラム名を付けることもできますが、慣例として(Javaであれば大文字始まりはクラス名というくらい常識的な命名規則として)「テーブル名_id」というカラム名で外部キーを設定するはずです。
この命名を利用して「Id」でsplitして連携先のテーブルモデルを探し出し、そのモデルと連携させます。文字列処理をするため、少し違う命名でテーブルを作ってしまうと正常に動かなくなってしまう、という危険はありますが、逆にこのことさえ理解してテーブル設計をすれば非常に便利に連携まで作れるようになります。

上の例ではuser.hasMany(user_book)と、user_book.belongsTo(user)の2つを設定します。
上記Issuesには完全なリレーション設定までのサンプルコードは載っていないですが、少し工夫すれば作れます(erf_devのmodels/index.jsを見てください)
もし1対1ならhasMany, belongsToの代わりにお互いにhasOne( )を設定します。

② 多対多の関係を設定する

多対多の場合はさらにトリッキーとなり、元々RDBでは多対多のテーブルは扱えないため、間にJunctionテーブルを設けます。よくある例で、学生は複数の講義を受講し、講義は複数の学生が受講する、と言う関係では、学生テーブル、受講テーブル、講義テーブル、と言う構成にします。

Sequelizeでトリッキーなことをしないでも基礎的なhasMany( )belongsTo( )だけで、学生から複数の受講を取得し、受講から各講義を取り出す、という2段構えで取得できます(講義も同様で講義から複数の受講を取得し、受講から各学生の情報を取り出すという方法です)。

ただSequelizeには多対多の関係を扱える仕組みが使えます。そのためには間のJunctionテーブルを「テーブル名_has_テーブル名」という命名規則にして、「_has_」でsplitし、互いのモデルクラスを直接多対多として結びつける、ということを行います。
学生.belongsToMany(講義)と、講義.belongsToMany(学生)として、間のJunctionテーブルを隠して直接学生から受講している講義を、講義からは受講生を取得できるようになります。

③ その他の設定

最後にテーブルのカラム名はスネークケースですが、JavaScriptの要素としてはキャメルケースで取る、という慣例に沿ってSequelize-auto、Sequelizeの設定をして完成です。

①、②のコードの主要な部分は以下です。
/**
 *  setup
 *  belongsTo(), belongsToMany(), hasMany(), hasOne()
 *  based on references
 */
log('────────────────────────────────────────────────');
sequelize.modelManager.models.forEach(model => {

  const isJunctionTable = model.tableName.split('_has_').length === 2;
  if (isJunctionTable) {
    const tables = Object.getOwnPropertyNames(model.rawAttributes)
      .filter(attributeName => undefined !== model.rawAttributes[attributeName].references)
      .map(attributeName => {
          const refModel = sequelize.modelManager.models
            .find(testModel => testModel.tableName === model.rawAttributes[attributeName].references.model);
          return {
            model: refModel,
            through: {
              through: model,
              as: attributeName.split('Id')[0],
            }
          };
        }
      );

    log(tables[0].model.tableName, ' 1..* ──────── 1..* ', tables[1].model.tableName);

    tables[1].model.belongsToMany(tables[0].model, tables[0].through);
    tables[0].model.belongsToMany(tables[1].model, tables[1].through);

  } else {
    Object.getOwnPropertyNames(model.rawAttributes).forEach(attributeName => {
      if (model.rawAttributes[attributeName].references) {

        const refModel = sequelize.modelManager.models
          .find(testModel => testModel.tableName === model.rawAttributes[attributeName].references.model);
        const belongsToOptions = {
          foreignKey: model.rawAttributes[attributeName].field.toString(),
          as: attributeName.split('Id')[0],
        };

        const options = {};
        if (model.rawAttributes[attributeName].primaryKey) {
          log(refModel.tableName, ' 1 ──────── 1 ', model.tableName);
          refModel.hasOne(model, options);
        } else {
          log(refModel.tableName, ' 1 ──────── 1..* ', model.tableName);
          refModel.hasMany(model, options);
        }
        model.belongsTo(refModel, belongsToOptions);
      }
    });
  }
});
log('────────────────────────────────────────────────');
また、erf_devのmodels/index.jsも見てください。
①、②の命名規則はerf_devのER_Diagram.mdを読んでください。
③については、
erf_devのpackage.jsonと、erf_devのsequelize-auto_additional.jsonを見てください。

バラバラでは分かりにくいはずなので、全てをまとめて動かしてみるには、
是非erf_devのTutorial.mdを試してみください。


How to Automatic generate "sequelize models" with All associations from MySQL Tables.

2019/08/18

Arch Linux をメインマシンに使ってみる

当ブログの閲覧者の解析結果について

当ブログを構築しているGoogleのブログサービス、Bloggerは簡単な閲覧者解析が付いている。
結果は、ブラウザはChromeが46%、OSはWindowsが82%となっている。
自分の周りだけ見ると林檎のマークのコンピュータが多いように感じるが、実際はWindows一色なのだ。

2位以下のブラウザはInternetExplorer, FireFox, ...となっている。
OSはMacOS, iOS, Android, Linuxの順だ。

Linuxは1%以下

さてどうしてLinuxはこんなに少ないのかな、という疑問。

僕が使ってるマシンはいわゆる自作PCで、パーツごとに買ってきて組み上げるタイプのマシンだ。
それを組む前はMacBookをメインに使っていたのでOS移行して良いものか、かなりの迷いがあったのだが、MacBookを使うのは『MacOSはUnixなのでWebサービス開発に便利』という理由。
「そんならむしろサーバーOSと同じCentOSの方が良いじゃん?」と思い、自作PCにLinuxを入れてメインの開発用マシンにしてみようと思い立った。

自作PCでOSを買わない場合、同スペックのMacProやiMac(デスクトップ型のMac)を買うよりも10万円〜20万円程度安くなる(ちょっと言い過ぎかも)。この差は大きい。

その後いろいろ考えを巡らせ、LinuxのディストリビューションはCentOSでなく Arch Linux を使うことにしてみた。

Linuxは既にメインマシンに使える状態

実際リナックスをメインにしてみてどうだったのか。
Archリナックスなので、インストールだけはえらい大変だったけどそれはCentOSとかにすれば良い。

実はPCを使う作業のうち、ブラウザ(Chrome)が使えれば問題ない、というものがほとんどだ。
その他はOffice系ソフト、DVDや動画を見るソフト、そして開発ツール類。
大半の人は既にLinuxに移行しても問題なくなっているはずだ。

特にWeb系の開発作業のためだけにMacBookを使ってる人はWindowsのノートPCにLinuxを入れて使っても問題無いはず。もちろん難しくなっている部分はあるけど、Macでターミナルを使い、HomeBrewを使うレベルならLinuxとあまり変わらんと思う。

MS純正のOfficeが無い、プリンタードライバが無い、などが場合によっては致命的な機能不足になることはあると思うので補完するノートPCは用意してある。

2019/08/14

ER図先行型開発


もし何かアイデアがあって、Webサービスを開発運営したいと思ったら、エンジニア以外の人は画面のデザインから作り始めて画面のデザインだけで終わってしまうでしょう。
しかし懸命なエンジニアである皆さんは、画面のデザインと同時にデータベースの設計も、RestAPIが何を受け取って何を返すのかも、その全体が機能するためのインフラの構成も全てに考えを巡らせるでしょう。
そもそもアイデアがあるということはデザインはともかく画面のラフスケッチはすでにある状態のはずです。

結局のところ、そのラフスケッチから実際に稼働するWebサービスを、エンジニアが形にするためには、ER図によるデータベース設計から始めることが必要なのです。
しかしグラフィカルなER図から始めてサーバーサイド処理を構築でき、気軽にER図に変更を加えてサービスを進化開発できるようなフレームワークは、Ruby On RailsのようなWebアプリケーションフレームワークと比べるとほとんど存在しない状態です。
その数少ない一つの例です:https://erfdev.wixsite.com/index