iT邦幫忙

2021 iThome 鐵人賽

DAY 15
0
Software Development

在 Ruby on Rails 中導入 Domain-Driven Design 是不是搞錯了什麼?系列 第 15

[Day15] Boxenn 實作 Repository & Query

Repository

source wrapperrecord mapperfactory 都完成後,在 repository 裡只需要簡單的歸位初始化後就完成了。

# 這邊以 order 做舉例,primary key 為 serial_number

class Order < Boxenn::Entity
  def self.primary_keys
    [:serial_number]
  end

  # 訂單編號 (identity)
  attribute :serial_number,         Types::Coercible::String
  # 狀態
  attribute :status,                Types::Symbol.enum(successed: 0, refunded: 1, canceled: 2)
  # 購買日期
  attribute :puchased_at,           Types::DateTime
  # 備註
  attribute :comment,               Types::Coercible::String.optional.default(nil)
end
module Repositories
  class Order < Boxenn::Repository
    option :source_wrapper, default: -> { Wrapper::Order.new }

    option :record_mapper, default: -> { Mapper::Order.new }

    option :factory, default: -> { Factory::Order.new }
  end
end
# 實際運用

repo = Repositories::Order.new
repo.find_by_identity(serial_number: 'ABC-1234567890') # 會回傳 order 的 entity
repo.destroy(serial_number: 'ABC-1234567890') # 會刪除資料庫中的 record
order = Order.new(
	serial_number: 'ABC-9876543210',
	status: :successed,
	puchased_at: Time.zone.now,
	comment: '測試',
)
repo.save(order) # 會根據 serial_number = 'ABC-9876543210' 來找 record,如果有找到即更新資料,沒有找到則建立一筆新 record

save 可以傳入多個 entity 的 array ,一次更新或新增多筆資料

Query

負責歸納複雜讀取邏輯。
唯一定義的介面是 collect ,回傳的資料會傳入 factory 轉成 entity,以ActiveRecord 為例回傳為 array of models 即可。

下面範例一樣以 ActiveRecord 為外部資源做示範:

require 'dry/initializer'

module Boxenn
  module Repositories
    class Query

      extend Dry::Initializer

      param :relation, default: proc { nil }

      def collect
        raise NotImplementedError
      end
    end
  end
end
# Order Query
class OrderQuery < Boxenn::Repositories::Query
	param :relation, default: -> { ::Models::Order }

  def comment_include(key_word)
    @relation = relation.where('comment LIKE ?', "%#{key_word}%")
    self
  end

  def by_status(status)
    @relation = relation.where(status: status)
    self
  end

  def collect
    relation
  end
end
# 實際運用

repo = Repositories::Order.new
query = OrderQuery.new
query.comment_include('測試')
repo.find_by_query(query)

需要注意這邊 query 有儲存 Active::Record::Relation, 因此不同的 method 之間可以 chain ,但如果想要新的搜尋結果就需要重新 new 一個 query

repo = Repositories::Order.new
query = OrderQuery.new
query.comment_include('測試').by_status(:refunded)
repo.find_by_query(query)

下篇會說明如何對整個 DAL 做測試。


上一篇
[Day14] Boxenn 實作 Source Wrapper
下一篇
[DAY16] Data Access Layer 測試
系列文
在 Ruby on Rails 中導入 Domain-Driven Design 是不是搞錯了什麼?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言