Skip to content

Instantly share code, notes, and snippets.

@a-suenami
Last active June 13, 2022 07:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save a-suenami/fc0750d15b873b1163d2e19179d74885 to your computer and use it in GitHub Desktop.
Save a-suenami/fc0750d15b873b1163d2e19179d74885 to your computer and use it in GitHub Desktop.
Railsでエヴァンスのいう集約の概念を取り入れる方法(案)

経緯

いろいろ雑多に

  • Rails の場合は Repository は冗長かなと思って Query クラスから直接 AR を使っている。
  • 同様の理由で MemberAggregate::Member をアレコレしてから store とかするのだるいので Command クラスにして、そこから直接 AR で副作用起こす感じ。
  • 僕が CQRS 好きだからというのもある。
  • Query クラスに Member とか MemberDetail を注入したい。
  • Query クラスのメソッドは Repository のメソッドに、Command クラスのメソッドはドメインイベント(の前段となるコマンド)に対応するイメージ。
  • 集約をまたぐ場合は app/controllers/concerns とかにアプリケーションサービスを作る。

ディレクトリ構造

app
└── aggregates
    └── member_aggregate
        ├── command.rb
        ├── member.rb
        └── query.rb
module MemberAggregate
class Command
def self.change_address!(address)
@member_detail.address = address
@member_detail.save!
end
def self.another_command(args)
# TODO
end
end
end
module MemberAggregate
class Member
include Command
def initialize(member, member_detail)
@member = member
@member_detail = member_detail
end
def self.from_relation(member_relation)
relation.map do |member|
new(member, member.memeber_detail)
end
end
end
end
module MemberAggregate
class Query
def self.premium_members
# Query の中から AR を直接使うのはアリかな…
rel = Member.where(member_type: :premium)
MemberAggregate.from_relation(rel)
end
def self.another_query
# TODO
end
end
end
class MembersController < ApplicationController
def index_premium
@members = MemberAggregate::Query.premium_members
end
def update_address
@member = MemberAggregate::Query.find_by(id: params[:id])
@member.change_address!
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment