5

我已经到了 Symfony 2 / Doctrine 2 的重点,我开始意识到我们已经在我们的应用程序中将太多的业务逻辑构建到服务和控制器中 - 而模型中构建的还不够。

我们希望为我们的模型引入配置(以修改行为),从而可能使模型直接访问服务以执行其行为。

我注意到以下问题的完全错误答案被标记为正确,有 8 个赞成票 - 所以我知道我们到目前为止所采用的方法(贫血模型)被很多 Symfony 认为是“正确”的做事方式2个用户。在阅读更多领域驱动设计之后,我知道情况并非如此。

Symfony2 MVC:我的代码属于哪里?

我看到很多捆绑包定义了模型中的行为并在实体/文档中扩展了它。这种模式在一定程度上有效——但我认为我们需要引入一个额外的阶段。我们模型的某些行为是可选的,并且具有该行为将取决于在我们的应用程序中注册了哪些额外的捆绑包(因此包括 X 捆绑包将允许应用程序做更多的事情)。一个例子。

我们有一个订单对象,目前它与快递包中的实体具有双向关系,这意味着存在硬依赖。我想将其解耦并让快递包有选择地为订单添加行为。考虑这个方法调用。

// no courier bundle is registered
$order->getShippingMethods();
// throws NoAvailableShippingMethodsException;

// one bundle registered
$order-getShippingMethods();
// returns an array with one shipping method

etc....

现在我们有一个 OrderProvider 服务,它位于实体管理器的顶部——所以如果你打电话

$orderProvider->GetOrder($id);

您只需从数据库中获取“直接”返回的实体。我的问题是其他人在这里使用什么模式?我正在考虑将所有“业务逻辑”移动到实体扩展的模型类中,让服务层将实体拉出(实体是具有数据库和吸气剂属性的愚蠢记录),然后使用配置配置模型(注入 OrderProvider 服务的配置),这将修改模型的行为。对于给出的示例,我可能会执行类似(在 OrderProvider 中)..

// trimmed down for example purposes by removing exceptions etc.
public function getOrder($id) 
{
    $order = $this->orderRepository->findOneById($id);
    if ($this->couriers){
       $order->addCouriers($couriers);
    }
    return $order;
}

// this function would be called by the courier bundle automatically using semantic configuration / tags / setter injection
public function addCourier(CourierInterface $courier)
{
    $this->couriers[] = $courier;
}

我拥有的另一个选项是创建一种新类型的对象 - 它装饰基本订单并且已经配置(因为它的 ITSELF 将被定义为 DIC 中的服务)并将订单注入其中。区别是微妙的,两种方法都行,但我想知道哪条路是最好的。

最后,我对所有这一切都有一个问题,我无法理解。如果我的基本订单实体与其他实体有关系并且需要配置 THOSE 实体 - 这应该在哪里发生?例如,如果我这样访问我的客户。

$order->getCustomer();

我得到了客户(实体)。但可能是我也需要向客户对象添加一些配置 - 比如

$customer->getContactMethods();

现在,此方法的行为可能会有所不同,具体取决于我的应用程序是否注册了 twitter 包或 facebook 包或其他东西。鉴于上面的例子,我不会得到一个配置充分的客户——而是'香草'基础实体。我能看到的唯一方法是切断需要配置的实体之间的关系,并从 CustomerProvider 服务中提取实体:

$customerProvider->getCustomerByOrder($order);

在我看来,这似乎是从模型层中删除信息,并转而依赖使用多种服务来完成简单的任务(我正试图摆脱这种情况)。想法和资源链接表示赞赏。

编辑:相关-我每天都会看到第一个答案中列出的缺点,这就是我问这个问题的原因->贫血域模型:优点/缺点

4

1 回答 1

1

您的项目的复杂性似乎是模块化要求 - 应用程序行为必须可以通过捆绑包进行扩展。我不熟悉 Symfony 2 / Doctrine 2,但典型的 DDD 策略是尝试确保诸如 Order 和 Customer 之类的域实体不知道捆绑配置。换句话说,周围的服务不应该向实体添加特定于包的行为。将捆绑感知的责任委托给实体会使它们过于复杂。制造实体类层次结构以支持广泛的行为也太复杂了。相反,这种可扩展性应该由应用程序服务来管理。应用程序服务将确定加载哪些捆绑包并协调相应的实体。

另一个需要考虑的战略模式是有界上下文。是否可以将您的应用程序划分为与模块对齐的有界上下文?例如,要解决$order-getShippingMethods()方法,您可以创建两个 BC,一个有一个具有getShippingMethods()方法的订单模型,另一个没有它。拥有两个模型似乎违反了DRY,但如果模型代表不同的事物(即有运输数据的订单与没有运输数据的订单),那么实际上不会重复任何事情。

于 2012-12-15T19:48:15.917 回答