iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0

隆重介紹 Boxenn!
它是我們專門用來在 legacy code 中導入 DDD 的套件,之後會花不小的篇幅來介紹每個 class 的設計理念對應到 DDD 中的哪個部分,以及怎麼實際地在專案中使用。

Boxenn::Entity

module Boxenn
  class Entity < Dry::Struct
    alias assign_attributes new

		# entity 識別屬性的 key 值
    def self.primary_keys
      raise UndefinePrimaryKeys.new(class_name: self.class.name)
    end

    # entity 識別屬性的 hash
    def primary_keys_hash
      if !self.class.primary_keys.all? { |s| attributes.key? s}
        raise UnassignPrimaryKeys.new(class_name: self.class.name)
      else
        attributes.slice(*self.class.primary_keys)
      end
    end
  end
end

Boxenn 的 Entity 是以 Dry-Struct 為基礎,他提供了許多好用的基礎型別來制定物件,也有方法可以對物件做限制。

primary_keys_hash 這個 method 是用來讓 repository 正確的轉換成 source 物件,repository 的詳細設計概念會在未來的篇章中介紹。

source 指的是在 boxenn 對資料庫進行操作時的物件,本系列文章 source 是代表 model。

實例

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
  • Entity 唯一的要求是需要提供 primary keys,呼應 entity 一定會擁有可以識別的屬性
  • Coercible 代表會嘗試自動轉型
  • optional 代表 value 可以為 nil
  • default 代表如果建立新物件時沒有賦值,預設帶 nil

更多關於 dry-typesdry-struct 的用法可以參考他們的官方文件
至於 value object 可以只繼承 Dry::Struct 或是使用 OpenStruct 本身提供的基礎型別。

Q: 為甚麼 entity 的 primary key 不用 DB 常見的 auto increament ID 就好?

ID 有兩個缺點:

  1. 在真實世界(商業邏輯)通常不具有意義,無法呈現特定物件帶有的資訊
  2. 如果資料需要在不同的資料庫或儲存空間共用的話,ID 沒有辦法當作 primary key

而透過制定 entity 的 primary key 可以對領域物件有更多的認識,但這種方式也有其缺點:

  1. 需要額外去維護唯一性,如果未來因為業務需求改動導致原訂的 primary key 失效,其成本會很高
  2. 需要的空間較大

因此選擇要用哪種方式還是要依專案而定。

下一篇會繼續來講如何使用 Boxenn::Entity 來製造一個比較複雜的 aggregate。


上一篇
[DAY8] 與 ActiveRecord 分手
下一篇
[Day10] Boxenn 實作 Aggregate 和 Aggregate Root
系列文
在 Ruby on Rails 中導入 Domain-Driven Design 是不是搞錯了什麼?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言