2016-12-26 19 views
4

Если мы следуем принципам DDD, один общий корень должен иметь только ссылки (по id) на другой совокупный корень (ы).DDD: как правильно реализовать отношения JPA/Hibernate?

Пример:

// Product Aggregate root 
class Product { 

    // References to categories Aggregate Roots (to ids) 
    Set<Long> categoryIds; 
} 

Но как это может быть достигнуто с JPA/Hibernate? В JPA, если мы хотим иметь, например, OneToMany отношение, мы определили его следующим образом:

// Product Aggregate root 
class Product { 

    // Holds category aggregate roots 
    @OneToMany(mappedBy = "", cascade = CascadeType.ALL) 
    Set<Category> categories; 
} 

Так JPA-s подход будет держать категории агрегатных корни себя, что не рекомендуется в DDD.

Как бы вы конструировали отношения с JPA, но чтобы соответствовать принципам DDD?

P.S .: Я думал создать свойство типа string и сохранить список разделенных запятыми идентификаторов категорий, но есть ли лучшее решение?

+0

'Каскад = CascadeType.ALL' движется в неправильном направлении, потому что который связывает два агрегата с одной и той же транзакцией –

ответ

1

Вы можете использовать таблицу соединения, чтобы избежать категории агрегирование корни так:

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

Просто в качестве примера я подключить несколько вместе:

for (int n = 0; n < 3; ++n) { 
    categoryRepository.save(new Category()); 
} 

Set<Category> categories = categoryRepository.findAll(); 

productRepository.save(new Product(categories)); 

что приводит к (вы не указали свою СУБД, поэтому я просто предположил ...) 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) 

И из курса не зе никакого удивления относительно их содержания:

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) 

Также я хотел бы избежать хранения отношений в список разделенных запятыми, если вы используете реляционную базу данных. Это приводит к нездоровому дизайну базы данных и вызовет у вас головные боли в какой-то момент.

+0

Но опять же, 'Product' содержит набор' Category '. Это то, чего мы должны избегать в первую очередь в соответствии с DDD. –

+0

Да, «Категория» в этом случае должна быть просто CategoryId – Teimuraz

0

Лучше всего придерживаться ссылки на личность при навигации между агрегатами. Используйте службу для загрузки необходимых объектов, прежде чем вызывать агрегатное поведение. Например:

public class MyProductApplicationService { 
    ... 
    @Transactional 
    public void loadDependentDataAndCarryOutAggregateAction(Long productId, Long categoryId) { 

     Product product = productRepository.findOne(productId); 
     Category category = categoryRepository.findOne(categoryId); 

     product.doActionThatNeedsFullCategoryAndMayModifyProduct(category); 
    } 
} 

Если это слишком громоздким, то, по крайней мере, не охватывают сделки от одного агрегата к другому:

class Product { 

    @OneToMany(mappedBy = "product") 
    Set<Category> categories; 
} 

public class Category { 

    @ManyToOne 
    @JoinColumn(name = "productid", insertable = false, updatable = false) 
    private Product product; 
} 
+0

спасибо, но в первом подходе, где вы будете хранить ссылки категорий для продукта? – Teimuraz

+0

@moreo Просто добавьте '@Column private Long productId' в' Category' –