[DAY9] Boxenn 实作 Entity 与 Value Object

隆重介绍 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。


<<:  成为工具人应有的工具包-09 IECookiesView 01

>>:  [D09] OpenCV 应用范围

从零开始的8-bit迷宫探险【Level 29】让你的 App 与众不同!设计 Icon 及 LaunchScreen

回到村子後,山姆变成了斜杠青年,他将探险的故事写成了一本书。 书的封面印着山姆的肖像。 而书名就叫...

那些被忽略但很好用的 Web API / History

历史是现在与过去之间永无休止的对话。 我们都知道浏览器提供了上一页、下一页,甚至可以让你回到前两页...

工欲善其事,必先利其器

开始IaC之前,必先做好前置作业 昨天介绍了一些AWS建置环境的方法,今天会先做CloudForma...

Day11 Platform Channel - EventChannel

EventChannel EventChannel:用於接收一系列讯息,这些讯息被包装到 Strea...

Day-13 ConstraintLayout

ConstraintLayout(约束布局) ConstraintLayout为Android St...