🧭

Handlerrフリーランスの技術スタックと選定理由

2024/06/10に公開

こんにちは!
フリーランスエンジニアが仕事を見つけることができるプラットフォーム Handlerrフリーランス を開発中の北村です。
将来的にはフリーランスエンジニア版の転職サイトを目指しています。
まだまだPMFと呼べる機能には程遠いのですが、主要な技術選定が完了して動作するようになりました。技術選定の際に考えたことやその背景などを共有しようと思います。

技術選定の方針

大前提として「限られたリソースの中でプロダクト開発に集中できる構成にする」という軸で選定しました。

  • 技術的なチャレンジはせず、ある程度枯れた技術を選択する
  • 少人数で開発可能にして、大規模開発を見越したスケーラブルな仕組みは採用しない
    • not マイクロサービス、not モジュラーモノリス
  • 将来的に開発を任せやすいようにフロントとバックエンドで分業がしやすいようにしておく
  • ある程度のインフラコストは許容する
    • 安く動かすために労力を使いすぎない

使用技術とインフラ構成図

主な使用技術は以下です。
詳細は後述していきます。

  • インフラ
    • GCP(Cloud Run、Cloud SQL for PostgreSQL)、Firebase、Vercel
  • バックエンド
    • NestJS(TypeScrpipt)、Prisma
  • フロントエンド
    • Next.js(React.js)
  • その他
    • GitHub Actions、Sentry、Stripe、Open API

フルスタックフレームワークは採用しない

Ruby on Railsのようなフルスタックフレームワークは採用せずに、フロント/バックエンドに分かれて開発できるFWにすることにしました。

  • プロダクトの特性上SEO対策が必要になるので、その際フロントのことだけを考えやすいようにしておきたかった
  • 将来的にはSchema駆動開発による分業をしていきたい
  • フロントとバックエンドをアーキテクチャレベルで分離することでテスタブルになる(主にバックエンド)

という理由です。

言語選定

ブラウザからアクセスするWebサービスを作ろうとしていたので、フロントエンドはTypeScriptで書くことにしました。
バックエンドの言語については、使用する言語を統一することで認知負荷を軽減し、生産性を高めたかったので、TypeScriptを第一選択肢にしました。調査の結果NestJSがやりたいことにマッチしていたので、そのままTypeScriptで行くことができました。

Handlerrフリーランスの開発前に私が一番書き慣れている言語はGoだったので、バックエンドをGoで書く選択肢もありました。短期的な生産性は間違いなくGoの方が高かったのですが、中長期で考えたときに言語統一した方が良いだろうという判断をしました。

マルチレポ

モノレポにはしませんでした。
モノレポのキャッチアップや環境整備に時間を使うよりもプロダクト開発に時間を使いたかったためです。

レポジトリは現在2つ(フロント、バックエンドでそれぞれ1つ。IaCはまだやっていないのでインフラのレポジトリはない。)なのですが、マルチレポなのでレポジトリの直下にコードを配置できています。それにより、CI/CD構築が公式ドキュメントの手順をそのままなぞるだけでよくて簡単に設定できました。
(モノレポだと階層が異なるので多少調整が必要になるはず。)

この記事を書くにあたってモノレポのメリットを調べたのですが、プロダクトコードを1箇所で集中管理することで、全体像が把握しやすくなる(主に組織のコミュニケーションという観点で)、という感じだったので、マルチレポにした分はしっかりコミュニケーションを取りながら開発していく体制を作っていけたらよいなと考えています。

インフラ

アーキテクチャが複雑になるほど生産性が低下して障害発生率が高まるので可能な限りシンプルな構成にしました。

  • Azureは全く使ったことがなかった
  • GCPのプロジェクトという概念が好きだった
    • AWSはプロジェクトという概念がないので、それに該当する設定を自分でする必要がある。
    • 「セキュリティグループの設定がしたいのではなくて、適切なアクセス制御がされた場所でクラウドリソースを活用したい」という思いがあるので、やる必要のないタスクはやりたくなかった
  • Firebaseを使いたかった

という理由でメインのクラウドサービスはGCPにしました。

フロントエンドはNext.jsを採用するならVercelにしようかなーと思っていて、Next.jsを採用したので流れでVercelになりました。

参考
https://zenn.dev/team_zenn/articles/5e9547a5c207e3#なぜ移行したのか

データベース

検索機能を充実させたかったので、NoSQLではなくて、RDBをメインのDBにしました。
Cloud SQLはMySQLとSQL Serverも使えますが、PostgreSQLが慣れていたのでPostgreSQLにしました。

チャット機能など、レコード数が増えそうなものはFirestoreに保存するようにしています。

バックエンド

フレームワーク

TypeScriptでAPI Serverを実装するとして以下を検討し、NestJSを採用しました。

  • Express
    • API ServerをTypeScriptで開発するならこれが王道っぽい雰囲気
    • 不足しているライブラリは自身でライブラリ選定をして追加していく必要がある
      • デファクトがあるならそれに従いたいが調査に時間をかけたくない
  • Fastify
    • ベンチマークのスコアが良かったのは魅力的
    • 開発でハマったときに失う時間を踏まえるとExpressの方が良さそう
      • 同じような理由でyarnやpnpmも使っていません。
  • tRPC
    • さすがにまだ枯れてなさそう
    • デプロイどうしようか悩んだがあまりドキュメントが出てこなかった
  • NestJS
    • Express、FastifyのラッパーFW
    • API Serverを実装するために必要な機能が一通り揃っていて、ドキュメントが充実している
    • Rails wayならぬNestJS wayがある(ように感じた)
    • Swaggerの生成が可能

ORM

NestJSとしてはTypeORMを推してそうですが、Xなどでよく目にするのはPrismaだったのでPrismaにしました。
ちなみにPrismaのマイグレーションにはprisma db pushというSchema dump方式のマイグレーションも可能で、こちらがおすすめです。(RubyのRidgepoleみたいな方式)
https://www.prisma.io/docs/orm/prisma-migrate/workflows/prototyping-your-schema#choosing-db-push-or-prisma-migrate

GraphQLは使わない

使ったことがなかったので使わないことにしました。
GraphQLでできることの大半はREST APIでもできるはず。

フロントエンド

NuxtはVer2→3への移行が数年かかっていて今後のアップデートが不安だった。
Remixは採用するにはまだ早そうに感じた。
という理由でNext.jsを採用しました。
Next.jsは多機能ですが、その分公式ドキュメントを読めばやりたいことができるところが気に入っています。

Next.jsの個別技術に関しては以下です。

  • CSRとSSRは使う。SSG、ISRは使わない
    • 認証が必要な画面はCSR、認証が不要な画面はSSRにしています。
  • Server Actionは使わない
  • App Routerは使う
    • App Routerだと実装が難しいUIがあるらしいですが、その場合はUIを妥協すればよいと割り切っています。
  • UIはTailwind CSS × shadcn/ui
  • 認証はFirebase Authをそのまま利用する
    • Next.jsの認証
      • 認証はDBまで密結合な場合を想定してそうだったので参考にしづらかった
    • NextAuth.js
      • 認証がサーバー側で行われているので、認証が必要な画面はCSRにするという方針に適していなかった
  • データ取得はSWR
    • データ取得できたとき、ローディング中、エラー時、とUIを宣言的に書きやすくてよいです。
  • API ClientはOpenAPI Generatorのtypescript-fetch

CI/CD

  • lint/test → GitHub Actions
  • デプロイ→Cloud Build(Cloud Run、DB migration、 Firebaseのセキュリティルール)

という棲み分けです。
GitHub Actionsからデプロイすることも可能ですが、それをやろうとするとGCPの内部の情報をGitHub Actionsが知りすぎることになり、インターフェース的に違和感があったためです。
GCPはソースコードを引数に(インプット情報として)環境を作る、ソースコード自体の検証はGitHub、という整理です。

監視

  • エラー監視: Sentry
    • BugSnagもありますが、Sentryの方が直近使うことが多かったのでSentryにしました。
    • エラー監視に限ればできることはだいたい同じなので、慣れている方を使えばよいと思います。
    • お金に余裕があるならDatadogを検討してもよいかもしれません
  • インフラ監視: Cloud Monitoring

OpenTelemetryも導入したかったのですが、PrismaのOpenTelemetryがPreview版だったのでまだ導入していません。

決済

  • Stripe
    • 比較検討はしていません。評判がよかった&使ってみたかったので使いました。

コスト

  • GCP: 1環境あたり約11000円(/月)
    • produtionとstagingの2つ環境を用意しているので合計で約22000円(/月)です
    • 金額の内訳ですが、99%がCloud SQLです
  • Vercel Pro Plan: 約3000円
    • $20(/月)
    • 1ドル150円で計算
  • Sentry: 約3900円
    • $26(/月)
    • 1ドル150円で計算

合計すると30000円くらいです。
正社員だと高く感じますが、時間単価5000〜のフリーランスなら、1日働いたら十分賄える金額です。(経費にもできる)
仕事に繋がったり、得た知見をクライアントに還元したりできるので、自分のポートフォリオの1つとしては悪くないコストかなと思います。

宣伝

エンジニアの方へ

Handlerrフリーランスに登録していただけると嬉しいです!
まだ機能は少ないですが、今後増やしていくので半年、1年後など時間を経てどのくらい進化したのか見て欲しいです。

採用担当の方へ

フリーランスエンジニア版の転職サイトを目指しているので将来的には求人掲載ができるようになります。
掲載可能になったらご案内するので興味がある方はお問い合わせよりご連絡ください。

最後に

最後まで読んでいただきありがとうございました!

いいね、コメントなどもらえると励みになるのでお願いいたします!

Discussion