Spring Boot 2.x實戰 - Spring Data12 - Spring Data JPA領域事件(Domain Events)


領域事件

由於在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 {}
  1. 若存在則更新雇員;

  2. 若不存在則保存新的雇員。

  3. 執行檢驗代碼:
  4. @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);
       };
    }

  5. 轉自:https://blog.csdn.net/wiselyman/article/details/106563016


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM