iT邦幫忙

2021 iThome 鐵人賽

DAY 12
0

建議搭配之前的 sequence diagram 一起服用!

Dry Initializer

在進到 Record Mapper 和 Factory 之前,先來介紹一下 dry-initializer,他替我們省略掉 constructor (在 ruby 中是 initialize)中賦值給實例變數(instance variable)的工作,在 spec 中這樣的特性讓我們可以簡單的替換掉跟外部依賴的 class。

require 'dry/initializer'

class User
	extend Dry::Initializer

	param  :name,  default: proc { nil }
	option :age, default: proc { nil }
end

user = User.new 'Tom', age: 23
user.name # => 'Tom'
user.age # => 23

Q: 放在 initialize 的參數和放在 method 的參數有甚麼不同?

我們放在 initialize 的參數都是屬於工具或模組化的 class,通常在 spec 中都會把這些 class 抽換掉,而 method 傳入的才是那個 method 有用到的參數。

Record Mapper

Record Mapper的職責是把 aggregatehash 轉成對應 db schema key 的 hash,選擇轉成 hash 的原因是 hash 是 ruby 的 base type,如此一來便可以讓 mapper 通用化到不同的外部資源(ORM、Database、其他 gem 等等)

class Order < Boxenn::Repositories::Mapper
  def build(hash)
    # key 是對應到 DB schema columns name,而 value 則是 entity 的 attibutes name
    {
      custom_serial_number: hash[:serial_number],
      status: hash[:status],
      created_at: hash[:puchased_at],
      comment: hash[:comment],
    }
  end
end

Factory

Factory 的職責跟 Record Mapper 剛好恰恰相反,是把 source 轉成 aggregate ,而在Boxenn::Repositories::Factory 中定義的介面也是 build,除此之外還需要使用 dry-initializer 指定 entity。

這邊的 Factory 不是在 DDD 或 clean architecture 中的 factory,單純只是一個轉換器,名字是為了與 record mapper 作區別。

class Order < Boxenn::Repositories::Factory
	# 指定 entity 讓 factory 和 repository 使用
  param :entity, default: -> { Entities::Order }

  def build(source_object)
    entity.new(
      serial_number: source_object.custom_serial_number,
      status: source_object.status,
      puchased_at: source_object.created_at,
      comment: source_object.comment,
    )
  end
end

下一篇會進一步擴充 record mapper,使每次在建立基礎設施時更加快速。


上一篇
[DAY11] Data Access Layer 設計概念
下一篇
[Day13] 擴充 Boxenn 的 Record Mapper
系列文
在 Ruby on Rails 中導入 Domain-Driven Design 是不是搞錯了什麼?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言