平衡域中的逻辑布局和系统的一般性能

Balancing logic placement in the domain and general perfomance of the system

提问人:Bernardo Benini Fantin 提问时间:11/15/2023 更新时间:11/16/2023 访问量:27

问:

我正在使用 DDD(在带有 TS 的 Node 中)编写我的第一个应用程序,我首先开始编写所有域 - 在启动存储库/DB 之前,然后是应用程序,同时为每个实体编写单元测试。随着我的领域发展,我开始对我的业务逻辑产生更多的怀疑。我将在下面举例说明。

我的一个业务逻辑指出,“请求者”可以创建和删除标签。因此,由于我首先编写了所有域,因此我在我的实体中创建了一个方法。该方法定义如下:createSectorTagRequester

  public createSectorTag(data: ISectorTagData): number {
    this.checkIfIsActive()
    const sector_tag = new SectorTag(data)
    return this.sector_tags.push(sector_tag)
  }

如您所见,该方法创建一个实体并将其添加到我的实体中的私有数组中。由于这种方法中有一些逻辑,我认为这一段代码存在于我的领域中是有意义的。但是,与此同时,我认为,如果对于一件简单的事情来说,这不是太多的工作:我的意思是,每次我的应用程序调用我的域请求者添加标签时,它都必须创建一个实体,然后将其添加到requester.sector_tags;之后,我必须获取新的并将其保存到带有存储库的数据库中。SectorTagRequesterthis.checkIfIsActive()RequesterSectorTag

另一个例子是方法。此操作有一个逻辑,该逻辑应验证请求者中的任何请求是否具有该标记,如果有任何标记,则应引发异常。该方法的定义:deleteSectorTag

  public deleteSectorTag(index: number): void {
    /**
     * Here I'll have to check if any of the requests inside the Request entity have
     * the tag specified by the index in the parameter, and if so, raise an exception
     */
    this.sector_tags.splice(index, 1)
  }

但同样,我认为所有这些业务逻辑都增加了对处理的过多权重。我必须从数据库中获取请求者的所有请求,创建一个实体,在实体中添加请求,然后进行验证。但这一切似乎都可能是数据库中用于验证的查询。Requester

嗯,我真的很喜欢把所有逻辑都放在域内的想法;这让我很高兴,因为我的代码似乎越来越接近可靠性。它适应了我的思维方式,但同时我担心性能。

如果有人能给我提示,我将不胜感激。

以下是完整的实体:Requester

import { BudgetRequest, SectorTag, BudgetEstimator } from '@/domain/BudgetRequest/entities'
import { RequesterIsInactiveError } from '@/domain/BudgetRequest/exceptions'
import {
  IBudgetRequestData,
  IRequesterData,
  ISectorTagData,
} from '@/domain/BudgetRequest/interfaces'

export class Requester {
  private readonly active: boolean
  private readonly name: string
  private readonly cnpj: number
  private budget_requests: Array<BudgetRequest> = []
  private sector_tags: Array<SectorTag> = []
  private budget_estimators: Array<BudgetEstimator> = []

  constructor(data: IRequesterData) {
    this.active = data.active
    this.name = data.name
    this.cnpj = data.cnpj
  }

  get budgets_list(): Array<BudgetRequest> {
    return [...this.budget_requests]
  }

  get sector_tags_list(): Array<SectorTag> {
    return [...this.sector_tags]
  }

  public createBudgetRequest(data: IBudgetRequestData): number {
    this.checkIfIsActive()
    const budget_request: BudgetRequest = new BudgetRequest(data)

    return this.budget_requests.push(budget_request)
  }

  public createSectorTag(data: ISectorTagData): number {
    this.checkIfIsActive()
    const sector_tag = new SectorTag(data)
    return this.sector_tags.push(sector_tag)
  }

  public deleteSectorTag(index: number): void {
    /**
     * Here I'll have to check if any of the requests inside the Request entity have
     * the tag specified by the index in the parameter, and if so, raise an exception
     */
    this.sector_tags.splice(index, 1)
  }

  public bindSectorTagToBudgetRequest(i: number, j: number): void {
    this.budget_requests[i].addSectorTag(this.sector_tags[j])
  }

  private checkIfIsActive(): void {
    if (!this.active) {
      throw new RequesterIsInactiveError()
    }
  }
}

节点.js 性能 架构 领域驱动设计 业务逻辑

评论


答:

1赞 VoiceOfUnreason 11/16/2023 #1

我的一个业务逻辑指出,“请求者”可以创建和删除标签。因此,由于我首先编写了所有域,因此我在请求者实体中创建了一个方法 createSectorTag。

  1. 这可能是一个错误
  2. 这不是你的错
  3. 文学很糟糕

实现领域模型并不意味着我们必须放弃合理的数据模型。

如果你的实现需要将一堆实际上没有用的信息加载到内存中,这表明你的模型目前不适合你的问题。

当你在两条信息之间有关系时,你需要仔细考虑三种情况

  • 一条信息是另一条信息的“一部分”(这是您在此处选择的;标记列表是请求者数据结构的“一部分”)
  • 这两条信息是分开的,但一条包含指向另一条的“链接”
  • 这两条信息是分开的,它们之间的“链接”本身就是一个独立的东西,有自己的生命周期

快速启发式方法,用于了解两条信息是否属于一起:对其中一条信息进行更改是否需要锁定对另一条信息的所有更改?

在您的示例中:修改标签是否应该阻止对预算请求的更改?

如果答案是肯定的 - 对业务来说,信息始终保持一致至关重要 - 那么将这些信息组合在一起是有意义的。

如果答案是否定的,那么您应该考虑独立锁定信息,这通常会涉及更多实体。

(提示:答案通常是“否”。

你可以用一个问题来区分这两者:数据需要多快达成一致。如果你能提出在10秒到一分钟之间改变所有信息,而领域专家说“是的,没关系”,那么信息肯定不需要共享一个锁,你应该考虑不共享的候选设计,看看它是否能让事情变得更好。


此外,值得注意的是,我们数据模型的某些部分实际上“只是”位于其他地方的信息缓存,我们真正需要做的就是使用通用工具(例如:级联删除)更新本地缓存的副本。

在这些地方,构建领域模型可能比它的价值付出更多的努力。

不要创建域模型来解决贫血数据存储可以更好地解决的问题。