かなり速いらしいRustのフレームワークActix web + docker composeでシンプルなwebアプリを作った(作ったものはgithubにあげてます)

## 背景
去年だとおもいますが、とある企業で面接をうけまして、その際に技術力を見るテストとしてRust + React + Next.jsでシンプルなwebアプリを作りました。数日で面接用に作ったものなので完成度はあまり高くないと思いますが、なるべく本番利用に耐えられるようにかんがえてつくったものなのでActix webを使ってなにか作りたいという人には参考になるとおもいます。Next.jsでSSGしており、読み込みも早いはずです。
URL: https://github.com/lechatthecat/assignment

開発をするたびにDocker composeのyamlファイルだったり、APIハンドラーだったり、Nextjsの設定だったり、
毎回いちいち同じような実装をするのが面倒だし、「あれ、あれってどうやるんだっけ」ってなることもあるので、とりあえず動いてはいるこれをもとに開発をすると早いと思います。
海外の人が面接官だったので中身は全部英語になっていますが、ご了承ください。
その企業には結局いかなかったのですが、せっかくつくったこのレポジトリを眠らせたままにしておくのはもったいないのでみんなに共有します。その会社に関連しそうな部分は削除してGitのヒストリーからも消してます。
ただし、面接の条件として、DBのORM library（Diesel等）を一切つかうなとのことだったのでORM系は一切入っていません。これを参考にする際にはそのあたりに留意をお願いします。実際にこれをもとに開発をする際にはDiesel等の導入が必要になります。
あとテストコードの書き方はあまり参考にしないでください。。あくまで時間がない中で作ったものを参考のために共有ということでお願いします。

なおRustのweb frameworkのActix webはそうとう速いらしく、数年前は下記のベンチマークでトップにいました（うろ覚え）。
https://www.techempower.com/benchmarks/#hw=ph&test=fortune&section=data-r22

いまはだいぶ順位が下になっていますが、Actix ecosystemのactix-httpはそれでも2024/8/15現在で12位にいます。たぶんそこそこ成熟したframeworkとしてはかなり速い部類なんじゃないかな。
Actix webはググっても色々な情報がでてきます。
https://jitera.com/ja/insights/22518#:~:text=Actix%20Web%E3%81%AF%E3%80%81Rust%E8%A8%80%E8%AA%9E%E3%81%A7%E9%96%8B%E7%99%BA%E3%81%95%E3%82%8C%E3%81%9F%E5%BC%B7%E5%8A%9B,%E3%82%92%E5%AE%9F%E7%8F%BE%E3%81%97%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82

## 使い方
使い方はREADME.mdにも書いてありますが、
```
$ git clone https://github.com/lechatthecat/assignment
$ cd assignment
$ docker compose up --build -d
```
そうするとRustのリリースビルドが始まって以下のURLからログイン画面が開けるようになります。
http://localhost/login

この情報でログインを行ってください。
name: test_user1
password: password

### 開発用に
なお、dev側のデバッグ用のdocker-compose_dev.ymlを使う場合はこんな感じになると思います。
```
$ docker compose -f docker-compose-dev.yml up -d --build
```

## 何をするアプリなのか
### 構成
Actix webによるAPIサーバーと、それを呼び出すReact + Next.jsアプリにわかれています。
どういうAPIが使用可能なのかはAPI handlerをみてください。
https://github.com/lechatthecat/assignment/blob/main/simple_restaurant_api/src/api/api_handler/handlers.rs

APIサーバー, nginx, postgresqlをDocker composeでそれぞれコンテナを作成して立ち上げるようにしています。`frontend`フォルダーのコードはnginxのコンテナ内に自動で同期され、nginxがそれを返してます。
ただし`/api`が先頭につくリクエストはすべてnginxがActix webサーバーにリダイレクト（リバースプロキシ）しています。それ以外のリクエストのみnginxコンテナ内の`frontend`を参照します。

### 概要
現在テーブルが10個あるレストランで使うことを想定したシンプルなSPA（Next.jsでSSGしているのでSPAという名称が正確なのかよくわからないですが）で、ウェイターがそれぞれタブレットをもって、タブレットから呼び出して、注文を受ける際にまずどのテーブルの注文なのか、テーブルを選択します。
![Screenshot from 2024-08-15 10-54-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260163/fdaa446c-dd40-4231-30d6-f056213972a3.png)

選んだらその画面の現在の受領済みオーダー一覧画面にいきます。
![Screenshot from 2024-08-15 10-54-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260163/f97bc4e8-91c2-f106-ac68-b81bc3901362.png)
Menuを押すとメニュー一覧ページに行くので、そこからオーダーされたメニューをクリックします。
メニューには料理の値段と準備ができるまでの想定時間がかかれています。
`Add this to order`を押すと受領済みオーダーにそのメニューを追加できます。
![Screenshot from 2024-08-15 10-56-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260163/b7f98f9f-c498-36aa-0817-77914c7b7087.png)

追加すると準備完了までのリアルタイムのカウントダウンが始まります。ゼロになったらウェイターがキッチンに料理を取りに行ってそのテーブルのお客さんに料理を出す、という感じの使い方を想定しています。
だいたい何時ころに料理の準備が完了するかの時刻も見れるようになっています。
![Screenshot from 2024-08-15 10-58-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260163/1cfcd7d9-5aa4-c32b-360b-9ef52d1ad42f.png)

なんだよ機能自体は大したことないな、って思いましたね。繰り返しますが面接用に数日で作ったので最低限しか実装していないです。。

## 追加でやったほうがいいこと
To DOとして、以下があります。
- APIのドキュメントが一切ないのでSwaggerを導入してそのあたりドキュメント化をしたほうがいいとおもっています
- RedisによるAPI側の処理のキャッシュ, Cloud flareなどのCDNキャッシュの導入もやるとサーバー負荷が下がるはずです。NginxによるProxy cacheもありですね。今回は内部の人が使うようなので大きな負荷はかからないはずであり、そのあたりはスキップしちゃいましたが、一般に公開するようなアプリの場合はキャッシュ処理の導入も必要になるはずです。
- 負荷がかかるようなサービスをホストする場合では、APIサーバーのコンテナ, frontend用のコンテナ（今回はNextjsによるSSGなのでこれはいらない）, ロードバランサ（=Nginxでできる）のコンテナは分けて、docker composeじゃなくてkubernetesでコンテナの管理をするとスケールアウト・スケールインが楽でいいと思います。
- `docker-compose.yml`の`myrust`のvolumeが以下のようにファイルごとにbindされています。これは`target`フォルダをbindしてしまうと、せっかくコンテナ側でbuildした`target`フォルダがホスト側の空の`target`フォルダで上書きされてしまうためです。ここは開発用にローカルで起動したときの利便性を考えると、ファイルごとではなくフォルダごとの同期のほうがいいかもしれないです。たとえば同期はフォルダ単位にしてbuild用のshell scriptをコンテナ立ち上げごとに自動実行するなど。（まあ必須な対応じゃないと思いますし、開発用には開発用の`docker-compose.yml`を使うようにするとかでも解決できます。build用のshell scriptは、`Run`ではなく`command`で実行する場合、`docker compose up`コマンドが完了してもスクリプトの`build`コマンドが終わるまでサービスが404になってしまうので面接官が混乱するかと思い採用しませんでした。。ん？でも、書いてて思ったけどホストマシンのディレクトリのマウントって本番へのデプロイなら必要ないかも。やっぱりyamlを本番と開発で分けるのが一番か)さらに言えば面接に使うもんでなければホスト側であらかじめビルドしたtargetをコンテナ側に同期するほうが効率的だと思います。
```yml
volumes:
    # log
    - ./logs:/simple_restaurant_api/log
    # Rust code
    - ./simple_restaurant_api/src:/simple_restaurant_api/src
    - ./simple_restaurant_api/Cargo.lock:/simple_restaurant_api/Cargo.lock
    - ./simple_restaurant_api/Cargo.toml:/simple_restaurant_api/Cargo.toml
```
- `frontend`ではNextjsのoutディレクトリをコミットしてしまっていますが、本当はnpm run buildをcontainer立ち上げのたびに行い、outディレクトリをコミットするのではなく生成する方式のほうがいいです。主にAPI側の実装をみる面接だったということで、面接官にいちいち`npm run build`コマンドを打ってもらうのもどうかなと思い。。本番で使うならここは修正したほうがいいと思います。（たとえば生成用のshell scriptを`command`で自動実行するようにするなど。`Run`でやる場合はファイルごとの同期に変えないとだめかも）
- 本当はオーダーを実際に厨房に伝える前に、お客さんに「〜という注文でよろしいですか？」という確認が入るはずなので、確認をするためのオーダーを仮に受ける（厨房には伝えない）という機能があったほうがいいんですが、フロント側の開発になりそうだし、本来はAPI側をみるための試験なのでスキップにしました。
- Production前提なのにunwrap関数をテスト以外で使っちゃってますがRustではお行儀の悪い書き方なのでここは直したほうがいいです。unwrapのかわりに使うべきなのはexpect関数（エラー時に任意のメッセージを一緒になげられる）、あるいは外部処理を呼び出している等Productionでもエラーになりうるようなところはmatch構文によって明示的にエラー処理をしたり、または[or_else](https://stackoverflow.com/questions/61995572/how-to-handle-exception-err-in-unwrap-or-else)関数を使うのもいいでしょう。