マイペースなRailsおじさん

Ruby、Ruby on Rails、オブジェクト指向設計を主なテーマとして扱います。だんだん大きくなっていくRuby on Rails製プロダクトのメンテナンス性を損なわない方法を考えたり考えなかったりしている人のブログです。

やりすぎないドメイン駆動設計 on Rails

ドメイン駆動設計(DDD)では、Springのようにドメインをほかの関心事(データソース、プレゼンテーション)から分離したフレームワークが用いられることが多です。一方で、Ruby on Railsは、ドメインとデータソース層が密に結合しており、DDDには向いていないと考えられています。

Railsドメイン駆動設計をする大変さ

それでも、アーキテクチャに手を加えることで、Railsでもドメイン駆動設計を行った事例が公開されています。以下の取り組みでは、Railsの提供するActive RecordをDAOとして利用してドメインを分離することで、ドメインを切り出しやすくしています。

ドメイン駆動設計の比類なきパワーでRailsレガシーコードなど大爆殺したるわあああ!!! - Qiita

Railsにおけるドメイン駆動設計の実践 · Linyclar

Railsドメイン駆動設計を行う上での課題は、ドメインオブジェクトがデータソース層(ActiveRecord)から分離できていない点にあります。 ドメインオブジェクトにドメイン以外の知識が入り込むことを防ぐことで、柔軟なモデルを作ることができるとされています。

このため、例に挙げた取り組みでは、ActiveRecordドメインオブジェクト用のクラスを分離する、アプローチをとっています。ドメインオブジェクトを利用するときは、ActiveRecordから得た値を使ってドメインオブジェクト生成します。ドメインオブジェクト生成用の値の詰め替え処理は、自前で書いていくことになるので結構骨の折れる作業になります。

また、Railsの開発効率の核となるActiveRecordのメソッドがコントローラやViewから使えなくなります。これも大きな痛みを伴う点です。

反対に、hanamiSpringでは、クリーンアーキテクチャで示されるような、ドメインモデルをDBやUIから分離する構成を比較的簡単に実現できます。

密結合なアーキテクチャでDDDをやってはダメなのか

ドメイン駆動設計で何を得たいのか、というと、主には下記の二つが挙げられます。

  • 変更容易性
  • ドメインの知識が反映されたソフトウェアのモデル

クリーンアーキテクチャでは、ドメインモデルがほかの要素から分離されたつくりにすることで、上記二つの効果を高めています。

一方で、Railsのような密結合なアーキテクチャで、ドメイン駆動設計をしたとしても、上記の効果は部分的ではありますが得ることができます。

部分的にドメインオブジェクトを使う

Railsアーキテクチャを壊さないままドメインモデルを表現しようとするととどうなるでしょうか?

ActiveRecordと重なるドメインオブジェクト

ActiveRecordは、ドメインオブジェクトとデータソースの両方の性質を併せ持つオブジェクトです。 このため、ActiveRecordはそのままドメインオブジェクトとして扱えます。

ActiveRecordでないドメインオブジェクト

ActiveRecordではないドメインオブジェクトは、下記のようなものが考えられます。

どちらの場合も、必要な時だけドメインオブジェクトにする、というアプローチが取れます。

読み込むときは、下記のようにします。

class Address
  attr_accessor :prefecture, :postal_code, :address_detail

  def domain_logic; end
end

# usersテーブルに、prefecture, postal_codeなどをもつ
class User < ActiveRecord
  def address_as_object
    Address.new(prefecture, postal_code, address_detail)
  end
end

書き込むときはこのようになメソッドを生やせばよいです。もし、この変換処理が複雑で分離する必要があるなら、Repositoryを適宜つくります。

class Client
  def save
    self.to_user.save
  end

  def to_user
    User.find_or_initialize_by(id: id,...)
  end
end

部分的に作ってうれしいのか

みんなアーキテクチャをごっそり変えてDDDしているのに、こんな中途半端な使い方で大丈夫か、と思うかもしれません。 DDDの核となる考えは、ドメインの知識を反映したモデルを作ることで、ドメインに対する洞察と変更容易性を得ることです。

たとえ部分的であっても、ドメインオブジェクトを作って使うことでその効果は得られます。

この方法の利点は、ActiveRecordの世界観をできるだけ壊さずに、ドメインモデルを使うことができる点です。 ドメインモデルをほかの層から分離することによるアーキテクチャの柔軟性は得られません。

既存のRailsアプリケーションで、特に複雑な部分から少しずつドメイン知識をコード化していくときに向いている手法だと考えます。

つらくなったら

つらくなったらアーキテクチャからごっそり変えて、ドメインを分離しましょう。 あるいは、ActiveRecordを普通に使ったほうが嬉しいと感じたら、またどこかのActiveRecordかコントローラにロジックを戻してあげましょう。後戻りはおそらく簡単なはずです。