TypeScript中Dependency inversion与clean architecture优化:组合根设计咨询
基于Clean Architecture优化依赖注入与组合根设计
核心问题分析
你当前的代码存在两个关键问题:
- 领域层接口定义混淆:
user.interface.ts和user-data.interface.ts的内容写反了,违反领域层实体定义规范 - 组合根职责不清晰:第二种方案将依赖注入逻辑放在Controller层,导致领域层依赖基础设施层,直接违反依赖倒置原则(高层模块不应依赖低层模块,二者都应依赖抽象)
符合Clean Architecture的优化方案
Clean Architecture的核心是依赖方向向内:领域层(最内层)不依赖任何外层,应用层依赖领域层,基础设施层依赖应用/领域层,组合根作为唯一的"外层向内"依赖入口,负责所有实例化与注入。
1. 修正领域层接口定义
先修正领域层的实体和数据传输对象接口,确保语义正确:
// domain/user.interface.ts // 领域实体:User,代表业务核心对象 export interface User { id: string; name: string; email: string; // 其他业务属性 }
// domain/user-data.interface.ts // 数据传输对象:UserData,用于接收外部输入的数据结构 export interface UserData { name: string; email: string; password: string; // 其他输入属性 }
2. 分离Controller的定义与实例化
Controller属于接口适配器层(Clean Architecture的外层),它应依赖应用层的用例抽象,而非自行实例化依赖。单独定义Controller类:
// adapters/controllers/user-controller.ts import { CreateUserUseCase } from '../../domain/usecase.interface'; import { UserData } from '../../domain/user-data.interface'; import { User } from '../../domain/user.interface'; export class UserController { constructor(private readonly createUserUseCase: CreateUserUseCase) {} async createUser(userData: UserData): Promise<User> { return this.createUserUseCase.execute(userData); } }
3. 优化组合根(app.ts)的职责
保留独立的app.ts作为组合根,这是符合Clean Architecture的正确做法——它是唯一允许跨层实例化对象的地方,负责串联所有依赖:
// app.ts // 组合根:唯一负责依赖注入的入口 import { UserController } from './adapters/controllers/user-controller'; import CreateUserUseCaseImpl from './domain/create-user.usecase'; import MongoDBUserRepository from './infra/persistence/mongodb-user.repository'; // 从最底层的基础设施实例化,向上注入 const userRepository = new MongoDBUserRepository(); const createUserUseCase = new CreateUserUseCaseImpl(userRepository); const userController = new UserController(createUserUseCase); export { userController };
4. 调整基础设施层的依赖引入
确保基础设施层只依赖领域层的抽象,不反向依赖外层:
// infra/persistence/mongodb-user.repository.ts import { UserRepository } from '../../domain/repository.interface'; import { User } from '../../domain/user.interface'; import { UserData } from '../../domain/user-data.interface'; export class MongoDBUserRepository implements UserRepository { async saveUser(userData: UserData): Promise<User> { // MongoDB具体实现逻辑 const savedUser = { id: 'mock-id-123', ...userData }; return savedUser; } }
5. 调整HTTP入口的依赖引入
HTTP层(基础设施层的一部分)只需从组合根获取已实例化的Controller,无需关心依赖链:
// infra/http/main.ts import express from 'express'; import { userController } from '../../app'; import { UserData } from '../../domain/user-data.interface'; const app = express(); app.use(express.json()); app.post('/users', async (req, res) => { const userData: UserData = req.body; try { const createdUser = await userController.createUser(userData); res.status(201).json(createdUser); } catch (error) { res.status(500).json({ error: 'Failed to create user.' }); } }); app.listen(3000, () => { console.log('Server running on port 3000'); });
优化后的分层依赖关系
- 领域层:仅包含业务实体、用例接口、仓库接口,不依赖任何外层
- 接口适配器层:Controller依赖领域层的用例抽象,不依赖基础设施
- 基础设施层:MongoDB仓库实现依赖领域层的仓库接口,HTTP服务依赖组合根提供的Controller
- 组合根(app.ts):唯一跨层实例化对象的地方,负责将所有依赖从外到内注入,完全符合依赖倒置原则
内容的提问来源于stack exchange,提问作者boojum
相关产品推荐
相关产品推荐

