Tei*_*raz 6 domain-driven-design hibernate jpa aggregateroot
如果我们遵循 DDD 原则,一个聚合根应该只引用(通过 id)到另一个聚合根。
例子:
// Product Aggregate root
class Product {
// References to categories Aggregate Roots (to ids)
Set<Long> categoryIds;
}
Run Code Online (Sandbox Code Playgroud)
但是如何通过 JPA/Hibernate 实现呢?例如,在 jpa 中,如果我们想拥有 OneToMany 关系,我们将其定义如下:
// Product Aggregate root
class Product {
// Holds category aggregate roots
@OneToMany(mappedBy = "", cascade = CascadeType.ALL)
Set<Category> categories;
}
Run Code Online (Sandbox Code Playgroud)
因此 JPA-s 方法本身将持有类别聚合根,这在 DDD 中是不推荐的。
您将如何设计与 JPA 的关系,但要符合 DDD 原则?
PS:我想制作categories
字符串类型的属性并保存逗号分隔的类别ID列表,但有没有更好的解决方案?
这是一个好问题,但你的例子不适用。从逻辑上讲,类别不是产品聚合根的一部分。产品和类别都有全局ID。当您删除产品时,您不会删除它所属的类别,并且当您删除类别时,您不会删除它拥有的所有产品。
\n总结Eric Evans 的 DDD 书中聚合用法的页面Google 图书上免费提供了以下是关于聚合的说法:
\n\n\n\xe2\x80\xa2 根实体具有全局身份,并最终负责检查不变量。
\n\xe2\x80\xa2 根实体具有全局标识。\n边界内的实体具有本地标识,仅在 AGGREGATE 内唯一。
\n\xe2\x80\xa2 AGGREGATE 边界之外的任何内容都不能保存对内部任何内容的引用,根实体除外。根实体可以将对内部实体的引用传递给其他对象,但这些对象只能暂时使用它们,并且它们可能不保存引用。根可能会将 VALUE OBJECT 的副本传递给另一个对象,并且它发生什么并不重要,因为它只是一个 VALUE,并且不再与 AGGREGATE 有任何关联。
\n\xe2\x80\xa2 作为上一条规则的推论,只能通过数据库查询直接获取 AGGREGATE\nroots。所有其他\n对象必须通过遍历关联来找到。
\n\xe2\x80\xa2 AGGREGATE 中的对象可以保存对其他 AGGREGATE 根的引用。
\n\xe2\x80\xa2 删除操作必须删除 AGGREGATE 边界内的所有内容。(使用垃圾收集,这很容易。因为除了根之外没有任何外部引用,因此删除根并且\n其他所有内容都将被收集。)
\n\xe2\x80\xa2 当提交对 AGGREGATE 边界内的任何对象> 的更改时,必须满足整个 AGGREGATE 的所有不变量。
\n
关于 JPA 实现,我想说有多种方法可以工作:
\n@Embeddable
这似乎是一个万无一失的解决方案,因为选民没有身份证。@OneToMany, @JoinTable
等 - 只要您不通过其他实体的 ID 引用成分,该方法也有效。不过,在实施过程中需要确保这一点,并且可能会意外违反。您可以使用连接表来避免类别聚合根,如下所示:
@Entity
public class Product {
@Id
@GeneratedValue
private int id;
@OneToMany
@JoinTable
private Set<Category> categories;
// constructor, getters, setters, etc...
}
@Entity
public class Category {
@Id
@GeneratedValue
private int id;
// constructor, getters, setters, etc...
}
Run Code Online (Sandbox Code Playgroud)
作为一个例子,我将把一些放在一起:
for (int n = 0; n < 3; ++n) {
categoryRepository.save(new Category());
}
Set<Category> categories = categoryRepository.findAll();
productRepository.save(new Product(categories));
Run Code Online (Sandbox Code Playgroud)
结果如下(你没有指定你的 DBMS,所以我只是假设......)MySQL:
MariaDB [so41336455]> show tables;
+----------------------+
| Tables_in_so41336455 |
+----------------------+
| category |
| product |
| product_categories |
+----------------------+
3 rows in set (0.00 sec)
MariaDB [so41336455]> describe category; describe product; describe product_categories;
+-------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
+-------+---------+------+-----+---------+----------------+
1 row in set (0.00 sec)
+-------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
+-------+---------+------+-----+---------+----------------+
1 row in set (0.00 sec)
+---------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------+------+-----+---------+-------+
| product_id | int(11) | NO | PRI | NULL | |
| categories_id | int(11) | NO | PRI | NULL | |
+---------------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
当然,其内容也不足为奇:
MariaDB [so41336455]> select * from category; select * from product; select * from product_categories;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.00 sec)
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)
+------------+---------------+
| product_id | categories_id |
+------------+---------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+------------+---------------+
3 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
另外,当您使用关系数据库时,我会避免将关系存储在以逗号分隔的列表中。它会导致不健康的数据库设计,并在某些时候让您头疼。
归档时间: |
|
查看次数: |
3297 次 |
最近记录: |