如何处理富域模型的持久性
How to handle Persistence with Rich Domain Model
我正在重新设计我的 NodeJS 应用程序,因为我想使用 Rich Domain Model 概念。目前我使用的是贫血领域模型,但扩展性不好,我到处都看到 'ifs'。
我已经阅读了一堆博客文章和 DDD 相关的博客,但是有一些我根本无法理解的东西......我们如何正确处理 Persistence。
首先,我想描述一下我定义的图层及其用途:
持久性模型
- 定义 Table 模型。定义 Table 名称、列、键和关系
- 我将 Sequelize 用作 ORM,因此使用 Sequelize 定义的模型被视为我的持久性模型
领域模型
- 实体和行为。对应于作为业务域的一部分创建的抽象的对象
- 我已经创建了几个 classes,这里最好的事情是我可以从层次结构中受益来解决所有问题(没有大量的 ifs yay)。
数据访问对象 (DAO)
- 负责持久性模型条目到领域模型实体的数据管理和转换。所有与持久性相关的活动都属于这一层
- 在我的例子中,DAO 在持久性模型上创建的 Sequelize 模型之上工作,但是,我正在根据它们的属性在不同对象的数据库交互上序列化记录 returned。例如:如果我有一个 Table 和一个名为 'UserType' 的列,其中包含两个值 [ADMIN,USER],当我在此 table 上输入 select 条目时,我将序列化return 根据用户类型,因此类型为 ADMIN 的用户将是 AdminUser class 的实例,其中类型为 USER 的用户将只是 DefaultUser...
服务层
- 负责所有通用业务逻辑,例如不属于任何域对象行为的实用程序和其他服务
客户端层
- 任何玩弄对象并负责触发持久性的消费者class
现在,当我实现客户端层时,混乱开始了...
假设我正在实施一个新的 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 来持久化订单以及项目到底。这样做的坏处是,现在 addOrder
还必须处理交易,因为我不想在没有商品的情况下添加订单,或者在没有订单的情况下更新用户。
那么,我在这里缺少什么?
汇总。
这是故事中缺失的部分。
在您的示例中,订单项可能没有单独的 table(并且没有关系,没有外键...)。这里的项目似乎是 values(描述一个实体,即:“45 美元”),而不是 entities(随时间变化的事物,我们跟踪,即:银行账户)。因此,您不会直接保留 OrderItems,而是仅保留订单(包含其中的项目)。
我希望找到的一段代码可以代替您的评论,看起来像 orderRepository.save(oOrder);
。此外,我希望用户在订单中是一个弱引用(仅通过 id),而不是像您的 oCurrentUser.addOrder(oOrder);
代码建议的那样包含在用户中的订单。
此外,您描述的层是有意义的,但在您的示例中,您将交付问题(请求、响应等概念)与领域概念(将项目添加到新订单)混合在一起,我建议您采取查看已建立的模式以保持这些问题的分离,例如 Hexagonal Architecture。这对于 单元测试 尤其重要,因为您的 "client code" 可能是测试而不是处理函数。 retrieve/create - 做某事 - 保存代码通常是描述您的用例的 应用程序服务 中的一个函数。
Vaughn Vernon 的 "Implementing Domain-Driven Design" 是一本关于 DDD 的好书,肯定会更清楚地阐明这个主题。
我正在重新设计我的 NodeJS 应用程序,因为我想使用 Rich Domain Model 概念。目前我使用的是贫血领域模型,但扩展性不好,我到处都看到 'ifs'。
我已经阅读了一堆博客文章和 DDD 相关的博客,但是有一些我根本无法理解的东西......我们如何正确处理 Persistence。
首先,我想描述一下我定义的图层及其用途:
持久性模型
- 定义 Table 模型。定义 Table 名称、列、键和关系
- 我将 Sequelize 用作 ORM,因此使用 Sequelize 定义的模型被视为我的持久性模型
领域模型
- 实体和行为。对应于作为业务域的一部分创建的抽象的对象
- 我已经创建了几个 classes,这里最好的事情是我可以从层次结构中受益来解决所有问题(没有大量的 ifs yay)。
数据访问对象 (DAO)
- 负责持久性模型条目到领域模型实体的数据管理和转换。所有与持久性相关的活动都属于这一层
- 在我的例子中,DAO 在持久性模型上创建的 Sequelize 模型之上工作,但是,我正在根据它们的属性在不同对象的数据库交互上序列化记录 returned。例如:如果我有一个 Table 和一个名为 'UserType' 的列,其中包含两个值 [ADMIN,USER],当我在此 table 上输入 select 条目时,我将序列化return 根据用户类型,因此类型为 ADMIN 的用户将是 AdminUser class 的实例,其中类型为 USER 的用户将只是 DefaultUser...
服务层
- 负责所有通用业务逻辑,例如不属于任何域对象行为的实用程序和其他服务
客户端层
- 任何玩弄对象并负责触发持久性的消费者class
现在,当我实现客户端层时,混乱开始了...
假设我正在实施一个新的 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 来持久化订单以及项目到底。这样做的坏处是,现在 addOrder
还必须处理交易,因为我不想在没有商品的情况下添加订单,或者在没有订单的情况下更新用户。
那么,我在这里缺少什么?
汇总。
这是故事中缺失的部分。
在您的示例中,订单项可能没有单独的 table(并且没有关系,没有外键...)。这里的项目似乎是 values(描述一个实体,即:“45 美元”),而不是 entities(随时间变化的事物,我们跟踪,即:银行账户)。因此,您不会直接保留 OrderItems,而是仅保留订单(包含其中的项目)。
我希望找到的一段代码可以代替您的评论,看起来像 orderRepository.save(oOrder);
。此外,我希望用户在订单中是一个弱引用(仅通过 id),而不是像您的 oCurrentUser.addOrder(oOrder);
代码建议的那样包含在用户中的订单。
此外,您描述的层是有意义的,但在您的示例中,您将交付问题(请求、响应等概念)与领域概念(将项目添加到新订单)混合在一起,我建议您采取查看已建立的模式以保持这些问题的分离,例如 Hexagonal Architecture。这对于 单元测试 尤其重要,因为您的 "client code" 可能是测试而不是处理函数。 retrieve/create - 做某事 - 保存代码通常是描述您的用例的 应用程序服务 中的一个函数。
Vaughn Vernon 的 "Implementing Domain-Driven Design" 是一本关于 DDD 的好书,肯定会更清楚地阐明这个主题。