在Symfony 2中创建一个胖模型 - 组合或继承以及如何配置我的模型层

时间:2012-12-15 16:54:58

标签: symfony configuration model doctrine-orm domain-driven-design

我已经了解了Symfony 2 / Doctrine 2,我已经意识到我们已经在我们的应用程序中构建了太多的业务逻辑到服务和控制器 - 并没有足够的模型。

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

我注意到以下问题将完全错误的答案标记为正确的8个upvotes - 所以我知道我们现在采取的方法(贫血模型)被认为是'正确'的做事方式很多Symfony 2用户。在进一步阅读域驱动设计后,我知道情况并非如此。

Symfony2 MVC: where does my code belong?

我看到很多软件包定义了模型中的行为,并在实体/文档中进行了扩展。这种模式在一定程度上起作用 - 但我认为我们需要引入一个额外的阶段。我们的模型的一些行为是可选的,并且具有该行为将取决于在我们的应用程序中注册了哪些附加包(因此包括X bundle将允许应用程序执行更多操作)。一个例子。

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

// 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);

您只需从数据库中“直接”返回实体。我的问题是其他人在这里使用的图案是什么?我正在考虑将所有“业务逻辑”转移到实体扩展的模型类中,让服务层拉出实体(实体是数据库和getter中具有属性的哑记录),然后使用配置配置模型(正在注入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中的服务)并将顺序注入其中。差异是微妙的,两种方法都可行,但我想知道哪条路是最好的。

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

$order->getCustomer();

我得到了客户(实体)。但也许我需要在客户对象中添加一些配置 - 比如

$customer->getContactMethods();

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

$customerProvider->getCustomerByOrder($order);

在我看来,这是从模型层中删除信息,然后又转向依赖于使用多个服务来完成简单的任务(我正试图摆脱它)。对资源的想法和联系表示赞赏。

编辑:相关 - 我看到每天第一个答案中列出的缺点,这就是为什么我问过这个问题 - > Anemic Domain Model: Pros/Cons

1 个答案:

答案 0 :(得分:0)

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

要考虑的另一个战略模式是bounded contexts。是否可以将应用程序划分为与模块一致的有界上下文?例如,要解决$order-getShippingMethods()方法,您可以创建两个BC,其中一个存在具有getShippingMethods()方法的订单模型,另一个没有它。拥有两个模型可能看起来违反了DRY,但如果模型代表不同的东西(即一个订单包含发货数据而订单没有),则实际上没有任何内容重复。