У меня есть класс, Proposal, который имеет статус $ ProposalStatus. Теперь, по большей части, идентичность ProposalStatus не меняется … но она (вроде). $ Id является фиксированным, но $ display_name и $ definition (только строки) могут меняться в течение длительного периода времени при обновлении основных данных, но он НЕ изменится в течение срока действия HTTP-запроса-ответа.
Вопрос 1 — Сущность или объект значения?
Является ли объект ценности чем-то никогда должен измениться или только никогда не должен меняться в течение срока действия конкретного выполнения приложения? Если отображаемое имя или определение будут изменены, то я действительно ожидаю / хочу, чтобы оно было изменено для всех. Тем не менее, так как они может быть определено вне предложения Я думаю, что просто прямо делает их сущностями, а не объектами стоимости.
Предложение никогда не изменяет атрибуты ProposalStatus, оно только меняет то, что имеет ProposalStatus.
Вопрос 2 — Как правильно установить статус для доменного дизайна?
У моего объекта предложения есть возможность управлять его статусами, но для этого мне нужен определенный объект ProposalStatus. Только, где находится список статусов, который позволяет ему вернуть ожидаемое право?
Пример:
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 выглядит как простое перечисление. Такие атрибуты, как DisplayName и Definition, не связаны с бизнес-кодом.
Просто определите ProposalStatus как перечисление (статический класс с полями только для чтения или любой другой структурой, поддерживаемой вашим языком). Это должно быть определено на бизнес-уровне. Код бизнес должен уметь различать значения перечисления (например, if (offer.Status == ProposalStatus.Pending) {poposal.Status = ProposalStatus.Approved;}).
В приложении или даже на уровне представления определите словарь, содержащий DisplayName и Definition, сопоставленные с ProposalStatus. Он будет использоваться только при отображении данных пользователям.
Из того, что я понимаю из вашего домена, 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...
}
Обратите внимание, что неизменность является важной характеристикой объекта Value.
В случае, если ваш ProposalStatus
это фиксированный список значений просто перейти на перечисление подход.
В противном случае вам нужно лечить ProposalStatus
как AggregateRoot
что пользователи могут создавать, обновлять и удалять (я думаю). При назначении ProposalStatus
к Proposal
вам просто нужно удостоверение личности. Если вы хотите проверить, что данный идентификатор существует, вам просто нужно удовлетворить инвариант с помощью специального запроса. Спецификация шаблон хорошо подходит здесь.
class ProposalStatusExistsSpecification
{
public function isSatisfiedBy(string $proposalSatusId): bool
{
//database query to see if the given ID exists
}
}
Ты можешь найти Вот Interfaces
реализовать вашу спецификацию.