隆重介紹 Boxenn!
它是我們專門用來在 legacy code 中導入 DDD 的套件,之後會花不小的篇幅來介紹每個 class 的設計理念對應到 DDD 中的哪個部分,以及怎麼實際地在專案中使用。
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
更多關於 dry-types 和 dry-struct 的用法可以參考他們的官方文件
至於 value object 可以只繼承 Dry::Struct
或是使用 OpenStruct
本身提供的基礎型別。
ID 有兩個缺點:
而透過制定 entity 的 primary key 可以對領域物件有更多的認識,但這種方式也有其缺點:
因此選擇要用哪種方式還是要依專案而定。
下一篇會繼續來講如何使用 Boxenn::Entity
來製造一個比較複雜的 aggregate。