LoginSignup
9
0

More than 1 year has passed since last update.

この記事は何

DDDを学んだ事がある方は「バリューオブジェクト」という名前を一度は聞いた事があるのではないでしょうか・
この記事ではRailsでバリューオブジェクトを実装するならどのような実装にすると良さそうかを紹介します。
なお、バリューオブジェクトの具体的な実装方針は千差万別だと思うので、この記事で紹介する実装はあくまで一例だと思っていただけるとありがたいです。

具体的なバリューオブジェクトの解説は↓の記事などが参考になると思います。

バリューオブジェクトの条件

バリューオブジェクトは以下の条件を満たしている必要があります。
条件は以下の通りです。

  • オブジェクトはイミュータブルである
    • 値の変化はインスタンスの再生成で実現する
  • オブジェクトの同一生はバリューオブジェクトが持つattributeの一致で定義される

Railsで実装する際も、これらの条件を満たす必要があります。
そしてバリューオブジェクトとして実装する以上、バリデーションも行えるようにしたいです。
これらの要件を満たすインターフェースを実装してみます。

実装物

実装物はズバリ↓のようなクラスです。

class Value::Base
  include ActiveModel::Model

  def initialize
    fail ActiveRecord::RecordInvalid unless valid?
  end

  # @param other [::Value::Base]
  # @return [true, false]
  def ==(other)
    evaluate_attributes.all? { |variable| try(variable) == other.try(variable) }
  end

  private

  # @return [Array<String>] attributes used to equal evaluation
  def evaluate_attributes
    fail NotImplementedError
  end
end

このクラスは以下のように継承し、利用します。

class Value::Name < Value::Base
  attr_reader :first_name, :last_name

  validates :first_name, presence: true
  validates :last_name, presence: true

  def initialize(first_name:, last_name:)
    @first_name = first_name
    @last_name = last_name
    super()
  end

  def full_name
    "#{first_name} #{last_name}"
  end

  private

  # @note Override {::Value::Base#evaluate_attributes}
  def evaluate_attributes
    %i(first_name last_name)
  end
end

このクラスの機能について説明します。

オブジェクトの不変性

このクラスでは、基本的にsetterを提供せずに、getterのみを公開します。
こうすることで、外部からインスタンスの変更を行うことはできません。

オブジェクトの同一性

このクラスにはevaluate_attributesというメソッドを実装しています。
また、==メソッドをオーバーライドし、各attributesの値の同一性を検証することでオブジェクト自体の同一性を判定するようにしています。
このようにすることで、==メソッドを用いた同一性検証はバリューオブジェクトが満たすべき条件と同じになります。

値のバリデーション

このクラスではActiveModel::Modelモジュールをincludeしています。
また、initializeのタイミングでvalid?falseを返すときにエラーを返すようになっています。
このように実装をし、子クラスで最後にsuper()を実行することで、インスタンス生成時に検証まで行えるようになっています。

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0