【问题标题】:Domain Driven Design for Node.jsNode.js 的领域驱动设计
【发布时间】:2017-06-06 22:04:31
【问题描述】:

我目前正在构建一个基于 Node.js 微服务的应用程序,我想利用领域驱动设计作为构建单个服务的指南。我有几个问题如下:

  1. 据我了解,域层通常应包含存储库接口,然后应在基础架构层中创建这些接口的特定实现。这将底层存储/技术问题从您的领域层中抽象出来。在我的项目环境中,鉴于 JavaScript 本机不支持接口之类的东西,如何实现类似的效果?
  2. 特别是一项服务将通过 OAuth 处理身份验证。人们通常会将与 OAuth 相关的逻辑归类为应用程序服务吗?还是属于基础设施层?我的理解是,它与基础架构无关,也与核心域无关,但作为向客户端提供应用程序的一部分仍然是必需的。因此,我现在将它放在应用层中。
  3. 从第 2 点开始,与 OAuth 相关的实体/存储库(即令牌/客户端)最好放置在哪里?我很想将它们与我的其他实体/存储库一起保留在域层中,即使它们在技术上不是业务问题。

在上面添加一些注释:

  1. 热衷于使用 TypeScript(建议 here)。
  2. 我完全意识到有些人可能认为 JavaScript 不适合 DDD 类型的方法。我熟悉其中的一些陷阱,并且我再次使用 DDD 作为指导。

顺便说一句,我提出了以下项目结构,其灵感来自 this Laravel 中基于 DDD 的项目的一个很好的例子:

app/
----app.js
----package.json
----lib/
--------app/
------------service/
----------------oauth.js
----------------todo.js
--------domain/
------------list/
----------------model.js
----------------repository.js
----------------service.js
------------task/
----------------model.js
----------------repository.js
----------------service.php
--------http/
------------controller/
----------------list.js
----------------task.js
------------middleware/
----------------auth.js
----------------error.js
--------infrastructure/
------------db.js
------------logger.js
------------email.js

我很想听听您对此布局的任何想法。我完全意识到项目结构这个话题在某种程度上是基于意见的,但我总是很想听听别人怎么说。

【问题讨论】:

  • 您认为 DDD 设计会告诉您需要为项目中使用的子目录选择哪些名称?我对此知之甚少,听起来好像还没有那么详细。似乎更多的是确保您在一个地方构建业务模型逻辑并保留所有其他支持的东西(例如用户身份验证和其他简单的基础设施东西),这些都是必需的,但与实际业务逻辑无关在一个很好的方便的单独的地方。
  • @jfriend00 完全同意。也就是说,在我看来,DDD 的主要驱动力——围绕“核心业务领域”构建。也许为了澄清起见,我将编辑我的帖子以强调我不太关注结构/命名的事实,更多的是确保正确的分类/分离。
  • 但是您的整个问题听起来像是关于将内容放入哪些目录,而不是定义应用程序中的核心业务逻辑以及如何将其与其他所有内容分开构建。坦率地说,其他一切的去向并不是那么重要(例如,什么是服务,什么是基础设施)。这些只是应该选择的标签,以使您的项目布局对其他人清晰并方便使用。
  • 据我了解,DDD 的主要目标是确保您知道核心业务逻辑是什么以及它的结构以及它是如何编写的,以独立于所有真正没有任何支持的代码与核心业务逻辑有关。在许多方面,它与非常好的面向对象设计没有什么不同,特别强调确保业务逻辑代码不会与非业务逻辑代码混在一起。
  • Tbh,我不认为有“错误”的方式来构建您的项目。有时在开始一个新项目或一个新组件时,我会在同一个文件夹/命名空间中设置所有单个文件。然后我开始重构,并一起行动。它避免关注基础设施或设计相关的名称,而更多地关注业务,这就是 ddd 的全部意义所在。

标签: javascript node.js domain-driven-design


【解决方案1】:

你考虑过wolkenkit吗?

它是适用于 Node.js 和 JavaScript 的 CQRS 和事件溯源框架,与领域驱动设计 (DDD) 配合得非常好。它可能会让您很好地了解如何构建代码,以及无需重新发明轮子即可执行事物的运行时。

我认识它背后的人,他们为此投入了 3-4 年的思想、血汗。

【讨论】:

  • 除此之外,在 2018 年国际 Javascript 会议上以 wolkenkit 的一位创建者的示例讨论 Node.js DDD:youtu.be/rolfJR9ERxo
【解决方案2】:

领域驱动设计将系统分解为一组有界上下文/服务/微服务。但是,您设计每个服务的方式是单独的,并且取决于服务的业务领域。例如,您企业的核心域服务和支持域服务应该以不同的方式进行架构。

【讨论】:

    【解决方案3】:

    即使这个问题已经很老了,我认为在你的第一次审讯中增加精确度会很有用:

    据我了解,域层通常应包含存储库接口,然后应在基础架构层中创建这些接口的特定实现。这将底层存储/技术问题从您的领域层中抽象出来。在我的项目中,鉴于 JavaScript 本身并不支持接口之类的东西,如何实现类似的效果?

    JavaScript 不提供接口。与其模仿 OOP 概念,不如看看功能更强大的概念?高阶函数非常适合 javascript,您可以使用它们来“声明”依赖项并在运行时注入它们:

    const { FooData } = require('./data');
    
    const getFooOfId = (getFooOfIdImpl = async (fooId) => { throw new Error(`Can't retrieved foo of id ${fooId} : missing implementation`) }) => async fooId => {
      try {
        const fooData = await getFooOfIdImpl(fooId);
        return FooData(fooData);
      } catch (err) {
        throw new Error(`Unable to retrieve Foo with id ${fooId}`);
      }
    }
    
    /*
    This function is used to build a concrete implementation, for example with an in-memory database :
    const inMemoryFooDatabase = {
      foo17: {
        id: 'foo17',
        foo: 'a foo value',
        bar: 'a bar value',
        foobaz: 1234,
      },
    };
    const getFooOfIdFromInMemoryDatabase = getFooOfId(fooId => inMemoryFooDatabase[fooId])
    */
    
    module.exports = {
      getFooOdId,
    }
    

    没有什么可以阻止您完全绕过此功能,因为 javascript 中没有强大的类型检查,但它充当了您的域“接口”需求的声明。

    如果您想了解更多相关信息,可以阅读我关于此主题的帖子:Domain-Driven Design for JavaScript developers

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-06
      • 2011-02-04
      • 2016-09-29
      • 1970-01-01
      • 1970-01-01
      • 2011-01-30
      • 2011-03-02
      相关资源
      最近更新 更多