6

I am redesigning my NodeJS application because I want to use the Rich Domain Model concept. Currently I am using Anemic Domain Model and this is not scaling well, I just see 'ifs' everywhere.

I have read a bunch of blog posts and DDD related blogs, but there is something that I simply cannot understand... How do we handle Persistence properly.

To start, I would like to describe the layers that I have defined and their purpose:

Persistence Model

  • Defines the Table Models. Defines the Table name, Columns, Keys and Relations
  • I am using Sequelize as ORM, so the Models defined with Sequelize are considered my Persistence Model

Domain Model

  • Entities and Behaviors. Objects that correspond to the abstractions created as part of the Business Domain
  • I have created several classes and the best thing here is that I can benefit from hierarchy to solve all problems (without loads of ifs yay).

Data Access Object (DAO)

  • Responsible for the Data management and conversion of entries of the Persistence Model to entities of the Domain Model. All persistence related activities belong to this layer
  • In my case DAOs work on top of the Sequelize models created on the Persistence Model, however, I am serializing the records returned on Database Interactions in different objects based on their properties. Eg.: If I have a Table with a column called 'UserType' that contains two values [ADMIN,USER], when I select entries on this table, I would serialize the return according to the User Type, so a User with Type: ADMIN would be an instance of the AdminUser class where a User with type: USER would simply be a DefaultUser...

Service Layer

  • Responsible for all Generic Business Logic, such as Utilities and other Services that are not part of the behavior of any of the Domain Objects

Client Layer

  • Any Consumer class that plays around with the Objects and is responsible in triggering the Persistence

Now the confusion starts when I implement the Client Layer...

Let's say I am implementing a new REST API:

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

On my handler function I would have something like:

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?

}

One way I found to make it work is to merge the Persistence Model and the Domain Model, which means that oCurrentUser.addOrder(...) would execute the business logic required and would call the OrderDAO to persist the Order along with the Items in the end. The bad thing about this is that now the addOrder also have to handle transactions, because I don't want to add the order without the items, or update the User without the Order.

So, what I am missing here?

4

1 回答 1

1

聚合体。

这是故事中缺失的部分。

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

我希望找到的代替您的评论的代码可能如下所示orderRepository.save(oOrder);。此外,我希望用户是订单中的弱引用(仅通过 id),而不是您的oCurrentUser.addOrder(oOrder);代码所建议的用户中包含的订单。

此外,您描述的层是有意义的,但在您的示例中,您将交付问题(请求、响应等概念)与域概念(将项目添加到新订单)混合在一起,我建议您查看已建立的模式保持这些问题解耦,例如Hexagonal Architecture。这对于单元测试尤其重要,因为您的“客户端代码”可能是测试而不是处理程序函数。检索/创建 - 做某事 - 保存代码通常是描述您的用例的应用程序服务中的一个函数。

Vaughn Vernon 的“实施领域驱动设计”是一本关于 DDD 的好书,肯定会更清楚地阐明这个主题。

于 2019-05-23T11:05:00.263 回答