DDD/HexArch提示與技巧:使用ComponentScan將領域繫結到Spring上下文 - beyondxscratch

banq發表於2019-07-29

六邊形架構告訴我們,領域內不應該存在任何框架,以避免技術意外的複雜性,並且無需重新開發業務邏輯部分即可輕鬆遷移到新的結構框架(或主要版本)。這意味著當您使用Spring時,您不能依賴任何構造型註釋,例如域內的@Service@Component
因此,我們通常在Spring Configurations中使用bean工廠方法,並使用大量樣板程式碼來例項化域服務,儲存庫和存根,因為我們認為我們不能將ComponentScan  用於領域物件。

@Configuration
public class DomainConfiguration {

    @Bean
    public RetrieveSanitaryGrades retrieveSanitaryGrades() {
        return new RetrieveSanitaryGrades();
    }
    
    @Bean
    public SearchRestaurants restaurantsFinder(Restaurants restaurants, 
                                               OpeningHours openingHours, 
                                               RetrieveSanitaryGrades retrieveSanitaryGrades) {
        return new RestaurantsFinder(restaurants, openingHours, retrieveSanitaryGrades);
    }
    
}

本文將向您展示如何在不違反六邊形架構規則的情況下利用元件掃描與Koltin中的程式碼示例。如果您正在使用Java,請不要擔心,程式碼將大致相同,您將不得不調整一點語法。

Spring元件掃描查詢帶有Spring構造型的帶註釋類,以標識要在ApplicationContext中註冊的物件。因此,這些物件將不可避免地依賴於Spring Framework,這基本上是領域實現的一個問題。。為了擺脫這種限制,我們將使用六邊形體系結構使用的技巧來確保您的域永遠不會依賴於持久層,這就是:依賴性反轉。
訣竅很簡單,在域內建立描述性註釋,以識別要向ApplicationContext公開的物件 - 通常是DomainServices和Stubs。

import java.lang.annotation.Inherited

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@Inherited
annotation class DomainService

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
@Inherited
annotation class Stub


保留策略設定為RUNTIME  以允許Spring發現它們。使用其他策略時,註釋將在執行時被丟棄,這對我們的情況沒有幫助。您現在可以使用自定義註釋安全地註釋要公開的領域物件。

您還可以為其他DDD物件(如Aggregate,Entity,ValueType,Repository)建立描述性註釋,即使您沒有將它們公開給ApplicationContext也是如此。這是一個很棒的技術,可以讓人們進入領域驅動設計(使用一些javadoc,請參閱下面的示例),或者只是為了更好地瞭解您的業務及其建模方式。
這有時對於檢測程式碼評審中的一些錯誤很有用。就像包含實體的ValueType,帶有副作用的ValueType一樣......有時很難確定我們正在處理的物件的性質,尤其是在複雜豐富的業務領域。

/**
 * <p>
 * A DDD aggregate is a cluster of domain objects that is treated as a
 * single unit to ensure consistency across them.
 * </p>
 *
 * @see <a href= "https://martinfowler.com/bliki/DDD_Aggregate.html">
 *  Aggregates in Domain Driven Design
 * </a>
 */
public @interface Aggregate {

}

現在,我們將告訴Spring像對待那些物件@Service,@Component註解一樣,透過配置我們的自定義域註釋在基礎設施掃描。只需建立一個DomainConfiguration並註釋如下:

@Configuration
@ComponentScan(
        // basePackageClasses = [Recommendation::class],
        includeFilters = [ComponentScan.Filter(type = FilterType.ANNOTATION, value = [DomainService::class, Stub::class])])
class DomainConfiguration


請注意,ComponentScan預設情況下僅在SpringBootApplication包中查詢,包括其子包。因此,如果您的域不在此層次結構中,則需要指定一個基本包類,以識別域的根包(請參閱上面的註釋程式碼)。通常,我們在這裡使用主域聚合,因為常見的DDD約定鼓勵我們在他之後命名根域包。

您還可以使用basePackages並將包名稱作為String。但是這種做法是不鼓勵的,因為不能適應包重構。

這是這種方法的限制:
  • 如果需要一些基於條件或基於配置檔案的bean構造,則必須切換回bean工廠。
  • 主觀:有些人不喜歡註釋。
  • 如果您使用的是Kotlin,對maven外掛開放的類現在可以擴充套件,因此它違反了開放式原則。

如果您想進一步使用Maven,Koltin和SpringBoot實現基於六邊形體系結構的應用程式,您可以檢視此儲存庫

 

相關文章