Closed5

Railsでのモジュール分けをどうするか検討する会

N04hN04h

Railsを使う上で標準のMVCだけではアプリケーションの規模に比例して、
Fat modelになったりやServiceがたくさん作られたり、
それらが密結合となっていることでバグの温床・改修が難しい・影響範囲がわかりにくいという問題がある。

それをRailsが提供しているRails Engine (公式ガイド)などをつかうことで解決したいが、何が良いのかを検討する。

今回管理画面とAPIを提供するRailsアプリケーションを作りたい

N04hN04h

Rails Engine

  • ミニチュア版Railsアプリケーション ... Railsとほぼ同じ機能のもの
  • rails plugin new XXXを実行するとroot直下に作成される
  • Engine, Gem といった形で切り出すことでBundlerがモジュール間の依存関係を検出できる
    • ただし、railsでgemをロードするとどこでも呼び出せてしまうので、自身と依存先だけロードするような仕組みを設ける必要がある
  • routingで/blogは別のサービスとして切り出したいなどの場合に向いている機能?

--full--mountableの違い

https://qiita.com/kidach1/items/565c2c077ae8d15fe3a8#full-engine-と-mountable-engine

ここがわかりやすかったが以下にまとめておく

  • isolate_namespaceがあるかないか
full
module Full
  class Engine < ::Rails::Engine
  end
end
mountable
module Mountable
  class Engine < ::Rails::Engine
    isolate_namespace Mountable
  end
end
  • routes.rbの内容
full
Rails.application.routes.draw do
end
mountable
Mountable::Engine.routes.draw do
end

実際の成果物リポジトリ

https://github.com/n04h/rails-with-engine

今回のケースの場合

API・管理画面とパスは変えてルーティングを別々に管理したいのであれば、
Mountableのほうがよさそう。

N04hN04h

モジュラーモノリス & ドメイン駆動設計 のサンプルリポジトリ
https://github.com/rootstrap/rails-modular-monolith-with-ddd

特徴

  • ActiveSupport::Notificationsをつかっている
    • Kafkaなどを使ったサンプルもある
  • Shopifyが作成したPackwerkをつかってモジュールを分けている

イベント駆動

ActiveSupport::NotificationsはRailsが提供しているPubSubてきなやつ?
以前のプロジェクトでイベント単位でなにかをしたいってときに、
その処理に打ち込んでたのをこれで疎結合にしたい。

https://railsguides.jp/active_support_instrumentation.html

実装例

購読するイベント名は正規表現で判定もできるとのこと

ActiveSupport::Notifications.subscribe("test.event") do |*args|
  event = ActiveSupport::Notifications::Event.new(*args)
  p "Received! (#{event.name}, #{event.payload})"
end

ActiveSupport::Notifications.subscribe(/^test/) do |*args|
  event = ActiveSupport::Notifications::Event.new(*args)
  p "Test all (#{event.name}, #{event.payload})"
end

ActiveSupport::Notifications.instrument("test.event", { foge: 'fuga' })

// 結果
// "Received! (test.event, {:foge=>\"fuga\"})"
// "Test all (test.event, {:foge=>\"fuga\"})"

instrumentのブロック内でエラーが発生したとき

ActiveSupport::Notifications.instrument("test.event") do
  raise "Error"
end

// 結果
// "Received! (test.event, {:exception=>[\"RuntimeError\", \"Error\"], :exception_object=>#<RuntimeError: Error>})"
N04hN04h

今回、そこまでモジュールをGemレベルで分けれるほどの依存関係ではないので、
Packwerkを使ってモジュール分けを行ったりすることにする

このスクラップは2022/12/13にクローズされました