If you have complicated calculations, let's create a CalcRule class

If there is a calculation process, you will create a method in the related ActiveRecord model [^ 1].

product.rb


class Product < ApplicationRecord
  def billing_price
    price * consumption_tax_rate
  end

  def consumption_tax_rate
    #Return sales tax rate
  end
end

[^ 1]: I'm omitting it because it's not the main subject, but let's calculate floating point numbers properly! => [Ruby] Use Big Decimal for sales tax calculation

I don't feel that much of a problem when calculating the tax-included amount. However, when discounts are started in campaigns or the discount conditions become complicated, the product class seems to be a monetary calculation class.

product.rb


class Product < ApplicationRecord
  def billing_price
    campaign_discounted_price * consumption_tax_rate
  end

  def consumption_tax_rate
    #Return sales tax rate
  end

  def campaign_discounted_price
    #Return the price to which the campaign was applied
    #If the campaign becomes complicated, this method becomes complicated,
    #The number of methods that can only be called from this method will increase!
  end
end

If nothing is done, it will become a Fat Model.

Let's create a class for calculations

Create a class to calculate the consumption tax and a class to calculate the campaign discount.

consumption_tax_calc_rule.rb


class ConsumptionTaxCalcRule
  attr_accessor :price, :product_type
  # product_type is the information to decide whether to apply the reduced tax rate (please keep in mind)
  def initialize(price, product_type)
    self.price = price
    self.product_type = product_type
  end

  def tax_included_price
    price * tax_rate
  end

  def tax_rate
    #Return sales tax rate
  end
end

campaign_discount_calc_rule.rb


class CampaignDiscountCalcRule
  attr_accessor :price, :campaigns
  #campaigns is information for campaign discounts (please keep in mind)
  def initialize(price, campaigns)
    self.price = price
    self.campaigns = campaigns
  end

  def discounted_price
    #Even if more methods are called only from this method
    #In the campaign discount calculation class
    #Nothing is wrong because there are only more methods for campaign discounts!
  end
end

For the product class, just use the CalcRule class.

product.rb


class Product < ApplicationRecord
  def billing_price
    discount_rule = CampaignDiscountCalcRule.new(price, campaigns)
    tax_rule = ConsumptionTaxCalcRule.new(discount_rule.discounted_price, product_type)
    tax_rule.tax_included_price
  end
end

Now, even if the sales tax changes or the campaign changes, you only have to check the CalcRule class!


This entry is for Business Rules (https://linyclar.github.io/software_development/requirements_analysis_driven_desgin/#%E3%83) in Requirements Analysis Driven Design (https://linyclar.github.io/software_development/requirements_analysis_driven_desgin/) % 93% E3% 82% B8% E3% 83% 8D% E3% 82% B9% E3% 83% AB% E3% 83% BC% E3% 83% AB) Shows a concrete example and general-purpose content It was rewritten to. It's a long story, but it's a story about DDD-like things in Rails.

Recommended Posts

If you have complicated calculations, let's create a CalcRule class
If there is a state transition, let's create a State class
What to do if you accidentally create a model
If you issue a freeeAPI invoice, let's do + update automatically! !!
We have released a service that allows you to easily create chats!
Create a temporary class with new Object () {}
[Rails] Let's create a super simple Rails API