品牌 网站建设,微博推广技巧,网站建设中倒计时模板,wordpress去除tag摘要本文主要介绍了DDD#xff08;领域驱动设计#xff09;在系统设计中的实践应用#xff0c;包括其在编码规范、分层架构设计等方面的具体要求和建议。重点强调了应用层的命名规范#xff0c;如避免使用模糊的Handler、Processor等命名#xff0c;推荐使用动词加业务动作…摘要本文主要介绍了DDD领域驱动设计在系统设计中的实践应用包括其在编码规范、分层架构设计等方面的具体要求和建议。重点强调了应用层的命名规范如避免使用模糊的Handler、Processor等命名推荐使用动词加业务动作的清晰命名方式区分命令和查询服务的命名规则以及Repository层和防腐层的设计原则。此外还探讨了DDD的价值和在实际系统中的应用思考。1. DDD领域模型驱动Coding规范1.1. 统一语言规范与统一语言英文一致的代码命名保证代码可读性和可沟通性、降低团队沟通成本和团的其他成员理解成本。1.2. Domian层1.2.1. Domain对象拒绝Getter、Setter、Constructor等注解在 DDD领域驱动设计中Domain 对象如 Entity、Value Object、Aggregate Root确实应当尽量避免使用 Getter、Setter、Constructor 等 Lombok 或 IDE 自动生成的注解这是出于“建模思想”与“封装业务规则”的考虑。以下是详细解释与建议。 破坏封装与建模思想DDD 强调 通过代码表达领域模型的意图和业务规则。直接暴露 getter/setter会让你的领域对象退化为一个贫血模型Anemic Domain Model只是一个数据容器而不是业务的承载体。
// ❌ 错误写法getter/setter public 字段完全暴露
Getter
Setter
public class Order {private Long id;private String status;
}❗这样写没有任何领域语义Order 的状态可以随意被外部修改违背领域封装原则。️ Setter 允许任意修改内部状态打破一致性领域对象的核心职责是保障业务数据的完整性和一致性。Setter 让外部可以绕过业务规则随意设置对象属性
order.setStatus(已支付); // 没有校验是否可以从“已取消”变成“已支付”而正确的做法应是
public void markAsPaid() {if (!canPay()) {throw new IllegalStateException(当前状态不可支付);}this.status Status.PAID;
}️ Constructor 注解如 AllArgsConstructor缺乏表达力AllArgsConstructor 自动生成构造函数但它不能表达“构建一个合法对象的业务意图”也难以做校验。例如构建一个 LoanApplication 时可能需要校验利率、贷款人信息、期限等而这些必须在构造过程强校验。应该这样
public LoanApplication(Applicant applicant, BigDecimal amount, Term term) {if (amount.compareTo(BigDecimal.ZERO) 0) {throw new IllegalArgumentException(贷款金额必须大于0);}this.applicant applicant;this.amount amount;this.term term;
}正确做法用行为方法代替 Setter用工厂方法代替构造器示例DDD 风格的 Order 聚合根
public class Order {private final OrderId id;private final ListOrderItem items new ArrayList();private OrderStatus status OrderStatus.CREATED;// 构造器设为 protected 或 private仅通过工厂创建protected Order(OrderId id) {this.id id;}public static Order create(OrderId id) {return new Order(id);}public void addItem(Product product, int quantity) {if (status ! OrderStatus.CREATED) {throw new IllegalStateException(只能在创建状态添加商品);}this.items.add(new OrderItem(product, quantity));}public void markAsPaid() {if (status ! OrderStatus.CREATED) {throw new IllegalStateException(不能重复支付);}this.status OrderStatus.PAID;}public OrderId getId() {return id;}public ListOrderItem getItems() {// 可返回不可变副本return Collections.unmodifiableList(items);}
}Domian对象code实践建议项目DDD 建议Getter❌ 只在读取聚合标识、只读字段时局部使用Setter❌ 禁止在领域对象中使用AllArgsConstructor❌ 不建议使用NoArgsConstructor❌ 避免除非 ORM 必须构造函数✅ 应包含业务校验逻辑Builder 模式✅ 可用于构造复杂值对象1.2.2. Domain仅包含领域模型定义的对象且用plain object。Domain层主要包含领域模型Domain Model比如实体Entity有唯一标识的业务对象如“订单”、“用户”。值对象Value Object无唯一标识仅通过属性值定义的对象如“地址”、“金额”。聚合根Aggregate Root实体的集合边界保证数据一致性。领域服务Domain Service当业务逻辑不适合放在某个实体上时用领域服务封装。领域事件Domain Event业务状态变化的事件。不包含技术层相关的类比如 DAO、DTO、Controller、ServiceImpl等。Domain对象都用plain objectPlain Object 指的是简单的、纯粹的业务对象即不依赖特定框架的特殊基类、注解或技术代码。这意味着领域模型类尽量只包含业务属性和行为不引入持久化、网络、序列化等技术代码。例如领域模型不要直接继承 JPA Entity或带大量数据库注解避免和框架耦合。保持领域模型的纯粹性方便单元测试和业务复用。
// 领域实体示例Plain Object
public class Order {private String orderId;private ListOrderItem items;public void addItem(OrderItem item) {// 业务规则校验items.add(item);}// 省略Getter/Setter聚焦行为
}这里的 Order 是一个“纯领域对象”没有任何持久化注解也没有依赖框架特性。在实际项目中领域对象和数据库映射对象通常会做分离通过Repository 层进行转换。这样既保持了领域层的纯粹也满足了持久化需求。1.2.3. Domain层不依赖spring的AOP和lOC等三方包Domain层应该保持“纯净”不依赖 Spring、MyBatis、Hibernate、Lombok 等任何三方框架尤其不能依赖 Spring 的 IoC、AOP 等容器机制。DDD 要求领域模型表达业务含义清晰面向业务而非技术可以脱离框架独立测试或演算保持“领域独立性”保持长生命周期可演进与基础框架解耦所以Component、Autowired、Transactional、Service 等 Spring 注解 → ❌ 不应该出现在 Domain 层Getter、Setter、Entity、Table 等 Lombok / ORM 注解 → ❌ 不应该污染领域模型如果你的 Domain层用了一堆Spring注解要小心说明你的领域模型很可能耦合了基础设施层逻辑违背 DDD 分层可能导致业务逻辑难以复用或测试层说明是否可用框架注解domain纯领域逻辑模型、实体、聚合、值对象、领域服务❌ 不依赖任何框架infrastructure数据持久化、消息中间件、缓存等实现细节✅ 用 Spring 管理application调用编排、流程协调、事务管理等✅ 用 SpringinterfacesController、API 接口层✅ 用 Spring MVC 注解等1.2.3.1. ❌ 不推荐污染领域模型
Entity
Getter
Setter
public class LoanApplication {Idprivate Long id;Autowiredprivate CreditService creditService; // 依赖外部服务public boolean canApprove() {return creditService.checkCredit(id); // 不可测、强耦合}
}1.2.3.2. ✅ 推荐纯净的领域对象
public class LoanApplication {private Long id;private int score;public LoanApplication(Long id, int score) {this.id id;this.score score;}public boolean canApprove() {return score 700;}
}外部服务如 CreditService由 DomainService 或 ApplicationService 在外部注入传参给领域对象不在对象中注入依赖。1.2.3.3. 好处✅ 易于单元测试构造纯对象即可测试不依赖 Spring 环境。✅ 解耦框架更容易迁移、更少“技术污染”。✅ 聚焦业务领域对象只关心业务含义职责清晰。1.2.4. Domain对象行为拒绝setter、update、modify、save、delete等无明确业务含义的方法这是 DDD领域驱动设计中对 Domain 对象即领域模型 的一种强烈编码规范领域对象的方法必须具备明确的业务含义。这些方法通常只表示技术操作而没有任何具体的业务语义违背了 DDD 中“领域模型体现业务行为”的基本理念。错误方式技术性方法正确方式业务性方法user.setStatus(DISABLED)user.disable()loan.updateStatus(APPROVED)loan.approve()order.delete()order.cancel()account.save()account.deposit(amount)/ account.withdraw(amount)1.2.4.1. 领域模型是业务专家的语言映射setter/update/save 等是 面向 ORM 和数据库 的语言。而approve()、reject()、cancel() 等方法是 业务专家能听懂的术语符合“统一语言”的要求。1.2.4.2. 降低贫血模型Anemic Model风险如果实体只有 getter/setter就沦为了数据容器贫血模型逻辑散落在 service 中失去了封装。加入业务行为才能形成真正的充血模型逻辑内聚模型可维护、可扩展。1.2.4.3. 那领域模型中应该怎么定义方法按照“行为驱动模型”的思路
public class LoanApplication {private LoanStatus status;public void approve() {if (this.status ! LoanStatus.PENDING) {throw new IllegalStateException(Loan cannot be approved in current state);}this.status LoanStatus.APPROVED;}public void reject(String reason) {this.status LoanStatus.REJECTED;// 记录拒绝原因可能还要写审计日志}
}方法名是否允许说明setXxx()❌除非是纯值对象如 DTO、配置类updateXxx()❌太笼统建议改为具体业务操作approve()、cancel()✅有明确业务语义validate()、calculate()✅计算、验证行为是业务的一部分toDTO()、toSnapshot()✅只读转换可接受但可以考虑放到 assembler 或 factory1.2.4.4. ✅ Domain对象行为总结编码项DDD 推荐是否写 setter❌ 尽量避免方法是否用 update/save/delete 命名❌ 避免方法是否要有业务含义✅ 强烈建议领域对象职责封装业务状态 表达业务行为对象类型推荐值对象不可变、实体对象充血1.2.5. 值对象命名不用加上标识技术语言的Enum。在很多项目中我们习惯于写
public enum LoanStatusEnum {PENDING, APPROVED, REJECTED;
}但在 DDD 中更推荐写为
public enum LoanStatus {PENDING, APPROVED, REJECTED;
}1.2.5.1. 领域语言优先表达业务语义而非技术语义DDD 强调“领域语言”即代码的命名要贴合业务、可读性强而不是暴露技术细节。LoanStatus是一个业务概念用户、产品经理、风控人员都能理解。LoanStatusEnum 是面向程序员的命名方式暴露了技术实现细节用的是 enum。 DDD 建议隐藏实现细节、突出业务意图。值对象本身就是“一个不可变、具备自我完整性的业务值”不管你是用 enum、class、record 实现的业务只需要知道这是 LoanStatus而不是“枚举”。1.2.5.2. 值对象不仅限于 enum很多值对象是用 class 定义的
public class Amount {private BigDecimal value;private String currency;
}如果你对 enum 加上 Enum 后缀那你是不是也要对上面的 class 叫 AmountClass显然没有这个习惯。所以统一叫“LoanStatus”这种业务术语风格更一致、更干净。1.2.5.3. ❌ 什么情况下不推荐简化命名以下场景你可能仍然需要保留Enum后缀但这不再是纯粹 DDD 语境了与其他类型冲突如 LoanStatus既是实体字段又是类名时。和第三方库集成时需要区分类型。较低层的工具包非 DDD用于统一标识枚举。维度建议命名方式推荐使用业务语言命名不加 Enum 后缀示例LoanStatus 而非 LoanStatusEnum原因保持领域语言一致性、隐藏实现细节、业务表达自然例外与类型冲突、集成第三方工具时可保留 Enum1.3. application层1.3.1. application层拒绝XXXHandler、XXXProcessor、XXXContext等含义不明确的命名1.3.1.1. ❌ 为什么要避免这种模糊命名命名问题OrderHandler“处理订单”具体是创建、取消、派送、结算还是别的看不出来LoanProcessor“处理贷款”是审批风控放款也看不出来UserContext是用户上下文对象Session请求参数环境变量不清晰XXXManager管理什么责任不清这些命名是技术导向的不利于业务沟通和代码可维护性。1.3.1.2. ✅ 推荐的命名方式动词 业务动作或职责清晰的命名命名应能直接反映业务操作的意图建议使用如下格式建议命名职责SubmitLoanApplicationService提交贷款申请CancelOrderService取消订单ApproveLoanApplicationUseCase审批贷款申请TransferFundsApplicationService发起资金转账GenerateReportService生成报表这些命名都表达了 清晰的业务行为更符合 DDD 中“应用层协调领域服务”的职责。1.3.1.3. ✅ 如何重构原名重命名建议LoanProcessorLoanApprovalService/ SubmitLoanServiceOrderHandlerCreateOrderService/ CancelOrderUseCaseUserContextCurrentUserInfo/ LoggedInUserInfo1.3.1.4. 补充说明不同于领域层、基础设施层层级命名推荐不推荐Domain 层LoanApplication/Order/UserLoanEntity / OrderDTOApplication 层ApproveLoanApplicationService/CancelOrderUseCaseLoanHandler / OrderProcessorInfrastructure 层KafkaMessageConsumer/HttpClientAdapter可容忍 Handler、Processor命名命名类别推荐所在层说明XXXHandler✅ Infrastructure 层 或 Application 层中实现类用于技术处理如消息消费、HTTP 请求处理等XXXProcessor✅ Infrastructure 层 或 Application 层中实现类用于组合多个行为、任务编排XXXContext✅ 可用于跨调用传递上下文对象如流程上下文但不作为核心业务对象放在 Application 层或跨层共享模块1.3.1.5. ✅ 总结命名原则说明❌ 避免 XXXHandler / XXXProcessor / XXXContext业务语义不明确✅ 使用 动作对象用途符合统一语言✅ 命名体现职责和行为方便业务沟通、代码自解释❌ 不建议应用层泛化职责如一个类什么都管导致职责混乱、难以维护1.3.2. 区分命令和查询命令推荐KXXCommandService,查询推荐XXXQueryService你提到的这个命名方式和区分 命令Command 与 查询Query 的设计是现代 DDD领域驱动设计中非常推荐的一种 CQRSCommand Query Responsibility Segregation命令查询职责分离 实践。1.3.2.1. ✅ 命名规则推荐类型命名规范示例说明命令类XXXCommandService 或 XXXCommandAppService代表状态变更操作有副作用查询类XXXQueryService 或 XXXQueryAppService代表数据读取操作无副作用1.3.2.2. 为什么这么命名CQRS 的核心思想是将“读操作”和“写操作”分离成两个服务接口职责清晰便于演进、扩展和性能优化。1.3.2.3. XXXCommandService只包含“写”操作新增、更新、删除、触发业务行为等会调用 领域服务 / 聚合根会有事务控制会影响系统状态
public interface UserCommandService {void registerUser(RegisterUserCommand command);void updateUser(UpdateUserCommand command);void disableUser(String userId);
}1.3.2.4. XXXQueryService只包含“读”操作查询详情、列表、分页等不包含任何副作用可返回 DTO/VO可对接读库或搜索引擎缓存等
public interface UserQueryService {UserDetailDTO getUserById(String userId);ListUserDTO listUsers(UserQuery query);
}1.3.2.5. ✅ 优点总结优点说明职责清晰读写逻辑分离不会混淆可单独优化查询可以走缓存、ES、分库命令可以做幂等性、事务保障更易测试Query 无副作用Command 只测试状态变更支持复杂业务扩展比如后续支持 Event Sourcing、审计日志、写扩展性等1.3.2.6. 反面示例混用
public class UserService {public void createUser(...) {...} // 写public User getUserById(...) {...} // 读
}这种 Service 混合读写职责后续很容易导致复杂度上升、耦合增加不易演进。1.3.2.7. 实战建议应用服务层Application Service就应该按照 Command / Query 分开设计Controller 层调用时清晰地知道是读请求还是写请求命名约定保持一致
UserCommandService / UserCommandAppService
UserQueryService / UserQueryAppService不需要为了“统一”而把 Command/Query 合并回一个 Service1.4. infrastructure层1.4.1. Repositoryl的入参和出参除了原始改据类型只能包含领域对象Repository 的职责是访问“领域模型”的持久化存储其输入输出应围绕“领域对象”展开而不是直接处理 DTO数据传输对象或 PO数据库实体对象。内容说明✅ 只能包含领域对象Domain ObjectRepository 是领域层的一部分它的作用是将领域对象保存/加载到持久化介质中所以它操作的对象应该是领域对象如实体、值对象✅ 避免 POPersistence ObjectPO 是数据库结构的映射属于基础设施infrastructure层而 Repository 是领域层的一部分它不应直接操作数据库结构的对象✅ 避免 DTOData Transfer ObjectDTO 是服务层或接口层的数据格式通常用于与外部系统或前端交互不属于领域模型因此不能作为 Repository 的输入输出1.4.1.1. ❌ 错误理解示例违反规范
// 错误传入和返回的是 DTO 或 PO而不是领域对象
UserDTO findById(Long id);
void save(UserPO userPo);1.4.1.2. ✅ 正确设计示例
// 正确传入和返回的都是领域对象Entity 或 ValueObject
User findById(UserId id); // 返回领域实体
void save(User user); // 传入领域实体1.4.1.3. 为什么这样设计原因说明分层清晰明确职责边界Repository 专注于领域模型的持久化DTO/PO 属于别的层降低耦合避免领域模型对数据库结构或外部接口耦合增强模型稳定性和可演进性保持统一语言领域对象使用的是统一语言建模符合业务语义PO/DTO 通常是技术导向结构1.4.2. Repository对外交互拒绝DTO、PO“Repository 对外交互拒绝 DTO、PO”可以从 架构职责分层、解耦性、建模一致性 等多个角度来理解。概念说明Repository是 DDD 中领域层的一部分负责对领域对象Entity、Value Object的持久化操作如存储、加载DTOData Transfer Object用于服务层、应用层与外部系统如接口调用、RPC、Web之间的数据传输对象不包含业务逻辑POPersistence Object通常是 ORM 框架如 JPA、MyBatis映射的数据库实体紧耦合于数据库结构1.4.2.1. ❌ 错误设计违反规范
// 错误直接传 PO、DTO
public interface UserRepository {void save(UserPO userPo); // 错使用 POUserDTO findById(Long id); // 错返回 DTO
}1.4.2.2. ✅ 正确设计遵守规范
public interface UserRepository {void save(User user); // 入参是领域对象User findById(UserId id); // 返回领域对象
}1.4.2.3. 为什么要拒绝 DTO 和 PO原因说明✅ 职责单一Repository 是领域层的一部分职责是“存取领域模型”不是处理数据库结构或 API 数据格式。✅ 分层解耦DTO 是接口层/应用层对象PO 是基础设施层对象而 Repository 是领域层对象 —— 应层层隔离不应交叉✅ 保持建模一致性领域对象才具备业务语义DTO 和 PO 都只是结构化数据不具备行为和语义✅ 便于演进若数据库字段或接口结构变化只需修改 PO/DTO不影响领域模型与 Repository 交互逻辑1.4.2.4. 那 Repository 和数据库是怎么交互的通过“转换器Assembler/Converter”在基础设施层完成对象转换---------------------| Domain Repository | ← 输入输出User领域对象---------------------↑-------------|------------------| Infrastructure 层 || UserRepositoryImpl.java || UserPO ↔ User 转换器 |-------------------------------↓-----------------| 数据库PO |-----------------示例
Override
public void save(User user) {
UserPO userPO UserPOAssembler.toPO(user);
userMapper.insert(userPO);
}1.4.3. 对外接口访问的防腐层统一命名为XXXAdaptor对外接口访问的防腐层统一命名为 XXXAdaptor。1.4.3.1. 什么是“防腐层”Anti-Corruption LayerACL在DDD中防腐层的作用是保护领域模型不被外部系统污染或侵蚀实现外部系统模型 → 自己系统领域模型的隔离和转换防止外部系统设计不佳、耦合度高、变化频繁影响你的系统 举个例子你需要调用第三方风控系统它返回的接口数据结构是ThirdPartyRiskResponse但你不希望这个结构在你的领域模型里出现。这时你应该定义一个RiskEngineAdaptor接口/实现将外部数据结构 ThirdPartyRiskResponse 转换为你自己的领域模型 RiskResult1.4.3.2. 为什么命名为 XXXAdaptor统一命名为 XXXAdaptor或 Adapter是为了一眼识别出它是 适配外部系统的类它的作用是“适配 转换 解耦 防腐”XXX 是被适配的系统名如 RiskEngineAdaptor, CreditPlatformAdaptor, OpenApiAdaptor1.4.3.3. Adaptor和领域的边界关系------------------------| 你的领域模型 || (干净、高内聚) |------------------------↑| ← 防腐转换Adaptor↓------------------------| 外部系统如三方接口 || 数据格式不一致模型低质 |------------------------1.4.3.4. 示例说明外部系统返回结构
Data
public class ThirdPartyRiskResponse {private String code;private String message;private MapString, String data;
}Adaptor 接口定义
public interface RiskEngineAdaptor {RiskResult query(RiskRequest request);
}Adaptor 实现类防腐层
Component
public class RiskEngineAdaptorImpl implements RiskEngineAdaptor {Overridepublic RiskResult query(RiskRequest request) {ThirdPartyRiskResponse response thirdPartyClient.call(request);return RiskResultAssembler.toDomain(response);}
}转换器Assembler
public class RiskResultAssembler {public static RiskResult toDomain(ThirdPartyRiskResponse response) {// 适配字段、格式、含义return new RiskResult(response.getCode(), response.getData().get(score));}
}1.4.3.5. 如果没有防腐层会怎样如果直接在 Service 中使用 ThirdPartyRiskResponse你的领域模型、服务层会大量出现外部结构 → 强耦合外部系统改了字段你系统大范围受影响业务含义模糊代码可读性差不利于测试、演进、重构Adaptor 就是你的系统与外部世界之间的“防护墙”统一命名为 XXXAdaptor 是为了职责清晰、结构分明、易于管理和维护。1.4.4. 禁止外部接口对象向上层透传“禁止外部接口对象向上层透传”的核心目的是不让外部结构入侵系统内部保持业务领域的纯洁性和独立性。外部接口返回的对象如三方 API、RPC、数据库 PO、Web 请求参数 DTO 等不能直接透传到系统内部尤其是不能传入领域层或直接暴露给上层。1.4.4.1. 透传的反例错误示范假设你调用一个外部授信平台它返回一个 CreditResponseDTO你直接在服务层或控制器里透传这个对象
// ❌ 错误做法把外部系统返回对象直接透传到上层接口
public CreditResponseDTO checkCredit(String userId) {return creditPlatformClient.query(userId);
}问题CreditResponseDTO 是外部定义的结构字段命名、含义不一定稳定一旦外部结构发生变动你的整个服务层、接口层都需要改你的业务逻辑会被迫使用外部系统的定义严重耦合1.4.4.2. ✅ 正确做法引入 转换层Assembler 和 防腐层Adaptor
// 对外暴露领域对象或自定义 VO而非外部结构
public CreditResult checkCredit(String userId) {CreditResponseDTO responseDTO creditPlatformClient.query(userId);return CreditAssembler.toCreditResult(responseDTO); // 转换为内部对象
}CreditResponseDTO 只在 Adaptor 或 Assembler 层使用CreditResult 是你自己定义的领域对象或 VO用于业务逻辑或接口输出这样无论外部系统怎么变只需改 Adapter/Assembler不影响核心业务1.4.4.3. 目的总结原则说明防腐外部系统不稳定不可信要设“隔离层”防污染解耦内部系统演化应与外部系统解耦可维护变化控制在边界便于测试和演进语义清晰自定义对象语义明确更符合业务语言1.4.4.4. 实战建议类型示例是否允许透传正确做法第三方接口返回对象AliyunRiskResponse❌ 禁止透传转换为 RiskResult数据库查询的 POUserPO❌ 禁止透传转换为 UserEntity前端提交的请求体 DTOUserRegisterDTO❌ 禁止透传转换为 RegisterCommand领域模型UserEntity✅ 允许传递按照聚合设计使用1.5. 事件层1.5.1. 事件命名为事件Event,且事件命名为动词过去时 1.5.1.1. 为什么事件命名要加Event后缀明确类型Event 后缀能清晰表示这是一个“事件对象”区别于命令Command、DTO、实体Entity等。增强可读性看到类名带 Event一目了然该对象是用于描述某个事件发生。方便维护在代码库中快速定位事件相关代码便于事件管理和监控。示例
UserRegisteredEvent
OrderCancelledEvent
PaymentSucceededEvent1.5.1.2. 为什么事件名用动词过去式表示已发生的事实事件描述的是“某件事已经发生了”所以用过去时更符合语义。符合事件驱动语义事件是对“发生事实”的记录或通知而不是命令或请求。区分命令和事件命令Command通常用动词原形或祈使句如CreateOrderCommand事件Event用动词过去式表明动作已完成如OrderCreatedEvent1.5.1.3. 结合起来的示例类型命名示例语义说明命令CreateOrderCommand请求创建订单动作指令事件OrderCreatedEvent订单已被创建已发生的事实事件PaymentSucceededEvent支付成功事件动作完成的结果1.5.1.4. 总结规范点理由事件名后缀 Event明确事件类型方便区分和维护动词过去式命名事件是“已经发生的事实”语义准确2. DDD领域驱动实战思考2.1. 你对DDD的理解是什么 DDD怎么样应用在实践系统中怎么发挥DDD的价值2.2. 什么项目场景使用DDD 为什么使用DDD现有技术框架满足不了吗能不能不使用DDD 使用DDD 之后带来的好处和弊端是什么2.3. 实践过程中DDD遇到问题、怎么解决这些问题的博文参考