如何使用富域模型处理持久性

时间:2017-02-16 23:23:21

标签: node.js architecture domain-driven-design sequelize.js rich-domain-model

我正在重新设计我的NodeJS应用程序,因为我想使用Rich Domain Model概念。目前我正在使用贫血领域模型,这不能很好地扩展,我只是在任何地方看到'ifs'。

我已经阅读了一些博客文章和DDD相关的博客,但有些东西我根本无法理解......我们如何正确处理持久性。

首先,我想描述一下我定义的图层及其用途:

持久性模型

  • 定义表模型。定义表名,列,键和关系
  • 我使用Sequelize作为ORM,因此使用Sequelize定义的模型被视为我的持久模型

域模型

  • 实体和行为。与作为业务域一部分创建的抽象相对应的对象
  • 我创建了几个类,这里​​最好的是我可以从层次结构中受益来解决所有问题(没有ifs yay的负载)。

数据访问对象(DAO)

  • 负责数据管理并将持久性模型的条目转换为域模型的实体。所有与持久性相关的活动都属于此层
  • 在我的案例中,DAO在Persistence Model上创建的Sequelize模型之上工作,但是,我根据其属性序列化不同对象中的Database Interactions返回的记录。例如:如果我有一个名为'UserType'的列,其中包含两个值[ADMIN,USER],当我在此表上选择条目时,我会根据用户类型序列化返回,因此具有类型的用户: ADMIN将是AdminUser类的一个实例,其中类型为USER的用户只是一个DefaultUser ...

服务层

  • 负责所有通用业务逻辑,例如不属于任何域对象行为的实用程序和其他服务

客户端层

  • 任何使用对象并负责触发持久性的消费者类

现在,当我实施客户端层时,混乱就开始了......

假设我正在实施一个新的REST API:

POST: .../api/CreateOrderForUser/
{
  items: [{
    productId: 1,
    quantity: 4
  },{
    productId: 3,
    quantity: 2
  }]
}

在我的处理函数上,我会有类似的东西:

function(oReq){
  var oRequestBody = oReq.body;
  var oCurrentUser = oReq.user; //This is already a Domain Object
  var aOrderItems = oRequestBody.map(function(mOrderData){
    return new OrderItem(mOrderData); //Constructor sets the properties internally
  });
  var oOrder = new Order({
    items: aOrderItems
  });

  oCurrentUser.addOrder(oOrder);

  // So far so good... But how do I persist whatever 
  // happened above? Should I call each DAO for each entity 
  // created? Like, first create the Order, then create the 
  // Items, then update the User?

}

我发现使其工作的一种方法是合并持久性模型和域模型,这意味着oCurrentUser.addOrder(...)将执行所需的业务逻辑,并将调用OrderDAO以保持Order与Item in in in结束。关于这一点的坏处是,现在addOrder也必须处理交易,因为我不想在没有项目的情况下添加订单,或者在没有订单的情况下更新用户。

那么,我在这里缺少什么?

1 个答案:

答案 0 :(得分:0)

聚合。

这是故事中缺少的部分。

在您的示例中,订单项可能没有单独的表(并且没有关系,没有外键...)。这里的项目似乎是(描述实体,即:“ 45美元”),而不是实体(随时间变化并且我们跟踪的事物,即:银行)帐户)。因此,您不会直接保留OrderItems,而是仅保留Order(其中包含项目)。

我希望找到的代替您的注释的代码看起来像orderRepository.save(oOrder);。此外,我希望用户在订单中成为弱引用(仅按ID),而不是您的oCurrentUser.addOrder(oOrder);代码所建议的用户中包含的订单。

此外,您描述的层次是合理的,但是在您的示例中,您将交付问题(例如请求,响应等概念)与领域概念(将项目添加到新订单中)混合在一起,我建议您看看以既定的模式来消除这些顾虑,例如六角形建筑。这对于单元测试尤其重要,因为您的“客户端代码”很可能是测试而不是处理程序功能。检索/创建-做某事-保存代码通常是应用服务中描述您的用例的功能。

沃恩·弗农(Vaughn Vernon)的“实现域驱动设计”是一本关于DDD的好书,肯定会更加阐明该主题。