2

我试图了解我应该如何在项目中实现组合根。

根据我的红色,如果以错误的方式使用组合根(例如,通过在应用程序代码的很多地方引用它),您最终会得到服务定位器。

让我向您展示一个没有组合根的项目示例。

我有以下项目结构:

  • 服务器.ts
  • 域.ts
  • 应用程序.ts
  • api.ts
  • sql 存储库

服务器.ts:

此文件导入 API 并初始化服务器。

import express from 'express';
import API from './api'

const app = express();
const port = 3000;

app.use(express.json());

app.use(API);

// Start server
app.listen(port, () => {
    console.log('listening on port: ' + port);
});

域.ts:

该文件包含域的核心逻辑。

export type Entity = {
    param1: string,
    param2: string,
};

export type IRepository = {
    GetMultipleEntities(filterParam: string): Entity[] | undefined
    GetEntity(filterParam: string): Entity | undefined
    CreateEntity(entity: Entity): void
    UpdateEntity(entity: Entity): void
}

应用程序.ts:

该文件包含应用程序的用例。

import {IRepository} from './domain';

export const CheckIfEntityExists = (filterParam: string, entityRepository: IRepository): boolean => {
    let entity = entityRepository.GetEntity(filterParam);
    return typeof entity != "undefined";
};

sql-repository.ts:

该文件包含 IRepository 接口的具体实现

import {Entity, IRepository} from './domain';

export class SqlRepository implements IRepository {
    GetEntity(filterParam: string): Entity {
        //
        // some sort of logic to get entity from an sql database
        //
        return {
            param1: '',
            param2: ''
        };
    }
    GetMultipleEntities(filterParam: string): Entity[] {
        //
        // some sort of logic to get multiple entity from an sql database
        //
        return [
            {
                param1: '',
                param2: ''
            },
            {
                param1: '',
                param2: ''
            }
        ];
    }
    CreateEntity(entity: Entity): void {
        // some logic to enter new data to the sql database that represents an entity
    }
    UpdateEntity(entity: Entity): void {
        // some logic to update the entity
    }
}

api.ts:

此文件包含使用 application.ts 文件中的用例的 api

import {Router} from 'express'
import {CheckIfEntityExists} from './application';
import {SqlRepository} from './sql-repository';

const router = Router();

router.get("/exists/:filterParam", async (req, res) => {
    CheckIfEntityExists(req.params.filterParam, new SqlRepository);
    res.end()
});

export default router

Ofc 这只是一个示例,但您了解项目的外观。

从您所见,一切都很好,直到我们看到 api.ts 文件。它导入具体实现并将其注入到用例中。如果要导入和使用更多的依赖项怎么办,我不希望 api.ts 负责决定哪些实现去哪个地方,这不是它的责任。

但另一方面,我应该如何实现组合根呢?我不知道我应该如何构造完整的对象图,然后将其传递给服务器对象,以便正确的实现将转到正确的对象。

提前致谢!

4

1 回答 1

1

定义

为了给出术语Composition Root的一些范围和定义,以下是 Mark Seemann 在两篇 相关文章中的引述:

我们应该在哪里组成对象图?

尽可能靠近应用程序的入口点。

什么是组合根?

组合根是模块组合在一起的应用程序中的(最好)唯一位置。

组合根是一个应用程序基础结构组件。

组合根是特定于应用程序的;它定义了单个应用程序。在整个代码库中编写好、解耦的代码之后,组合根是您最终耦合所有内容的地方,从数据访问到(用户)界面。

影响

换句话说,您api.ts可以被视为您的服务器应用程序的入口点,因此在其中组成您的对象图是非常好的。你也可以

  1. 选择server.ts
  2. 定义一个单独的 DI 模块,就像composition-root.ts它完成所有的组成并由server.tsor导入api.ts(甚至更具凝聚力)。

这里更重要的是,您在项目的应用程序入口点附近/中拥有一个独特的位置,该位置负责创建/组合依赖项。

例子

让我们以您的具体示例为例,并假设我们要在composition-root.tsimport by中完成所有组成工作api.ts。您的依赖关系图如下所示(-->在此处表示导入):

server.ts --> api.ts --> application.ts --> domain.ts 
                                        --> sql-repository.ts

除了它之外的所有东西都composition-root.ts与它的依赖关系解耦。构造函数注入可以像文章的例子一样使用,也可以使用任何其他注入方法,具体取决于语言/框架/编码风格。您的示例看起来已经很不错了,让我们为存储库添加一些 DB 抽象层,并从api.ts.

sql-repository.ts:

export class SqlRepository implements IRepository {
  constructor(private db: DB) {}
  ...
}

api.ts:

import {CheckIfEntityExists} from "./composition-root"
...

router.get("/exists/:filterParam", async (req, res) => {
    CheckIfEntityExists(req.params.filterParam);
    res.end()
});

组合根.ts:

import {CheckIfEntityExists} from './application';
import {SqlRepository} from './sql-repository';

const db = new DB();
const sqlRepository = new SqlRepository(db);
// We do partial application here to free api.ts from 
// knowing the concrete repository.
const _CheckIfEntityExists = (filterParam: string) =>
  CheckIfEntityExists(filterParam, sqlRepository);

export { _CheckIfEntityExists as CheckIfEntityExists };

总而言之,您已将依赖项很好地封装在一个地方composition-root.ts,而应用程序架构更内层中的其他代码对它们的构造一无所知。

希望,这有帮助。

于 2019-08-24T09:35:18.827 回答