Nestjs:DDD实现 - A
#javascript #typescript #体系结构 #nestjs

您可以阅读西班牙语aquí

中的版本

嘿!在本文中,我们将讨论清洁体系结构的实现,特别是域驱动的设计(ddd) nestjs 。我们将介绍从文件夹结构到层之间的依赖性注入的各个方面。我们将使用ODM Mongoose,但请随时使用您的首选ORM/ODM!

跳过整个文章,然后查看源代码here

为什么要Nestjs?

Nestjs是一个node.js框架,为我们提供了一个用于后端应用程序开发的全面工具箱。它提供了一个分层(基于模块的)体系结构,以分离我们的应用程序中的职责。毫无疑问,这是一个值得考虑的框架,创建一个基于node.js的项目。

为什么要DDD?

域驱动的设计是一种软件设计方法,侧重于理解和建模应用程序的域,围绕关键业务概念构建软件。
此外,作为架构“模式”使实现和代码可读性更容易。
如果您没有关于DDD的先验知识,我建议阅读此article

文件夹架构

.
└──src
   ├──application
   |  ├──cat (module)
   |  └──organization (module)
   ├──domain
   |  ├──entities
   |  └──interfaces
   └──infrastructure
      ├──schemas
      └──services

看到了这一点,我们需要记住DDD体系结构中定义的层之间的依赖项。

ddd-image

域层

在此文件夹中,我们将定义将在应用程序中管理服务的实体和接口。如果您想以这种方式看到它,我们正在为我们的数据库创建实体和应用程序中存在的函数,而这些实体将在不导入或注入我们将使用的数据库的依赖项。请记住,在此层中,没有任何形式的存储库处理。

让我们创建实体:

export enum CatStatus {
    AVAILABLE = 'available',
    PENDING = 'pending',
    ADOPTED = 'Adopted',
}

export class CatEntity {
    id?: string;
    name: string;
    age: number;
    color: string;
    status: CatStatus;
}

现在接口:

import { CatEntity } from "../Entities/Cat.entity";

export interface ICatRepository {
    findById(id: string): Promise<CatEntity>;
    create(cat: CatEntity): Promise<string>;
    delete(id: string): Promise<boolean>;
}

export const ICatRepository = Symbol('ICatRepository');

您可以看到,除了创建界面外,我们还创建了一个符号。我们这样做是因为我们将此接口注入应用程序层。

,我们将更详细地深入研究。

基础架构层

在此层中,我们将定义模式(在这种情况下)和与数据库相关的服务。所有这些元素将从我们在域层中创建的实体和接口继承其属性。

我们通过从实体CatEntity实现字段来创建模式:

@Schema()
export class Cats implements CatEntity {
    @Prop({
        type: String,
        required: true
    })
    name: string;

    @Prop({
        type: Number,
        required: true
    })
    age: number;

    @Prop({
        type: String,
        required: true
    })
    color: string;

    @Prop({
        type: String,
        required: true
    })
    status: CatStatus;
}

export type CatDocument = Cats & Document;

export const CatSchema = SchemaFactory.createForClass(Cats);

现在您可能想知道,我们是否需要为数据库管理创建一个单独的实体?答案是肯定的。我们需要将实体的属性映射到我们的ORM/ODM可以解释的语言中。您可以将它们视为配置文件。

现在是时候从事服务了。

@Injectable()
export class CatMongoRepository implements ICatRepository {
    constructor(
        @InjectModel(Cats.name)
        private catModel: Model<CatDocument>
    ) { }

    async create(cat: CatEntity): Promise<string> {
        const result = await this.catModel.create(cat);
        return result._id;
    }

    async findById(id: string): Promise<CatEntity> {
        const cat = await this.catModel.findById(id);
        return cat;
    }

    async delete(id: string): Promise<boolean> {
        const result = await this.catModel.deleteOne({ _id: new Types.ObjectId(id) });
        return result.deletedCount > 0;
    }

}

在这侧,我们将处理所有数据库查询。
将业务规则远离这里!

依赖注入

很棒,我们现在已经建立了域和基础架构层。现在,我们只需要配置依赖项注射。

首先,我们需要在基础架构层中导出服务,以及架构:

const mongooseSchemas = [{
    name: Cats.name,
    schema: CatSchema
}, {
    name: Organizations.name,
    schema: OrganizationSchema
}];

@Module({
    imports: [
        MongooseModule.forRoot(`mongodb://admin:password123@mongo-ddd:27017/accounts?authSource=admin&readPreference=primary&ssl=false&directConnection=true`),
        MongooseModule.forFeature(mongooseSchemas)
    ],
    controllers: [],
    providers: [
        OrganizationMongoRepository,
        CatMongoRepository
    ],
    exports: [
        MongooseModule.forFeature(mongooseSchemas),
        OrganizationMongoRepository,
        CatMongoRepository,
    ]
})
export class InfrastructureModule { };

现在,在域层中,要处理依赖项注入,我们需要导入基础架构层并配置要从域层注入接口的基础架构服务:

@Module({
    imports: [InfrastructureModule],
    controllers: [],
    providers: [{
        provide: ICatRepository,
        useClass: CatMongoRepository
    }, {
        provide: IOrganizationRepository,
        useClass: OrganizationMongoRepository
    }],
    exports: [{
        provide: ICatRepository,
        useClass: CatMongoRepository
    }, {
        provide: IOrganizationRepository,
        useClass: OrganizationMongoRepository
    }]
})
export class DomainModule { };

虽然这似乎是矛盾的,但我们正在打破DDD的规则之一:没有人应该依靠基础架构层。但是,由于Nestjs的局限性,这是我发现完成此任务的唯一方法。
然而,依赖性最小,仅限于domain.module.ts中的配置。

使用服务

在我们的cat模块中,在应用程序层中,我们需要导入域模块。这样,我们可以通过依赖注入来利用接口!:

@Injectable()
export class CatService {
    constructor(
        @Inject(ICatRepository)
        private catRepository: ICatRepository
    ) { }

    async create(data: CatCreateDto): Promise<string> {
        const cat = new CatEntity();
        cat.name = data.name;
        cat.age = data.age;
        cat.color = data.color;
        cat.status = CatStatus.AVAILABLE;

        return await this.catRepository.create(cat);
    }

    async findById(id: string): Promise<any> {
        const result = await this.catRepository.findById(id);
        return result;
    }

    async delete(id: string): Promise<boolean> {
        const result = await this.catRepository.delete(id);
        return result;
    }
}

概括

这是在Nestjs中实现DDD的快速概述,这是您使用的数据库的不可思议的,无论是否是SQL。
毫无疑问,还有很多要探索的事实,例如我们仍然处理贫血领域模型以及缺乏事件来避免应用层中模块之间的循环依赖性。但是,我将发表更多有关此主题的文章。我希望这可以帮助您更好地了解Nestjs的建筑设计的实现。 frengers见!

只是提醒您可以使用代码here

检查存储库