Rails has a policy of "Skinny Controller, Fat Model".
Put the main logic of the application in the model The policy is to limit the role of the controller (or view) to something that can only be done at that layer.
By following this policy, the dependencies between each layer can be easily understood. This can be done by clarifying the scope of influence.
But to focus the business logic on the model If you don't create the model properly, the model will grow too large.
One of the countermeasures against the bloat of the model is the introduction of `` value object''.
Value objects come from Domain Driven Design
This is one of the patterns for expressing the domain model
in code.
The domain
is the problem domain targeted by the application.
Analyzing the domain and extracting the construct is called modeling
.
And the concept obtained as a result of modeling is called domain model
.
The domain model has attributes and behaviors related to that concept.
As a representation of this domain model as an object
There is a "entity
" and the "value object
" mentioned earlier.
Each entity and value object has the following characteristics.
entity | Value object | |
---|---|---|
identity | If the identifiers are the same, they are considered to be the same | If all the attribute values are the same, they are considered to be the same. |
Variability | Attributes can be changed after generation | Attributes do not change after generation |
** Entity example **
--Employee ――Even if there are two employees with the same name, they are different people ――Even if the attribute of age changes on your birthday, you will not be another person. --If the employee ID is the same, it can be judged as the same employee. --Employee ID is an identifier --Since it is judged by the identifier whether they are the same, it can be considered as an entity.
** Example of value object **
--Currency ――When comparing currencies only by monetary value, two 1000-yen bills are considered to be the same even if they are manufactured in different years. --If the attributes match, it is judged to be the same, so it can be considered as a value object.
By the way, the instance of the model using ActiveRecord is ** "id" is used as an identifier ** and is used to implement the entity.
There is a User model with an attribute called "email address" Suppose you want to add logic that returns only the domain name of the email address that the User has.
** Implement logic as an instance method of User model **
python
class User < ApplicationRecord
validates :email, format: { with: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/ }
def email_domain
email.split("@").last
end
end
** Problems with this implementation ** --In the future, if the logic related to email addresses increases, the User model will become bloated. --When a model with an email address other than User appears, it is necessary to implement the same method in that model as well.
Next, to solve these problems, let's implement the logic using value objects.
** Separate from User model with email address as value object **
Introduced a value object called Email Implement email address attributes and email address logic in that object.
python
class Email
attr_reader :value
delegate :hash, to: :value
def initialize(value)
raise "Email is invalid" unless value.match?(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/) # (*1)
@value = value.frozen? ? value : value.dup.freeze # (*2)
end
def ==(other)
self.class == other.class && value == other.value # (*3)
end
def domain
email.split("@").last
end
end
# (*1)
#The validation process could also be separated from the User model.
# (*2)
#One of the characteristics that value objects must satisfy is invariance.
#Therefore, the attributes do not change after the object is created.
# (*3)
#Equivalence is a feature that value objects must satisfy.
#Therefore, it is possible to compare with other Email objects.
python
class User < ApplicationRecord
composed_of :email, mapping: %w[email value] # (*4)
end
# (*4)
# composed_of is a Rails model, a method to make value objects easier to work with.
#I won't explain it in detail here, so please check it out.
** Benefits of separating email addresses as value objects **
--The User model does not grow even if the logic of the email address increases --Clarified where to implement logic for email addresses --Email address logic can be reused in models other than User
When modeling with Rails, it tends to be forcibly implemented as an entity because ActiveRecord is used. If you try to express the domain model entirely with entities, there is a high possibility that it will become a FatModel, so it is important to properly examine whether it should be implemented as an entity or a value object !!
Perfect Ruby on Rails The idea of Value Object Value object in 3 minutes DDD basic explanation: What are Entity and Value Object Handle value objects in Rails with composite_of Realistic regular expression for email address
Ateam Hikkoshi samurai Inc. × Ateam Connect Inc. Advent Calendar 2020 How was the article on the 19th day? Tomorrow is @ cheez921's article !! Please read it !!
Recommended Posts