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

标签 java spring jpa domain-driven-design

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

@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
}

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

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 仍然可以在其他地方更改类别。例如,在 Controller 中,某人可以直接从存储库获取Product并可以更改其类别。我不想禁止这个用例。

所以我正在考虑将逻辑 addCategory(Long ProductId, Long CategoryId) 放在 Product 类本身中,这实际上是由领域驱动设计建议的。但我不知道如何做到这一点,因为我无法将 ComplextDBService 注入(inject) Product 中。一种方法是将其作为参数传递给 addCategory 方法,如 addCategory(Long ProductId, Long CategoryId, ComplextDBService complexDBService),这是一个好的做法吗? 是否有其他方法可以将自定义数据库操作逻辑放入域类中?

最佳答案

addCategory(Long productId, Long categoryId, ComplextDBService complexDBService), is this a good practice?

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

Is there some other ways to put custom database manipulation logic in a domain class?

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

领域事件来救援

您需要反转控件。产品聚合应该向外部通报其内部的事件,并且外部应该对此使用react。产品不应依赖于其他不相关的聚合/概念。

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

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

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

顺便说一句,您错过了 DDD 的许多重要概念。

  1. 聚合产品不应该包含其他聚合类别的列表(也许您的项目的这个有界上下文根本不应该使用 DDD 来实现?)
  2. 您不应将对象直接添加到聚合所拥有的列表中 p.getCategories.add(c)
  3. //getters、setters - 这些不是面向对象的......

关于java - Spring JPA,更新实体属性时的附加数据库操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48021711/

相关文章:

java - Android java+原生数据可见性

java - 没有 xml 配置的 Spring @Async

java - SAP HANA Cloud如何支持多个数据库?

java - 执行 EntityManagerFactory.createEntityManager() 每次都会返回新实例吗?

spring - 使用 Gradle 构建 Spring 源文件失败

java - 如何解决从另一个实体 (JPA) 继承的实体的 'no primary key specified'?

java - Android NDK多线程 block UI响应

java - 在映射中存储具有多个值的键

java - Jacoco 覆盖率和 Kotlin 默认参数

java - 如何处理 Spring 中已发布事件的异常