Clean Architecture与NestJS中无耦合存储多模块客户设置的实现问询
无耦合的客户多模块设置存储方案
针对你在Clean Architecture + NestJS架构下的需求,下面提供两种适配不同数据库的实现方案,核心都是避免模块耦合,同时不用大量多对多表:
一、PostgreSQL 适配方案
方案1:通用设置表 + JSONB 字段(推荐)
这是最简洁的方案,利用PostgreSQL的JSONB类型存储模块专属配置,同时通过唯一约束避免冗余:
数据库表设计:
创建customer_settings表,字段如下:CREATE TABLE customer_settings ( id SERIAL PRIMARY KEY, customer_id INT REFERENCES customers(id) NOT NULL, module VARCHAR(50) NOT NULL, -- 取值为'text-generator'/'module-b' config JSONB NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(customer_id, module) -- 确保一个客户一个模块只有一条设置 );示例数据:
customer_id module config 1 text-generator {"usage_limit": 1000} 1 module-b {"text_color": "#ff0000"} NestJS 实现要点:
- 每个模块独立定义自己的配置DTO,比如
text-generator模块创建TextGeneratorSettingsDto,包含usage_limit字段并添加验证规则; - 通用的设置服务负责从
customer_settings表读取数据,每个模块自行将config字段解析为自己的DTO,完全不需要依赖其他模块; - 利用PostgreSQL的JSONB索引,可以针对常用配置字段(比如
usage_limit)创建索引,提升查询效率。
- 每个模块独立定义自己的配置DTO,比如
方案2:基础设置表 + 模块专属表(强类型约束)
如果需要严格的字段类型校验,可拆分表结构,模块间完全解耦:
- 数据库表设计:
- 先建基础表
customer_settings_base,关联客户:CREATE TABLE customer_settings_base ( id SERIAL PRIMARY KEY, customer_id INT REFERENCES customers(id) NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); - 每个模块创建专属设置表,通过外键关联基础表:
-- text-generator模块表 CREATE TABLE text_generator_settings ( id SERIAL PRIMARY KEY, settings_base_id INT REFERENCES customer_settings_base(id) NOT NULL UNIQUE, usage_limit INT NOT NULL DEFAULT 0 ); -- module-b模块表 CREATE TABLE module_b_settings ( id SERIAL PRIMARY KEY, settings_base_id INT REFERENCES customer_settings_base(id) NOT NULL UNIQUE, text_color VARCHAR(7) NOT NULL DEFAULT '#000000' );
- 先建基础表
- NestJS 实现要点:
- 每个模块维护自己的实体和Repository,只依赖
customer_settings_base的外键,模块间无直接关联; - 查询时通过
customer_id找到基础记录,再关联对应模块的表获取配置,完全符合Clean Architecture的边界隔离要求。
- 每个模块维护自己的实体和Repository,只依赖
二、MongoDB 适配方案
MongoDB的文档模型天生适合存储嵌套配置,无需额外集合:
- 集合结构设计:
在customers集合中直接嵌套settings字段,每个模块作为独立的子对象:{ "_id": ObjectId("60d21b4667d0d8992e610c85"), "name": "某客户", "email": "customer@example.com", "settings": { "text-generator": { "usage_limit": 1000 }, "module-b": { "text_color": "#ff0000" } } } - NestJS 实现要点:
- 每个模块定义自己的配置Schema(比如
TextGeneratorSettingsSchema),通过Mongoose的嵌套Schema或类型断言处理; - 模块的服务仅读取
settings下对应自己的节点,完全不关心其他模块的配置结构,实现彻底解耦。
- 每个模块定义自己的配置Schema(比如
核心原则(避免耦合与多对多)
- 摒弃“设置项与客户多对多”的思路:你的场景是客户-模块-专属设置的一对一关系,而非多对多,所以用唯一约束的外键或嵌套结构即可;
- 模块边界隔离:每个模块只负责自己的配置定义、解析与验证,不依赖其他模块的任何代码;
- 遵循Clean Architecture:将配置的存储逻辑放在基础设施层,模块的业务逻辑只依赖抽象的配置接口,而非具体的存储实现。
内容的提问来源于stack exchange,提问作者Théo Tessilimi
相关产品推荐
相关产品推荐

