Spring JPA,更新实体属性时的附加数据库操作

问题描述 投票:0回答:1

我正在使用Spring JPA设计电子商务Web应用程序。我有两个类ProductCategory,其中Product可以分配到许多类别,但Category不关心Product

@Entity(name = "products")
class Product {
    @Id
    @Column(name = "product_id")
    private Long productId;

    @ManyToMany
    @JoinTable(
            name = "product_category_links",
            joinColumns = @JoinColumn(name = "product_id", referencedColumnName = "product_id"),
            inverseJoinColumns = @JoinColumn(name = "category_id", referencedColumnName = "category_id"))
    private List<Category> categories;

    // getters, setters,
}

@Entity(name = "categories")
class Category {
    @Id
    @Column(name = "category_id")
    private Long category_id;

    // getters, setters
}

Product的类别发生变化时,我还需要更新数据库中的内容。我正在考虑通过创建专用服务方法来更新产品类别来保持这种完整性。

class ProductService
    @Autowired
    private ProductRepository productRepository;
    private ComplexDBService complexDBService;

    @Transactional
    public void addCategory(Long productId, Long categoryId) {
        Product p = productRepository.findByProductId(productId);
        Category c = categoryRepository.findByCategoryId(categoryId);
        p.getCategories.add(c);
        complexDBService.doSomething();
    }
}

但我认为这是不实际的,因为Product仍然可以在其他地方改变类别。例如,在控制器中,有人可以直接从存储库获取Product可以更改其类别。我不想禁止这个用例。

所以我想把逻辑addCategory(Long productId, Long categoryId)放在Product类中,这实际上是由Domain Driven Design提出的。但我无法弄清楚如何做到这一点,因为我无法将ComplextDBService注入Product。一种方法是将它作为addCategory方法的参数传递为addCategory(Long productId, Long categoryId, ComplextDBService complexDBService),这是一个好习惯吗?是否有其他方法将自定义数据库操作逻辑放在域类中?

java spring jpa domain-driven-design
1个回答
2
投票

addCategory(Long productId,Long categoryId,ComplextDBService complexDBService),这是一个很好的做法吗?

不,不是。在复杂的业务案例中,您有时必须将某种“服务”作为参数传递给在聚合上调用的方法,但根据经验,您应该只在此“服务”上调用只读查询方法。

是否有其他方法将自定义数据库操作逻辑放在域类中?

应该只在产品聚合中发生与产品相关的事情,例如操纵产品状态。您的要求是响应Product aggregate内的事件。

域事件救援

您需要反转控件。产品总量应告知外部有关其内部的事件,外部应对此作出反应。产品不应该依赖于其他不相关的聚合/概念。

class Product {
    void addCategory(CategorySnapshot category) {
        categories.add(category);
        eventPublisher.publish(new ProductCategoryAdded(getSnapshot(), category));
    }
}

现在你应该注册其他组件来监听ProductCategoryAdded事件,那些其他组件是什么并不重要(如果你需要进行数据库操作,也许你正在实现CQRS?)。

您可以自己实现发布者或使用Guava Event Bus,Axon等框架。

顺便说一句,你缺少许多重要的DDD概念。

  1. Aggregate-Product不应该有其他Aggregate-Category的列表(可能这个有界的上下文不应该使用DDD来实现?)
  2. 您不应将对象直接添加到聚合p.getCategories.add(c)所拥有的列表中
  3. //getters, setters - 那些不是面向对象的......
© www.soinside.com 2019 - 2024. All rights reserved.