DDD实战 - Repository模式的妙用( 二 )


资源库的设计通常包括两个主要组成部分:定义和实现 。定义部分是一个抽象接口,它只描述了我们可以对数据执行哪些操作,而不涉及具体如何执行它们 。实现部分则是这些操作的具体实现 。它依赖于一个特定的持久化媒介,并可能需要与特定的技术进行交互 。
2.2 领域层与基础设施层根据DDD的分层架构,领域层包含所有与业务领域有关的元素,包括实体、值对象和聚合 。领域层表示业务的核心概念和逻辑 。
另一方面,基础设施层包含支持其他层的通用技术,比如数据库访问、文件系统交互等 。
资源库模式很好地适用于这种分层结构 。资源库的定义部分,即抽象接口,位于领域层,因为它直接与领域对象交互 。而资源库的实现部分则属于基础设施层,它处理具体的数据访问逻辑 。
以DailyMart系统中的CustomerUser为例

DDD实战 - Repository模式的妙用

文章插图
图片
如上图所示,CustomerUserRepository是资源库接口,位于领域层,操作的对象是CustomerUser聚合根 。CustomerUserRepositoryImpl是资源库的实现部分,位于基础设施层 。这个实现部分操作的是持久化对象,这就需要在基础设施层中有一个组件来处理领域对象与数据对象的转换,在之前的文章中已经推荐使用工具mapstruct来实现这种转换 。
2.3 小结资源库是DDD中一个强大的概念,允许我们以一种整洁和一致的方式来处理数据访问 。通过将资源库的定义放在领域层,并将其实现放在基础设施层,我们能够有效地将业务逻辑与数据访问代码解耦,从而使应用程序更加灵活和可维护 。
3. 仓储接口的设计原则当我们设计仓储接口时,目标是创造一个清晰、可维护且松耦合的结构,这样能够让应用程序更加灵活和健壮 。以下是仓储接口设计的一些原则和最佳实践:
  • 避免使用底层实现语法命名接口方法:仓储接口应该与底层数据存储实现保持解耦 。使用像insert, select, update, delete这样的词语,这些都是SQL语法,等于是将接口与数据库实现绑定 。相反,应该视仓储为一个类似集合的抽象,使用更通用的词汇,如 **find、save、remove** 。特别注意,区分insert/add 和 update 本身就是与底层实现绑定的逻辑,有时候存储方式(如缓存)并不区分这两者 。在这种情况下,使用一个中立的save接口,然后在具体的实现中根据需要调用insert或update 。
  • 使用领域对象作为参数和返回值:仓储接口位于领域层,因此它不应该暴露底层数据存储的细节 。当底层存储技术发生变化时,领域模型应保持不变 。因此,仓储接口应以领域对象,特别是聚合根(Aggregate Root)对象,作为参数和返回值 。
  • 避免过度通用化的仓储模式:虽然一些ORM框架(如Spring Data和Entity Framework)提供了高度通用的仓储接口,通过注解自动实现接口,但这种做法在简单场景下虽然方便,但通常缺乏扩展性(例如,添加自定义缓存逻辑) 。使用这种通用接口可能导致在未来的开发中遇到限制,甚至需要进行大的重构 。但请注意,避免过度通用化并不意味着不能有基本的接口或通用的辅助类 。
  • 定义清晰的事务边界:通常,事务应该在应用服务层开始和结束,而不是在仓储层 。这样可以确保事务的范围明确,并允许更好地控制事务的生命周期 。
通过遵循上述原则和最佳实践,我们可以创建一个仓储接口,不仅与底层数据存储解耦,还能支持领域模型的演变和应用程序的可维护性 。
4.  Repository的代码实现在DailyMart项目中,为了实现DDD开发的最佳实践,我们创建一个名为dailymart-ddd-spring-boot-starter的组件模块,专门存放DDD相关的核心组件 。这种做法简洁地让其他模块通过引入此公共模块来遵循DDD原则 。
DDD实战 - Repository模式的妙用

文章插图
图片
4.1 制定Marker接口类Marker接口主要为类型定义和派生类分类提供标识,通常不包含任何方法 。我们首先定义几个核心的Marker接口 。
public interface Identifiable<ID extends Identifier<?>> extends Serializable {ID getId();}public interface Identifier<T> extends Serializable {T getValue();}public interface Entity<ID extends Identifier<?>> extends Identifiable<ID> { }public interface Aggregate<ID extends Identifier<?>> extends Entity<ID> { }


推荐阅读