在我参与的DDD
项目中,我们正在寻找一些方便的解决方案,以将entity objects
映射到domain objects
,反之亦然。
[该项目的开发人员同意将域模型与数据模型完全脱钩。数据层使用JPA (Hibernate)
作为持久性技术。
我们都认为持久性是DDD中的实现细节,从开发人员的角度来看,我们都在寻求应用程序各个方面的最合适解决方案。
[我们最担心的是将包含aggregate
列表的entities
映射到依次包含JPA entity
关系的one-to-many
上。
看下面的例子:
public class Product extends Aggregate {
private ProductId productId;
private Set<ProductBacklogItem> backlogItems;
// constructor & methods omitted for brevity
}
public class ProductBacklogItem extends DomainEntity {
private BacklogItemId backlogItemId;
private int ordering;
private ProductId productId;
// constructor & methods omitted for brevity
}
public class ProductJpaEntity {
private String productId;
@OneToMany
private Set<ProductBacklogItemJpaEntity> backlogItems;
// constructor & methods omitted for brevity
}
public class ProductBacklogItemJpaEntity {
private String backlogItemId;
private int ordering;
private String productId;
// constructor & methods omitted for brevity
}
public interface ProductRepository {
Product findBy(ProductId productId);
void save(Product product);
}
class ProductJpaRepository implements ProductRepository {
@Override
public Product findBy(ProductId productId) {
ProductJpaEntity entity = // lookup entity by productId
ProductBacklogItemJpaEntity backlogItemEntities = entity.getBacklogItemEntities();
Set<ProductBacklogItem> backlogItems = toBackLogItems(backlogItemEntities);
return new Product(new ProductId(entity.getProductId()), backlogItems);
}
@Override
public void save(Product product) {
ProductJpaEntity entity = // lookup entity by productId
if (entity == null) {
// map Product and ProductBacklogItems to their corresponding entities and save
return;
}
Set<ProductBacklogItem> backlogItems = product.getProductBacklogItems();
// how do we know which backlogItems are: new, deleted or adapted...?
}
}
ProductJpaEntity
中已经存在DB
时,我们需要更新所有内容。如果有更新,则ProductJpaEntity
在Hibernate PersistenceContext
中已经可用。但是,我们需要确定更改了哪个ProductBacklogItems
。
更具体地说:
ProductBacklogItem
本可以添加到Collection
ProductBacklogItem
本可以从Collection
中删除每个ProductBacklogItemJpaEntity
都有一个指向Primary Key
的ProductJpaEntity
。似乎检测到新的或已删除的ProductBacklogItems
的唯一方法是按Primary Key
进行匹配。但是,主键不属于域模型...
也有可能首先删除ProductBacklogItemJpaEntity
的所有ProductJpaEntity
实例(数据库中存在),刷新到DB,创建新的ProductBacklogItemJpaEntity
实例并将其保存到DB。这将是一个糟糕的解决方案。 Product
的每次保存都会在数据库中导致多个delete
和insert
语句。
存在哪些解决方案可以解决此问题,而又不会在Domain&Data模型上做出太多牺牲?
这是Blaze-Persistence Entity Views的完美用例。
我创建了该库,以允许在JPA模型与自定义接口或抽象类定义的模型之间轻松进行映射,例如类固醇上的Spring Data Projections。想法是,您可以按自己喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(获取器)映射到实体模型。
实体视图也可以是可更新和/或可创建的”,即支持回冲更改,可以将其用作DDD设计的基础。可更新的实体视图实现脏状态跟踪。您可以内省实际的更改或刷新更改的值。
您可以将可更新的实体视图定义为抽象类,以隐藏“具体实施方式”,例如受保护修饰符后面的主键是这样的:
@@ UpdatableEntityView@EntityView(ProductJpaEntity.class)公共抽象类产品扩展了聚合{@IdMapping受保护的抽象ProductId getProductId();公共抽象集getBacklogItems();}@UpdatableEntityView@EntityView(ProductBacklogItemJpaEntity.class)公共抽象类ProductBacklogItem扩展DomainEntity {@IdMapping受保护的抽象BacklogItemId getBacklogItemId();受保护的抽象ProductId getProductId();公共抽象int getOrdering();}
查询是将实体视图应用于查询的问题,最简单的方法就是按id查询。
Product p = entityViewManager.find(entityManager, Product.class, id);
保存也就是冲洗更改也很容易
entityViewManager.save(entityManager, product);
Spring Data集成使您几乎可以像Spring Data Projections:https://persistence.blazebit.com/documentation/1.4/entity-view/manual/en_US/#spring-data-features一样使用它;对于刷新更改,您可以在存储库中定义一个save
方法以接受可更新的实体视图