我有一个类,提案,该建议具有$ proposalStatus的$状态。现在,在大多数情况下,提案的身份并没有改变……但它确实是(有点)。它的$ id是修复的,但是$ display_name和$ deaction(Just Strings)可以在很长一段时间内随着主数据的更新,>,但在HTTP请求response 。
问题#1- 实体或值对象?
值对象是从不应该更改或仅在应用程序执行的特定执行的寿命中更改?如果更改了显示名称或定义,那么我确实希望/希望每个人都更改它。但是,由于它们可以在提案之外定义,所以我认为只是直接使它们成为实体而不是价值对象。
提案从来没有更改提案的属性,仅更改提案的属性。
问题#2 - 如何正确设置域驱动设计的状态?
我的提案对象具有管理状态的能力,但是要做到这一点,我需要具有特定的建议status对象。仅,允许其返回的状态列表在哪里?
- 我可以从提案的质量中获得它...但是一切都是通过提议毫无意义的总体访问的。
- 我可以拥有与提案的$ id相匹配的常数,但这似乎是错误的。
- 我可以做一个提案statusrepository ...但是我应该从建议中访问另一个存储库吗?
- 我可以将所有可能状态的数组作为键作为密钥并添加到建议中,但这与存储库没有太大不同...
示例:
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
}
}
从我从您的域中了解到,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...
}
请注意,不变性是价值对象的重要特征。
如果您的ProposalStatus
是固定的值列表,则只需使用枚举方法。
否则,您需要将ProposalStatus
视为用户可以创建,更新和删除的CC_4(我想)。在将ProposalStatus
分配给Proposal
时,您只需要ID即可。如果您想检查给定的ID是否存在,则只需使用专门的查询来满足不变性。规格模式在这里很合适。
class ProposalStatusExistsSpecification
{
public function isSatisfiedBy(string $proposalSatusId): bool
{
//database query to see if the given ID exists
}
}
您可以在此处找到Interfaces
来实现您的规范。
是所有可能的提案状态静态列表吗?我觉得是这样的。因此,提案史图斯看起来像是一个简单的枚举。诸如DisplayName和定义之类的属性与业务代码无关。
只是将提案史图斯定义为枚举(具有只读字段或您语言支持的任何其他结构的静态类)。它在业务层中定义。业务代码应该能够区分枚举值(例如(form.status == proposalStatus.perding){poposal.status = proposalStatus.appraved;})。
在应用程序甚至演示层中定义了一个字典,该字典包含显示为proposalStatus的displayName和定义。仅在向用户显示数据时才使用。