個人開発のWebサービスをRuby on RailsからGo言語へ移行している | shimbaco

  • ページ公開日: 2025年11月29日 (土)
  • 書いた人: shimbaco

Annict (アニクト) という見たアニメが記録できるWebサービスを個人開発しています。
このサービスはRailsで開発されており、ソースコードを公開しています。

https://github.com/annict/annict

最近このRailsアプリをGo言語で書き直し始めています。
このページではその背景やどのようにやっているかなどについてご紹介します。

  • なぜRailsからGoに移行することにしたのか
  • どのように移行を進めているか
  • インフラ構成とプロジェクト構成
  • 使用しているライブラリ
  • 移行して良かったところ・不安なところ

関西Ruby会議08に参加して発表しました

Rails版ではActiveRecordなクラス群であるRecordが至るところから参照されるのを許していました。
RailsはActiveRecordをつかってなんぼだと思うのでこうしていました。

しかしGo版ではデータベースとやり取りするQueryはRepositoryからのみ参照できるようにしています。
また、ViewModelを定義するようにしています。
これによってGo版はQuery (Record) とRepositoryの責務がより明確になったかなと思います。

https://go-chi.io/

net/http互換でシンプルそうだったので選びました。

https://sqlc.dev/

SQLを自分で書くのは大変だと思っていましたが、最近はAIが書いてくれるようになりました。そのためORMを使わずSQLを直書きで良いのでは?と思い始めていました。
ORMを使っても結局どんなSQLが実行されているかは確認する必要があるので、それなら最初からSQLが直書きされているほうが直感的で読みやすいだろうという考えです。

sqlcはSQLからコードを生成してくれるので、型安全かつどんなSQLが実行されるのか意識しやすくて良いです。

https://templ.guide/

Railsでビューを実装するときはView Componentを使うのが好きでした。
普通のRubyのコードのようにメソッドにデータを渡せるところと、ERBでHTMLが書けるところが好きでした。

JavaScript界隈で使われているJSXも好きです。
最終的な出力結果となるHTMLに限りなく近いフォーマットでテンプレートを書きたいという気持ちがあるんだと思います。

templからはGo + JSXみたいな印象を受けました。
データはGoの関数の引数として型付きで渡すことができ、テンプレート部分はJSXみたいなフォーマットで書くことができます。

型を意識してHTMLに近い形でテンプレートが書けるのが好みです。

https://riverqueue.com/

Rails版ではDelayedJobを使っています。
RiverもDelayedJobと同じくPostgreSQLで動くジョブサーバーになっています。
サーバーの構成をシンプルにするために、PostgreSQLでできることはPostgreSQLでやると良いかなと思うので、Riverの存在はありがたいです。

https://golangci-lint.run/

いくつか設定していますが、中でも一番新鮮・導入できて嬉しかったのがDepguardというリンターです。

https://github.com/OpenPeeDeeP/depguard

これは各パッケージでどんなパッケージのインポートを許可・拒否するかといった設定ができるリンターです。

Go版は上に書いたように簡易的なレイヤードアーキテクチャーをもとにパッケージを構成しているため、例えば「TemplateはRepositoryに依存しないこと」といったルールが存在します。
このルールはドキュメントに書いてあるだけだったのでスルーすることもできる状態だったのですが、Depguardを導入して以下のようなルールを追加することで、機械的に依存関係がチェックできるようになりました。

.golangci.yml の設定例:

depguard:
  rules:
    # Templates層のルール: ViewModelを通じてデータを表示
    # 直接のデータアクセスとビジネスロジックに依存しない
    templates-layer:
      files:
        - "**/internal/templates/**/*.go"
      deny:
        - pkg: github.com/annict/annict/internal/query
          desc: "TemplatesはQueryに依存できません。"
        - pkg: github.com/annict/annict/internal/repository
          desc: "TemplatesはRepositoryに依存できません。"
        - pkg: github.com/annict/annict/internal/model
          desc: "TemplatesはModelに直接依存できません。ViewModelを経由してください。"
        - pkg: github.com/annict/annict/internal/usecase
          desc: "TemplatesはUseCaseに依存できません。"
        - pkg: github.com/annict/annict/internal/handler
          desc: "TemplatesはHandlerに依存できません。"
        - pkg: github.com/annict/annict/internal/middleware
          desc: "TemplatesはMiddlewareに依存できません。"

最低限の依存関係の強制ができれば良いので、ブラックリスト方式にして必ず守りたいことだけを記述することにしています。

MewstWikino といったAnnict以外のプロジェクトもGoに移行していこうと思っています。
CLAUDE.md を別リポジトリにコピペして、Annictと並行して作業していこうかなと思います。
また、最初からGoを使う新規サービスの開発も始めています。

新しい技術スタックで開発していくのが楽しみです。


元の記事を確認する

関連記事