年末休みにnodeでAPIサーバを勉強した復習〜part1 設計編

#はじめに
https://qiita.com/YukiMiyatake/items/9cef90e2a18573e7bca3
ここで　年末休みにnodeを勉強した内容をまとめたが
その中身を細かく切り出す

##目的
いま新人教育をしているが、その新人教育をオープンにする
私自身 出し惜しみはしない。むしろ もし私の知識で少しでも世の中が便利になるのならラッキーだ
また　間違った事は　公開すれば教えてくれるはず

#検討
まずは新人教育のための題材を作ることからはじめた

##制作物
スマホのソーシャルゲーム用のAPIサーバ
自分がゲームエンタメ業界が多いので、ソーシャルゲームのサーバは持っていて損はない
以前 JavaPlayframeworkで作っていたが そろそろ作り直したいと思っていた

##言語
ソーシャルゲームのAPIサーバはほとんどがDBからReadしたりそのままWriteしたりCPU処理は少ない
マイクロサービス化も検討しているため、重い処理があればその部分は別の言語で動かすことも出来る
そのため　言語自体の速度はそこまで気にするシーンは無いはずである
最も重要なのは、クライアントからのコネクション時の起動やI/Oである

###Perl、PHP、Python、Rubyなど
これらのスクリプト言語は基本的に非同期処理を行なう事が難しい
（非同期ライブラリもあるが　一般的ではないしパフォーマンスも出ない）
そのため、コネクションごとにプロセスを起動する事になるが　上記の理由で
プロセス起動コスト、タスク上限問題（C10K問題）のため、私の中では選択肢に入らない
却下

###Haskell、Erlang、Elixir・・・
マイナー言語というと怒られるかもしれないがマイナー言語である
非同期処理は得意であるが、お客さんに納品しにくいので却下した
個人的には　嫌いじゃない言語だけどね

###C#、Kotlin、Swift
C#は クロスプラットフォームになってきてるけど、まだベンダーロックインに分類した
ベンダーロックイン言語は使いたくない
C#はIL2CPPとか使えると面白くなるんだけどね
C#は言語的に優れてると思うんだけどね・・
今回とは別で C#サーバは勉強したいが　今回は却下

###Scala
コンパイルの遅さが嫌い　それ以外はとってもいい
いずれScalaサーバは勉強しますが今回は却下で


###C++、Java、Go、node
非同期が得意でメジャー言語、特に欠点のない言語で　この選択肢になった

C++は新人にはまだ難しく 言語の習得に時間がかかるので今回は却下した
Goは以前プロダクトに使ったが、文法が20年ほど古く これ以上使う気がおこらない

Javaは速度も速く、システム系では最も使われていて 仕事的に何も不自由ないので教えたいと思った
が、nodeを選んだ。
この2択は　かなりどちらでも問題なかったが　あえていうなら、AWS　LambdaのようなFaaSサービスに使いやすいからだ

もちろんnodeには弱点がある
Javaに比べると遅いし、同時接続も非同期にしては弱い。
だが　フロントエンドでもJavaScriptは使うので　この際覚えてもらうには良いかと。

##FW
nodeに決めたからにはフレームワークは node、Express、MongoDB に決めた
また　APIキャッシュとしてRedisを使い、課金など重要なDBは MySQLの併用を検討している
Typescriptを必須にした。
このあたりは　nodeではよくある構成と思う

##実行環境
Docker（DockerCompose）を前提で作る
AWSを考えているが他のサービスでも可能
マイクロサービス化も当然考えている
開発はWindowsでもMacでもDocker使えば問題ない

##ソース管理、タスク管理、スケジュール管理
ソース管理はGithubかGitlabか悩んだが　機能が多く全部つかいこなしてないGitlabの採用を決めた
理由は特にないが、機能豊富でサイトも軽いので不自由がないからだ
タスク管理も GitlabのIssueで管理することにした。かんばんがデフォルトでついてるのは良い
スケジュールは　建てないことにした。それよりは確実に勉強すること

##DevOps
とりあえずSlackBotを導入して　コミットやマージリクエスト、Issueを通知させることにした

#設計
設計もGitlabのWikiで行った

##DB,API
DBは基本的には user_idをキーとした1テーブルでほとんどカバー出来るが
フィールドが多いと R/Wが遅くなる、マイクロサービス化したときに同じテーブルだとコンフリクトする
ため、機能ごとにテーブルをわけた
細かいフィールドはゲームの内容で変わるのであとで考えるとして
Account、ダウンロードデータ、ステータス、カード、デッキ、アーチーブメント、ログボ、フレンド、ブロック、課金・・・
と詳細に分割した
また　APIも使うDBに紐づけた

#環境構築
##Docker
AlpainLinuxをベースに、node、Mongo、Redis、MySQLのイメージを使う
dockerfile、docker-composeを設定して　難なく動かす
これで環境構築が終わる　素晴らしい世の中だ

```bash
version: '3'
services:
  node:
    build:
      context: ./server/docker/construct_node # Dockerfile保存場所
    image: sss-node                           # イメージ名
    container_name: sss-node                  # コンテナ名
    tty: true
    ports:                                    # ローカルとホストのポートを接続
      - 3000:3000
    volumes:                                  # 現在のディレクトリをworkdir(/src)にマウント
      - ./:/src

  mysql:
    build:
      context: ./server/docker/construct_mysql # Dockerfile保存場所
    image: sss-mysql                           # イメージ名
    container_name: sss-mysql                  # コンテナ名
    ports:                                     # ローカルとホストのポートを接続
      - 33060:3306
    volumes:                                   # databaseをホストと共有
      - ./server/docker/construct_mysql/data:/var/lib/mysql
    environment:
      - "MYSQL_ROOT_PASSWORD=root"             # ルートのパスワードは必須

  mongo:
    build:
      context: ./server/docker/construct_mongo # Dockerfile保存場所
    image: sss-mongo                           # イメージ名
    container_name: sss-mongo                  # コンテナ名
    ports:                                     # ローカルとホストのポートを接続
      - 27018:27017
    volumes:                                   # databaseをホストと共有
      - ./server/docker/construct_mongo/data:/data/db

  redis:
    build:
      context: ./server/docker/construct_redis # Dockerfile保存場所
    image: sss-redis                           # イメージ名
    container_name: sss-redis                  # コンテナ名
    command: redis-server --appendonly yes     # データをファイルに保存する形式に指定
    ports:                                     # ローカルとホストのポートを接続
      - 16379:6379
    volumes:                                   # databaseをホストと共有
       - ./server/docker/construct_redis/data:/data
```

さすが新人コメント多い


##モジュール
npmでは色々インストールした
express、mongooseなど

##Typescript
最初はWebpackを導入したが　やはり消耗した・・
が最終的には package.jsonだけでなんとかなった！
concurrentlyを導入したが

```json
  "main": "src/app.ts",
  "scripts": {
    "start": "npm run serve",
    "build": "npm run build-ts",
    "serve": "node dist/app.js",
    "watch-node": "nodemon dist/app.js",
    "watch": "concurrently -k -p \"[{name}]\" -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold,green.bold\" \"npm run watch-ts\" \"npm run watch-node\"",
    "test": "jest --forceExit --coverage --verbose",
    "watch-test": "npm run test -- --watchAll",
    "build-ts": "tsc",
    "watch-ts": "tsc -w",
    "tslint": "tslint -c tslint.json -p tsconfig.json",
    "copy-static-assets": "ts-node copyStaticAssets.ts",
    "debug": "npm run build && npm run watch-debug",
    "serve-debug": "nodemon --inspect dist/server.js",
    "watch-debug": "concurrently -k -p \"[{name}]\" -n \"Sass,TypeScript,Node\" -c \"yellow.bold,cyan.bold,green.bold\" \"npm run watch-sass\" \"npm run watch-ts\" \"npm run serve-debug\""
  },

```

nodemonでファイルの変更を監視しJSファイルが変更されたらnode再起動
これで快適だ


#HelloWorld

まずはJavaScriptでテストをしていく

```js
var express = require('express');
var app = express();

app.get('/', function(req, res, next) {
  res.send("Welcom to SSS");
});

app.listen(3000);
```


docker-composeでちゃんと動いたので
開発が可能になった