在我参与的一个DDD
-项目中,我们正在寻找一些方便的解决方案来将entity objects
Map到domain objects
,反之亦然。
该项目的开发人员同意将领域模型与数据模型完全解耦,数据层使用JPA (Hibernate)
作为持久化技术。
我们都认为持久性是DDD中的一个 * 实现细节 *,从开发人员的Angular 来看,我们都在寻找应用程序各个方面的最合适的解决方案。
我们最担心的是,当一个aggregate
,包含一个entities
的列表,被Map到一个JPA entity
,而这个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...?
}
}
当DB
中已经存在一个ProductJpaEntity
时,我们需要更新所有的内容。在更新的情况下,ProductJpaEntity
在Hibernate PersistenceContext
中已经可用。但是,我们需要找出哪些ProductBacklogItems
被更改了。
更具体地说:
ProductBacklogItem
可能已添加到Collection
ProductBacklogItem
可能已从Collection
中删除
每个ProductBacklogItemJpaEntity
都有一个指向ProductJpaEntity
的Primary Key
。似乎检测新的或删除的ProductBacklogItems
的唯一方法是通过Primary Key
来匹配它们。但是,主键不属于域模型...
也有可能首先删除ProductJpaEntity
的所有ProductBacklogItemJpaEntity
示例(存在于DB中),刷新到DB,创建新的ProductBacklogItemJpaEntity
示例并将其保存到DB中。这将是一个糟糕的解决方案。每次保存Product
都会导致DB中出现几个delete
和insert
语句。
哪种解决方案可以解决此问题,而又不会对域和数据模型造成太多牺牲?
3条答案
按热度按时间8gsdolmq1#
您可以让JPA/Hibernate为您解决问题。
这样做的结果是:
c86crjj02#
这是Blaze-Persistence实体视图的完美用例。
我创建这个库是为了允许在JPA模型和自定义接口或抽象类定义的模型之间进行简单的Map,就像Spring Data Projections一样。其思想是您可以按照自己喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(getter)Map到实体模型。
实体视图也可以是 * 可更新 * 和/或 * 可创建 * 的,即支持刷新更改,这可用作DDD设计的基础。可更新的实体视图实施脏状态跟踪。您可以检查实际更改或刷新更改的值。
您可以将可更新的实体视图定义为抽象类,以隐藏“实现细节”,例如protected修饰符后面的主键,如下所示:
查询是将实体视图应用于查询的问题,最简单的是按id查询。
Product p = entityViewManager.find(entityManager, Product.class, id);
保存(即刷新)更改也很容易
entityViewManager.save(entityManager, product);
Spring Data 集成允许您像Spring Data 投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features,为了刷新更改,您可以在存储库中定义一个接受可更新实体视图的
save
方法insrf1ej3#
我相信你需要用不同的方式来解决这个问题。当你有一个复杂的对象图时,很难确定哪些被改变了。但是,应该有其他人(也许是一个服务)真正知道哪些被改变了。
实际上,我在您的问题中没有看到真实的的业务“Service”,也没有看到一个解决业务逻辑的类,这个才是解决这个问题的,所以。您将在您的存储库中拥有一些更具体的
removeProductBacklogItem(BacklogItemId idToRemove)
或...addProductBacklogItem(ProductId toProductId, ProductBacklogItem itemToAdd)
。这将迫使您以其他方式管理和识别更改...而服务将负责。