自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何使用六邊形架構測試存儲庫適配器

譯文
開發(fā) 架構
通過本文,人們將了解如何使用六邊形架構測試存儲庫適配器,包括指導代碼和圖像。

當應用六邊形架構(端口和適配器)訪問數(shù)據(jù)庫等基礎設施元素時,可以通過適配器的方式實現(xiàn)。適配器只是域定義的接口(端口)的實現(xiàn)。本文將提供同一存儲庫端口的兩個實現(xiàn),一個在內存中,另一個基于JPA。其重點是如何使用相同的測試集測試這兩個實現(xiàn)。?

場景

許多在企業(yè)場景中開發(fā)的軟件解決方案都有一些狀態(tài),需要保存在持久存儲設備中以供以后訪問。根據(jù)特定的功能性需求和非功能性需求,選擇正確的持久性解決方案可能很難,而且很可能需要一份架構決策記錄(ADR),其中詳細說明了選擇的基本原理,包括替代方案和權衡。為了持久保持應用程序狀態(tài),用戶需要參考CAP定理來做出最適當?shù)臎Q策。 ?

這個決策過程不應該延遲應用程序域模型的設計和開發(fā)。工程團隊應該專注于交付(業(yè)務)價值,而不是維護一堆DDL腳本和開發(fā)一個高度變化的數(shù)據(jù)庫模式,在幾周(或幾個月)之后,他們會意識到使用文檔數(shù)據(jù)庫而不是關系數(shù)據(jù)庫可能會更好。 ?

同樣,關注交付域值也會阻止工程團隊基于過早采取的技術或基礎設施相關決策(例如在本例中是數(shù)據(jù)庫技術)的約束而做出與域相關的決策。正如行業(yè)專家所說,其架構應該允許延遲框架決策(以及基礎設施決策)。 ?

推遲與基礎設施相關的決策

回到數(shù)據(jù)庫技術的例子,一種推遲基礎設施決策的方法是決定使用哪種數(shù)據(jù)庫技術應使用,它將從存儲庫的簡單內存實現(xiàn)開始,其中域實體可以存儲在內存中的列表中。這種方法加速了特性和領域用例的發(fā)現(xiàn)、設計和實現(xiàn),使利益相關者能夠快速反饋重要事項:域值。?

現(xiàn)在,有人可能會想,“但是,我并沒有交付一個端到端工作的特性”,或者“我如何使用存儲庫的內存適配器驗證這一特性?”在這里,像六邊形架構(也稱為端口和適配器)這樣的架構模式和像域驅動設計(DDD)這樣的方法(對于擁有干凈的架構和最終干凈的代碼來說不是強制性的)開始發(fā)揮作用。 ?

六邊形架構

許多應用程序是按照經(jīng)典的三層架構設計的:?

(1)演示/控制器 ?

(2)服務(業(yè)務邏輯) ?

(3)持久層 ?

這種架構傾向于將域定義(例如,域實體和值對象)與表(例如,ORM實體)混合在一起,通常表示為簡單的數(shù)據(jù)傳輸對象。如下圖所示:?

與其相反,在六邊形架構中,實際的持久性相關類都是基于域模型定義的。?

通過使用存儲庫的端口 (它被定義為域模型的一部分),可以定義與底層技術無關的集成測試,它驗證了對存儲庫的域期望。以下了解在用于管理學生的簡單域模型中的代碼是什么樣子的。 ?

展示代碼

作為域的一部分,這個存儲庫端口看起來如何呢?它本質上定義了域對存儲庫的期望,并根據(jù)域泛在語言定義了所有方法: ?

Java 
public interface StudentRepository {

Student save(Student student);
Optional<Student> retrieveStudentWithEmail(ContactInfo contactInfo);
Publisher<Student> saveReactive(Student student);

}

基于存儲庫端口規(guī)范,可以創(chuàng)建集成測試定義。該定義僅依賴于端口,并且不知道為持久化域狀態(tài)而做出的任何底層技術決策。這個測試類將有一個屬性作為驗證期望的存儲庫接口(端口)的實例。以下顯示了這些測試的樣子?

Java 
public class StudentRepositoryTest {

StudentRepository studentRepository;

@Test
public void shouldCreateStudent() {
Student expected = randomNewStudent();
Student actual = studentRepository.save(expected);

assertAll("Create Student",
() -> assertEquals(0L, actual.getVersion()),
() -> assertEquals(expected.getStudentName(), actual.getStudentName()),
() -> assertNotNull(actual.getStudentId())
);
}

@Test
public void shouldUpdateExistingStudent() {
Student expected = randomExistingStudent();
Student actual = studentRepository.save(expected);
assertAll("Update Student",
() -> assertEquals(expected.getVersion()+1, actual.getVersion()),
() -> assertEquals(expected.getStudentName(), actual.getStudentName()),
() -> assertEquals(expected.getStudentId(), actual.getStudentId())
);
}
}

一旦存儲庫測試定義完成,就可以為內存存儲庫創(chuàng)建一個測試運行時(集成測試): ?

Java 
public class StudentRepositoryInMemoryIT extends StudentRepositoryTest {

@BeforeEach
public void setup() {
super.studentRepository = new StudentRepositoryInMemory();
}

}

或者使用Postgres對JPA進行更詳細的集成測試: ?

Java 
@Testcontainers
@ContextConfiguration(classes = {PersistenceConfig.class})
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class StudentRepositoryJpaIT extends StudentRepositoryTest{

@Autowired
public StudentRepository studentRepository;

@Container
public static PostgreSQLContainer container = new PostgreSQLContainer("postgres:latest")
.withDatabaseName("students_db")
.withUsername("sa")
.withPassword("sa");


@DynamicPropertySource
public static void overrideProperties(DynamicPropertyRegistry registry){
registry.add("spring.datasource.url", container::getJdbcUrl);
registry.add("spring.datasource.username", container::getUsername);
registry.add("spring.datasource.password", container::getPassword);
registry.add("spring.datasource.driver-class-name", container::getDriverClassName);
}

@BeforeEach
public void setup() {
super.studentRepository = studentRepository;
}
}

兩個測試運行時都擴展了相同的測試定義,因此可以確定,當從內存適配器切換到最終的全功能JPA持久性時,不會有任何測試受到影響,因為它只需要配置相應的測試運行時。

這種方法將允許用戶在不依賴于框架的情況下定義存儲庫端口的測試,并在域定義更好、更穩(wěn)定,以及團隊決定使用更好地滿足解決方案質量屬性的數(shù)據(jù)庫技術時重用這些測試。?

項目的整體結構如下圖所示:?

項目的結構介紹:?

  • student-domain:域定義模塊,包括實體、值對象、域事件、端口等。這個模塊不依賴于框架,盡可能使用Java。 ?
  • student-application:目前,這個模塊沒有代碼,因為它超出了本文的范圍。遵循六邊形架構,該模塊編排對域模型的調用,成為域用例的入口點。 ?
  • student-repository-test:這個模塊包含存儲庫測試定義,不依賴于框架,只驗證所提供的存儲庫端口的期望。 ?
  • student-repository-inmemory:域定義的存儲庫端口的內存實現(xiàn)。它還包含集成測試,該測試為學生存儲庫測試的測試定義提供了端口的內存適配器。 ?
  • student-repository-JPA:域定義的存儲庫端口的JPA實現(xiàn)。它還包含集成測試,該測試為學生存儲庫測試的測試定義提供了端口的內存適配器。這個集成測試設置有點復雜,因為它將一個基本的Spring場景和一個Postgres容器一起啟動。
  • student-shared-kernel:這個模塊不在本文討論范圍之內;它為設計項目的其余部分提供了一些實用程序類和接口。

結論

在項目中使用這種架構風格可以促進域模型和基礎設施元素之間的良好分離,確保后者不會影響前者,同時促進良好的代碼質量(干凈的代碼)和高可維護性。 ?

原文標題:??Testing Repository Adapters With Hexagonal Architecture??,作者:David Cano

責任編輯:華軒 來源: 51CTO
相關推薦

2017-02-21 17:25:51

架構六邊形架構數(shù)據(jù)庫

2023-08-06 23:31:36

架構系統(tǒng)RPC

2020-04-02 13:44:57

架構Netflix數(shù)據(jù)

2019-12-16 08:08:39

六邊形架構分層架構架構

2024-04-17 08:06:41

六邊形洋蔥架構領域

2023-11-01 07:41:39

六邊形架構適配器架構

2023-12-13 10:06:28

六邊形架構系統(tǒng)測試

2022-12-28 07:48:40

六邊形動畫CSS

2021-08-29 18:32:18

CSS

2023-10-30 10:12:20

2025-01-17 11:38:10

2025-02-24 07:39:53

2017-06-08 10:33:42

軟件開發(fā)前后端架構

2022-11-08 08:00:00

開發(fā)Uber數(shù)據(jù)庫

2023-09-08 18:37:34

HarmonyOS

2009-12-21 10:26:09

Oracle適配器

2022-02-18 17:21:29

適配器模式客戶端

2020-10-25 08:56:21

適配器模式
點贊
收藏

51CTO技術棧公眾號