代码之家  ›  专栏  ›  技术社区  ›  PatrickSJ

DDD,状态对象/值对象

  •  0
  • PatrickSJ  · 技术社区  · 7 年前

    我有一个类,Proposal,它的$status类型为ProposalStatus。现在,在大多数情况下,ProposalStatus的身份没有改变。。。但它确实(有点)。它的$id是固定的,但随着主数据的更新,$display\u名称和$definition(仅字符串)可能会在很长一段时间内发生变化, 但它在HTTP请求响应的生存期内不会更改 .

    问题#1- 实体还是价值对象?

    是一个价值对象 从不 在应用程序的特定执行的生命周期内应该更改还是从不应该更改?如果显示名称或定义发生更改,那么我真的希望/希望每个人都能更改它。然而,由于他们 可以在提案之外定义 我认为直接向上使它们成为实体而不是价值对象。

    提案在任何时候都不会更改ProposalStatus的属性,它只会更改ProposalStatus的属性。

    问题#2- 如何正确设置域驱动设计的状态?

    我的proposal对象能够管理其状态,但为了做到这一点,我需要一个特定的ProposalStatus对象。只有 允许它返回预期正确的状态列表在哪里?

    • 我可以从ProposalRepository中获取它。。。但是,所有内容都是通过聚合根访问的,因此这没有意义。
    • 我可以有与ProposalStatus的$id匹配的常数,但这似乎是错误的。
    • 我可以提出一个提案StatusRepository。。。但我是否应该从提案中访问另一个存储库?
    • 我可以创建一个以$id为键的所有可能状态的数组,并将其添加到提案中,但这与存储库没有太大区别。。。

    例子:

    class ProposalStatus {
        protected $id; // E.g., pending_customer_approval
        protected $display_name; // E.g., Pending Customer Approval
        protected $definition; // E.g., The proposal needs to be approved by the customer
    }
    
    class Proposal {
        /**
         * The current status of the proposal
         * @var ProposalStatus
         */
        protected $proposal_status;
    
        public function withdraw() {
            // verify status is not closed or canceled
            // change status to draft
        }
    
        public function submit() {
            // verify status is draft
            // change status to pending customer approval
        }
    
        public function approve() {
            // verify status is pending customer approval
            // change status to approved
        }
    
        public function reject() {
            // verify status is pending customer approval
            // change status to rejected
        }
    
        public function close() {
            // verify status is not canceled
            // change status to closed
        }
    
        public function cancel() {
            // verify status is not closed
            // change status to canceled
        }
    }
    
    3 回复  |  直到 7 年前
        1
  •  2
  •   Constantin Galbenu    7 年前

    据我从你的领域了解, ProposalStatus 应该是 Value object . 所以,它应该是不变的,并且包含特定的行为。在您的案例中,行为是测试特定值并仅初始化到允许的值范围。您可以使用PHP类,以及私有构造函数和静态工厂方法。

    /**
     * ProposalStatus is a Value Object
     */
    class ProposalStatus
    {
        private const DRAFT                     = 1;
        private const PENDING_CUSTOMER_APPROVAL = 2;
        private const CANCELLED                 = 3;
        private const CLOSED                    = 4;
    
        /** @var int */
        private $primitiveStatus;
    
        private function __construct(int $primitiveStatus)
        {
            $this->primitiveStatus = $primitiveStatus;
        }
    
        private function equals(self $another): bool
        {
            return $this->primitiveStatus === $another->primitiveStatus;
        }
    
        public static function draft(): self
        {
            return new static(self::DRAFT);
        }
    
        public function isDraft(): bool
        {
            return $this->equals(static::draft());
        }
    
        public static function pendingCustomerApproval(): self
        {
            return new static(self::PENDING_CUSTOMER_APPROVAL);
        }
    
        public function isPendingCustomerApproval(): bool
        {
            return $this->equals(static::pendingCustomerApproval());
        }
    
        public static function cancelled(): self
        {
            return new static(static::CANCELLED);
        }
    
        public function isCancelled(): bool
        {
            return $this->equals(static::cancelled());
        }
    
        public static function closed(): self
        {
            return new static(static::CLOSED);
        }
    
        public function isClosed(): bool
        {
            return $this->equals(static::closed());
        }
    }
    
    class Proposal
    {
        /** @var ProposalStatus */
        private $status;
    
        public function __construct()
        {
            $this->status = ProposalStatus::draft();
        }
    
        public function withdraw()
        {
            if (!$this->status->isClosed() && !$this->status->isCancelled()) {
                $this->status = ProposalStatus::draft();
            }
        }
    
        // and so on...
    }
    

    注意,不变性是值对象的一个重要特征。

        2
  •  1
  •   mgonzalezbaile    7 年前

    如果你的 ProposalStatus 是一个固定的值列表,只适用于枚举方法。

    否则你需要治疗 提议状态 作为一个 AggregateRoot 用户可以创建、更新和删除(我猜)。当分配 提议状态 到a Proposal 你只需要ID。如果你想检查给定的ID是否存在,你只需要用一个专门的查询来满足不变量。规格模式很适合这里。

    class ProposalStatusExistsSpecification
    {
        public function isSatisfiedBy(string $proposalSatusId): bool
        {
            //database query to see if the given ID exists
        }
    }
    

    你可以找到 here 这个 Interfaces 以实现您的规范。

        3
  •  0
  •   poul_ko    7 年前

    所有可能提案状态的列表是否静态?我想是的。所以ProposalStatus看起来像一个简单的枚举。DisplayName和Definition等属性与业务代码无关。

    只需将ProposalStatus定义为枚举(具有只读字段或您的语言支持的任何其他结构的静态类)。它应该在业务层定义。业务代码应该能够区分枚举值(例如,if(proposal.Status==ProposalStatus.Pending){poposal.Status=ProposalStatus.Approved;})。

    在应用程序甚至表示层中,定义一个字典,其中包含映射到ProposalStatus的DisplayName和定义。它仅在向用户显示数据时使用。