2

我正在使用一个域模型,其中我有一个 Reservation 类:

class Reservation
{
    public function changeStatus($status) { ... }
}

因为该changeStatus()方法只能在发送所有适当通知(电子邮件,...)的上下文中调用,所以我想将此方法的调用限制为ReservationService

class ReservationService
{
    public function confirmReservation(Reservation $reservation)
    {
        $reservation->changeStatus(Reservation::STATUS_CONFIRMED);
        // commit changes to the db, send notifications, etc.
    }
}

因为我正在使用 PHP,所以没有包可见性朋友类这样的概念,所以我的changeStatus()方法只是公共的,因此可以从应用程序的任何地方调用。

我发现这个问题的唯一解决方案是使用某种双重调度

class Reservation
{
    public function changeStatus(ReservationService $service)
    {
        $status = $service->getReservationStatus($this);
        $this->setStatus($status);
    }

    protected function setStatus($status) { ... }
}

潜在的缺点是:

  • 这使设计有点复杂
  • 这使实体知道服务,不确定这是否真的是一个缺点

你们对上述解决方案有什么意见,或者有更好的设计建议限制对这种changeStatus()方法的访问吗?

4

4 回答 4

4

使用强制执行所需上下文的接口:

interface INotifiable {
  public function updated( $reservation );
}

class Reservation {
  public function changeStatus( $status, INotifiable $notifiable ){
    $this->setStatus( $status );
    $notifiable->updated( $this );
  }
}

class EmailNotifier implements INotifiable {
  public function updated( $reservation ){
    $this->sendUpdateEmail( $reservation ); //or whatever
  }
}

然后,预订不需要了解有关服务的任何信息。另一种方法是在 Reservation 上定义事件,但这增加了您可能不需要的复杂性。

于 2011-11-08T12:25:39.097 回答
1

您可以将消息从一个域实体发送到另一个域。只有能够产生某些消息的对象才会调用相关方法。代码片段如下。该解决方案适用于依赖注入是一种宗教的项目,例如PHP+DDD

预订服务获取消息工厂。工厂是通过构造方法注入的。没有这个工厂的对象不能发出这种消息。(当然,您必须将对象实例化限制为工厂。)

class Domain_ReservationService
{

    private $changeStatusRequestFactory;

    public function __construct(
        Message_Factory_ChangeStatusRequest $changeStatusRequestFactory
    ) {
        $this->changeStatusRequestFactory = $changeStatusRequestFactory;
    }

    public function confirmReservation(Domain_Reservation $reservation) {

        $changeStatusRequest = $changeStatusRequestFactory->make(
            Reservation::STATUS_CONFIRMED
        );

        $reservation->changeStatus($changeStatusRequest);
        // commit changes to the db, send notifications, etc.

    }

}

Reservation 对象检查消息的内容并决定要做什么。

class Domain_Reservation
{

    public function changeStatus(
        Message_Item_ChangeStatusRequest $changeStatusRequest
    ) {
        $satus = $changeStatusRequest->getStatus();
        ...
    }

}

消息对象是一个 DDD 值对象。(有时它就像一种策略。)

class Message_Item_ChangeStatusRequest
{
    private $status;

    public function __construct( $status ) {
        $this->$status = $status;
    }

    public function getStatus() {
        return $this->$status;
    }

}

这个工厂生产消息​​。

class Message_Factory_ChangeStatusRequest
{

    public function make($status) {
        return new Message_Item_ChangeStatusRequest ($status);
    }

}

所有领域对象都由该层工厂产生。

class Domain_Factory
{

    public function makeReservationService() {
        return new Domain_ReservationService(
            new Message_Factory_ChangeStatusRequest()
        );
    }

    public function makeReservation() {
        return new Domain_Reservation();
    }

}

上面的类可以在您的应用程序中使用,如下所示。

$factory = new Domain_Factory();
$reservationService = $factory->makeReservationService();
$reservation = $factory->makeReservation();
$reservationService->confirmReservation($reservation);

但我不明白为什么你不想使用 $reservation->beConfirmed() 而不是传递状态常量。

于 2012-08-30T15:26:08.557 回答
1

实际上,这听起来像是缺少一个非常重要的概念,即ProcessManager。ProcessManager 表示跨越多个上下文的分布式业务事务。实际上它是一个简单的有限状态机

示例工作流程:

  1. APlaceReservationCommand被发送到ReservationContext,它处理它并发布订阅的ReservationWasPlacedEventa 。ReservationProcessManager
  2. ReservationProcessManager接收并验证它ReservationWasPlacedEvent可以转换到下一步。然后我发送一个NotfiyCustomerAboutReservationCommandNotificationContext. 它现在听ReservationNotificationFailedEventReservationNotificationSucceededEvent
  3. 现在,只有ReservationProcessManager在收到.ConfirmReservationCommandReservationContextReservationNotificationSucceededEvent

这里的诀窍Reservation. ProcessManager负责跟踪此业务事务的状态。最有可能存在一个ReservationProcess包含ReservationProcess::INITIATEDReservationProcess::CUSTOMER_NOTIFICATION_REQUESTEDReservationProcess::CUSTOMER_NOTIFIEDReservationProcess::CONFIRMATION_REQUESTED等状态的聚合ReservationProcess::CONFIRMED。最后一个状态表示将过程标记为完成的有限状态。

于 2016-08-05T08:25:30.960 回答
0

Symfony2 和 FLOW3 框架采用的其中一件事是使用@api注释注释标记其稳定的公共 API。

虽然这不是您正在寻找的东西,但它很接近。它记录了用户可以依赖的 API 部分。另外,您的实体不必了解服务,避免了邪恶的循环依赖。

例子:

class Request
{
    /**
     * Gets the Session.
     *
     * @return Session|null The session
     *
     * @api
     */
    public function getSession()
    {
        return $this->session;
    }
}
于 2011-11-08T12:21:33.597 回答