領域事件
由於在DDD中采用了“設計小聚合”的原則,因此避免了領域模型的相互關聯,從而避免了在應用演進中形成“大泥球”(Big Ball of Mud),也因為上述的原因,本書將不講解@OneToMany、@ManyToMany等關聯注解。聚合之間在沒有了關聯關系后,聚合之間的數據通訊通過領域事件來完成,領域事件是由聚合根發出的。
Spring Data對領域事件做了專門的支持,使用@DomainEvents
注解注冊領域事件或者繼承AbstractAggregateRoot
使用它的registerEvent
方法注冊事件。
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Person { @DomainEvents // 使用集合類注冊事件列表 Collection<Object> domainEvents(){ List<Object> events= new ArrayList<Object>(); events.add(new PersonSaved(this.id, this.name, this.age)); return events; } @AfterDomainEventPublication //所有事件發布完成后調用,一般用來清空事件列表 void callbackMethod() { domainEvents().clear(); } }
當Repository每一次調用save
方法時,領域事件都會被發布。
我們的領域事件定義:
import lombok.Value; @Value public class PersonSaved { private Long id; private String name; private Integer age; }
我們現在定義另外一個聚合根,為雇員(Employee
),它和Person
是一一對應的關系,但是多了公司的信息。基於設計小聚合的原則,我們沒有給他們配置@OneToOne
,而是當Person
保存成功后發布領域事件PersonSaved
,在事件監聽的位置我們在另外一個事務中新建對應的Employee
。小聚合的另外一個好處就是將事務邊界變小從而有更快的速度和更好的性能。
新的雇員聚合:
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Employee { @Id private Long id; private String name; private Integer age; @Embedded private Company company; }
@Data @AllArgsConstructor @NoArgsConstructor @Embeddable public class Company { private String name; private String city; }
聚合的Repository:
public interface EmployeeRepository extends JpaRepository<Employee, Long> { List<Employee> findByName(String name); }
領域發送的事件是Spring事件,我們可以使用@EventListener
來接受,Spring Data給我們提供了專門的事務監聽注解@TransactionalEventListener
,它組合了@EventListener
。
@Component public class DomainEventListener { private EmployeeRepository employeeRepository; public DomainEventListener(EmployeeRepository employeeRepository) { this.employeeRepository = employeeRepository; } @Async //1 @TransactionalEventListener public void handlePersonSavedEvent(PersonSaved personSaved){ Company company = new Company("某某公司", "hefei"); Optional<Employee> employeeOptional = employeeRepository.findById(personSaved.getId()); employeeOptional.ifPresent(employee -> { //2 employee.setName(personSaved.getName()); employee.setAge(personSaved.getAge()); employeeRepository.save(employee); return; }); employeeRepository.save(new Employee(personSaved.getId(), personSaved.getName(), personSaved.getAge(), company));//3 } }
使用@Async
注解讓處理在另外一個線程處理;需要在配置類啟用異步支持@EnableAsync
@SpringBootApplication @EnableAsync public class LearningSpringDataJpaApplication {}
-
若存在則更新雇員;
-
若不存在則保存新的雇員。
- 執行檢驗代碼:
-
@Bean CommandLineRunner domainEvents(PersonRepository personRepository, EmployeeRepository employeeRepository){ return args -> { Address address = new Address("nanjing","Jiangsu"); Collection<Child> children = Arrays.asList(new Child("xxxx", Gender.FEMALE)); Person savedPerson = personRepository.save(new Person("wwww", 33, address, children)); Thread.sleep(100); //監聽是在異步線程執行的,所以需等待 List<Employee> employees1 = employeeRepository.findByName("wwww"); employees1.forEach(System.out::println); savedPerson.setName("wwwww"); personRepository.save(savedPerson); Thread.sleep(100); List<Employee> employees2 = employeeRepository.findByName("wwwww"); employees2.forEach(System.out::println); }; }
- 轉自:https://blog.csdn.net/wiselyman/article/details/106563016