DDD 中的通用存储库:如何使此接口通用?
Generic Repository within DDD: How can I make this interface generic?
我正在按照领域驱动设计原则开发多模块 CMS 应用程序。我正在尝试弄清楚如何实现通用存储库,从而避免大量样板代码。
想法是在持久性模块中实现“双向”映射策略(模型到实体,反之亦然)和通用存储库。此外,Domain 模块中的接口将充当 Domain 和 Persistence 之间的契约,因此我可以将其用于以后在其他层中的注入。
如何使这个界面通用?
具体来说,这里的问题是映射。由于我使用的是“双向”映射策略,域模块不知道数据库特定实体。
有没有办法在层之间映射通用类型模型?或者在保持层松散耦合的同时使用其他映射策略?
这是一个代码示例,用于阐明我要实现的目标。
这将是通用存储库的代码示例:
@MappedSuperclass
public abstract class AbstractJpaMappedType {
…
String attribute
}
@Entity
public class ConcreteJpaType extends AbstractJpaMappedType { … }
@NoRepositoryBean
public interface JpaMappedTypeRepository<T extends AbstractJpaMappedType>
extends Repository<T, Long> {
@Query("select t from #{#entityName} t where t.attribute = ?1")
List<T> findAllByAttribute(String attribute);
}
public interface ConcreteRepository
extends JpaMappedTypeRepository<ConcreteType> { … }
此外,我想创建自己的自定义存储库,以便能够将模型映射到实体,反之亦然,因此我的域中不会有 JPA 特定注释 classes,因此使其松散耦合。我希望这个自定义存储库实现域模块的接口,允许我稍后在服务层中注入它。
public class CustomRepositoryImpl implements CustomRepository {
public final JpaMappedTypeRepository<T> repository;
...
}
我怎样才能使这个 class 和这个接口通用,这样我就可以在模型和实体之间进行映射,因为域层没有关于实体 classes 的信息?
我终于想通了。
正如问题中所述,问题是层之间的映射。我创建了一个映射接口来声明映射方法。我使用 MapStruct 中的 @ObjectFactory
注释来处理通用映射 (look here):
public interface EntityMapper<M, E> {
M toModel(E entity);
List<M> toModelList(List<E> entities);
E toEntity(M model);
List<E> toEntityList(List<M> models);
// default object factory methods
}
然后我继续为每个子 classes 创建一个映射器,并使用 EntityMapper 接口扩展它,其中包含我想要映射的具体类型。
@Mapper(componentModel="spring")
public interface ConcreteEntityMapper extends EntityMapper<ConcreteModelType, ConcreteJpaType> {
}
我创建了一个摘要 class,我在其中注入了 JPA 存储库和映射器,还实现了常用方法。
abstract class CustomRepositoryImpl<T extends AbstractModelMappedType, E extends AbstractJpaMappedType> {
private final JpaMappedTypeRepository<E> repository;
private final EntityMapper<M, E> mapper;
//... common methods for mapping and querying repositories.
}
然后我用这个抽象扩展了一个 ConcreteTypeRepositoryImpl class,并实现了一个通用接口,我以后可以在其他层中作为参考。
public interface CustomRepository<M> {
M saveOrUpdate(M model);
Optional<M> getById(Long id);
List<M> getByName(String name);
List<M> getAll();
void delete(Long id);
}
@Component
public class ConcreteTypeRepositoryImpl extends CustomRepositoryImpl<ConcreteModelType,ConcreteJpaType> implements CustomRepository<ConcreteModelType> {
public ConcreteTypeRepositoryImpl(JpaMappedTypeRepository<ConcreteJpaType> repository,
EntityMapper<ConcreteModelType, ConcreteJpaType> mapper) {
super(repository, mapper);
}
}
就是这样。现在我可以将 CustomRepository 注入其他层并点击所需的存储库。
我正在按照领域驱动设计原则开发多模块 CMS 应用程序。我正在尝试弄清楚如何实现通用存储库,从而避免大量样板代码。
想法是在持久性模块中实现“双向”映射策略(模型到实体,反之亦然)和通用存储库。此外,Domain 模块中的接口将充当 Domain 和 Persistence 之间的契约,因此我可以将其用于以后在其他层中的注入。
如何使这个界面通用?
具体来说,这里的问题是映射。由于我使用的是“双向”映射策略,域模块不知道数据库特定实体。
有没有办法在层之间映射通用类型模型?或者在保持层松散耦合的同时使用其他映射策略?
这是一个代码示例,用于阐明我要实现的目标。 这将是通用存储库的代码示例:
@MappedSuperclass
public abstract class AbstractJpaMappedType {
…
String attribute
}
@Entity
public class ConcreteJpaType extends AbstractJpaMappedType { … }
@NoRepositoryBean
public interface JpaMappedTypeRepository<T extends AbstractJpaMappedType>
extends Repository<T, Long> {
@Query("select t from #{#entityName} t where t.attribute = ?1")
List<T> findAllByAttribute(String attribute);
}
public interface ConcreteRepository
extends JpaMappedTypeRepository<ConcreteType> { … }
此外,我想创建自己的自定义存储库,以便能够将模型映射到实体,反之亦然,因此我的域中不会有 JPA 特定注释 classes,因此使其松散耦合。我希望这个自定义存储库实现域模块的接口,允许我稍后在服务层中注入它。
public class CustomRepositoryImpl implements CustomRepository {
public final JpaMappedTypeRepository<T> repository;
...
}
我怎样才能使这个 class 和这个接口通用,这样我就可以在模型和实体之间进行映射,因为域层没有关于实体 classes 的信息?
我终于想通了。
正如问题中所述,问题是层之间的映射。我创建了一个映射接口来声明映射方法。我使用 MapStruct 中的 @ObjectFactory
注释来处理通用映射 (look here):
public interface EntityMapper<M, E> {
M toModel(E entity);
List<M> toModelList(List<E> entities);
E toEntity(M model);
List<E> toEntityList(List<M> models);
// default object factory methods
}
然后我继续为每个子 classes 创建一个映射器,并使用 EntityMapper 接口扩展它,其中包含我想要映射的具体类型。
@Mapper(componentModel="spring")
public interface ConcreteEntityMapper extends EntityMapper<ConcreteModelType, ConcreteJpaType> {
}
我创建了一个摘要 class,我在其中注入了 JPA 存储库和映射器,还实现了常用方法。
abstract class CustomRepositoryImpl<T extends AbstractModelMappedType, E extends AbstractJpaMappedType> {
private final JpaMappedTypeRepository<E> repository;
private final EntityMapper<M, E> mapper;
//... common methods for mapping and querying repositories.
}
然后我用这个抽象扩展了一个 ConcreteTypeRepositoryImpl class,并实现了一个通用接口,我以后可以在其他层中作为参考。
public interface CustomRepository<M> {
M saveOrUpdate(M model);
Optional<M> getById(Long id);
List<M> getByName(String name);
List<M> getAll();
void delete(Long id);
}
@Component
public class ConcreteTypeRepositoryImpl extends CustomRepositoryImpl<ConcreteModelType,ConcreteJpaType> implements CustomRepository<ConcreteModelType> {
public ConcreteTypeRepositoryImpl(JpaMappedTypeRepository<ConcreteJpaType> repository,
EntityMapper<ConcreteModelType, ConcreteJpaType> mapper) {
super(repository, mapper);
}
}
就是这样。现在我可以将 CustomRepository 注入其他层并点击所需的存储库。