🎨sketch

投資不動産の売主と買手のための取引プラットフォーム

概要

投資不動産売主と買主のための取引プラットフォームです。売主は物件を登録すると物件の詳細が自動で生成され、買主は自身の買いニーズにマッチする物件情報の配信を受けることができるWebサービスです。

案件の管理や、各種データの自動生成など業務を捗らせるための様々な機能を提供していきます。

元々はPMFを探る段階で、Google Workspaceやノーコードツールで構成されていましたが、2023年5月から、サービス刷新のためフルスクラッチでの開発を行っています。

開発の始め方

どのように開発を始めるか、READMEを一部公開します。

# sketch

## Synopsis

```

### install dependencies
$ make deps

### build applications
$ make build

### run applications
$ make run

### test your github-actions
$ make test-github-actions

### and show helps about all tasks
$ make help

```

## Applications Port

| app        | port | local url              |
| ---------- | ---- | ---------------------- |
| client     | 4000 | http://localhost:4000/ |
| server     | 4001 | http://localhost:4001/ |
| public_api | 4002 | http://localhost:4002/ |
| worker     | ---  | ---                    |

## GitHub Actions

### Testing

We use [`nektos/act`](https://github.com/nektos/act/blob/master/README.md) for testing GitHub Actions workflows.

## Database Branching

We should create a detabase branch when develop on localhost.

It is a step-by-step recture of database branching in below.

### 1. Login to your PlanetScale account with `make auth-pscale`.

If you are never touched to this project, you should to login to the PlanetScale with below command before begin your development.

```
$ make auth-pscale
```

You will then see the following announcement, which you can follow to log in.

```
Failed to open a browser: exec: "xdg-open": executable file not found in $PATH

Confirmation Code: XXXXXXXX

If something goes wrong, copy and paste this URL into your browser: https://auth.planetscale.com/oauth/device?user_code=XXXXXXXX
```

### 2. Create a git branch.

You should create a new git branch before create a database branch.

```
$ git branch feature/yourname-feature-name
```

### 3. Create a database branch with `make create-db-branch`.

```
$ make create-db-branch
```

As example, a database branch name is `feature-yourname-featurename` when your git branch name is `feature/yourname-featurename`.

### 4. Run apps and database with `make run`

Now, `make run` boots apps, and database.

```
$ make run
```

Then, you can see logs as lile below.

```
./scripts/shoreman
05:56:57 azurite        | 'azurite --silent --location /tmp --debug /tmp/debug.log' started with pid 31481
05:56:57 server | bash: denon: command not found
05:56:57 server | 'cd apps/server && denon run --allow-net --allow-read --allow-env ./src/index.ts' started with pid 31491
05:56:57 db     | 'make run-db-branch' started with pid 31499
05:56:57 db     | make[1]: Entering directory '/workspaces/toggle_sketch'
05:56:57 db     | run database branch feature-sazuma-planetscale-branch
05:56:57 azurite        | Azurite Blob service is starting at http://127.0.0.1:10000
05:56:57 azurite        | Azurite Blob service is successfully listening at http://127.0.0.1:10000
05:56:57 azurite        | Azurite Queue service is starting at http://127.0.0.1:10001
05:56:57 azurite        | Azurite Queue service is successfully listening at http://127.0.0.1:10001
05:56:57 azurite        | Azurite Table service is starting at http://127.0.0.1:10002
05:56:57 azurite        | Azurite Table service is successfully listening at http://127.0.0.1:10002
05:56:59 db     | Secure connection to database toggle_sketch and branch feature-sazuma-planetscale-branch is established!.
05:56:59 db     |
05:56:59 db     | Local address to connect your application: 127.0.0.1:3306 (press ctrl-c to quit)
```

And you can access to database-branch with `mysql -h 127.0.0.1 -uroot`

### 5. Make some changes on database branch.

Yay! You can begin your development!

### 6. Create a deploy-request with `make deploy-request-db-branch`

You can create a deploy-request if you want to deploy your changes in database with below command.

```
$ make deploy-request-db-branch
```

## Schema Deploy Flow

1. Create a new branch on your git repository
2. Create a new DB branch with `make create-db-branch`
3. Make changes to `packages/prisma/schema.prisma`
4. Deploy your changes in schema to your DB branch with `make push-schema`
5. Create a pull-request on GitHub
6. Create a deploy-request on PlanetScale

これらの環境は、いちいちローカルにチェックアウトせずにGitHub Codespacesで起動できるようになっているため、開発者は生産的でない開発環境の整備は必要ありません。

技術スタック

選定基準

「フルスタック」なものを使わない。フルスタックなものを使うことにより、以下の問題があると考えられるため、これを避ける観点で選定しています。

  1. コンポーネント設計に対する制約が課せられる

  2. 記法が複雑化する傾向がみられる

  3. メモリ空間に対するフットプリントが大きくなる傾向がみられる

  4. 他の技術への乗り換えが困難となる

  5. 規模が大きくなった時にコンポーネントの分割が難しくなる

全般

全方位TypeScriptで作ることとし、リポジトリはモノレポ構成です。

.
├── Makefile
├── README.md
├── apps
   ├── client_webapp
   ├── public_api
   ├── server
   └── workers
├── infra
   ├── README.md
   └── index.ts
├── package.json
├── packages
   ├── api
   └── schema
└── scripts

パッケージマネージャは安全性と速度の観点からpnpmを採用しました。

フロントエンド

Vite + React + Ant Design を採用しています。

  • Viteはesbuildを用いた開発サーバでビルドが高速なので開発者体験が良く、Pluginも充実してきているため採用

  • Reactはもう特に議論の必要はないレベルでフロントエンドのデファクトだと思います

  • Ant DesignとChakraで議論になりましたが、B2Bの管理画面向けとして使えるコンポーネントがChakraよりも多くあるため採用

バックエンド

Hono + Prisma を主なコンポーネントとして採用しています。

  • HonoはWAFとしてシンプルに振舞うため、乗るアプリのコンポーネント設計と切り離すことができ、高速であるため採用

  • Prismaはprisma.schemaの定義ができていればTypeとテーブル双方の定義が出来上がるなど、データベースを使った開発の生産性を高めることができ、ORMとしても一定のシェアを持っていることから採用

アプリケーション構成としてはレイヤードアーキテクチャに倣った構成を取っています。

レイヤーはそれぞれ、以下のような配置をしています。

apps/server/src
├── domain      // 副作用を伴わない純粋なビジネスロジックの配置場所
├── handler     // クライアントからのリクエストをどの処理に流すかを制御するプログラムの配置場所
├── infra       // データベースなどと通信するプログラムの配置場所
├── service     // 実態としての処理が行われるプログラムの配置場所
├── test        // 主にdomainに対するテストプログラムの配置場所
├── transaction // Handlerから処理をうけとり、複数のServiceやDomainを連携した処理プログラムの配置場所
└── views       // テンプレートエンジンetaのテンプレート。メール送信の際のテンプレなどを配置

データベース

PlanetScaleを採用しているのが大きなポイントです。

PlanetScaleは、いわゆるサーバレスのDB as a Serviceです。PlanetScale最大の特徴は「JITスキーマ変更」と「ブランチ」の概念があることで、これらが開発者生産性に大きく寄与すると判断したため採用しています。

  • JITスキーマ変更:本番環境でのスキーマ変更をダウンタイム無しで行え、かつ切り戻しにも対応している。

  • ブランチ:ブランチを作成すると、既存のDDLを引き継いだ別のデータベース接続エンドポイントが作成されます。更にスキーマ変更を「Deploy Request」として管理することができるため、開発者が開発者のメンタルモデルでデータベースを扱うことができる点が、他のDBaaSと大きく異なる利点です。

インフラ(IaC)

「全方位TypeScript」の実践として Pulumi を採用しています。

Pulumi を採用したことで、インフラ関連の操作も単純なTypeScriptのプログラムに落とし込むことができ、自動化も一般的なTypeScriptのプログラムのビルドプロセスとほぼ変わらない形で実行できるようになっています。

また、後述のインフラへの更新反映などがシンプルになるのと、プログラマであってもインフラにどのようなものが展開されているのかという事を把握することができるようになりました。

プルリクのレビュー

  • プルリク画面の「Review in codespace」からCodespaceを起動 or 既存のCodespace上で gh pr checkout [pr-number] してプルリクを手元に持ってくる。

  • make auth-pscale でPlanetScaleにログイン。

  • make run すると、プルリクに対応するデータベースブランチを参照した状態で起動するため、いちいち自分でデータベースへのMigrationなどを走らせる必要はありません。

リリース戦略

リリースノートの自動生成とビルド

リリース前にはその準備を自動で行うGitHub Actionsを用意しています。

このActionsを通じて

  1. リリースタグの作成(いつのリリースかなどをわかりやすくするため、calvarを採用しています)

  2. リリースノートの作成(前回のリリースノートからのマージ差分を抽出しての自動作成)

  3. コンテナのビルド

  4. ビルドしたコンテナをレジストリへプッシュ

  5. GitHubのReleaseを作成

という一連の作業が行われ、フロントエンド、バックエンド、インフラへの反映準備ができます。

インフラへの更新反映

こちらもGitHub Actionsで実行できるように整備しており、↑の手順後に作成されたリリースの内容を本番環境に反映させることができるようになっています。

その時のリリースに、インフラへの変更を必要とする場合にはそのタグを指定してActionsを実行します。「Is dryrun?」のチェックを外さない限り本番環境への変更は行われないので、事前に実行してどのような変更があるかの確認も気軽に行うことができます。

データベースへの更新反映

データベースにはPlanetScaleを利用しており、Deploy Request機能を用いてこれを実現しています。

開発者はCodespace上でブランチを作り適切な初期化処理を走らせることでPlanetScale上にデータベースのブランチを作成し、その上で開発作業を行うことができます。データベースへの変更がある場合、コード変更の際のPull Requestのように反映依頼を出します。

反映依頼はPlanetScaleのコンソール上で、GitHubのPull Requestをレビューするのと同じようにレビューし、本番環境のデータベースに反映させることができます。

バックエンドのデプロイ

「リリースノートの自動生成とビルド」で作られたコンテナを用いてデプロイする手法を取っています。PaaSのインプレースデプロイは「新しいコンテナをpull→起動してヘルスチェックがクリアになる→既存コンテナと置き換わる」という挙動で差し替えが行われます。

指定のタグでビルドされたコンテナを送り込む処理を実行することができるGitHub Actionsを作成して、そちらで実行しています。

同時にPaaSのログストリームを確認しながら実施するやり方を取れるため、本番でしか出ない不具合に遭遇してもすぐに戻すことができます。

フロントエンドのデプロイ

フロントエンドへのリリースはGitHub Actionsを用いて行っており、こちらもリリースタグを指定して実行することでデプロイを行います。

フロントエンドのデプロイ環境はNodeなどを用いずにstatic buildしたものをデプロイする構成を取っているため、このActionsの中で静的サイトとして書き出しを行い、サーバへ展開しています。

最終更新