偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

如何使用六邊形架構(gòu)測試存儲庫適配器

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

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

場景

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

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

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

推遲與基礎(chǔ)設(shè)施相關(guān)的決策

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

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

六邊形架構(gòu)

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

(1)演示/控制器 ?

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

(3)持久層 ?

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

與其相反,在六邊形架構(gòu)中,實際的持久性相關(guān)類都是基于域模型定義的。?

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

展示代碼

作為域的一部分,這個存儲庫端口看起來如何呢?它本質(zhì)上定義了域?qū)Υ鎯斓钠谕⒏鶕?jù)域泛在語言定義了所有方法: ?

Java 
public interface StudentRepository {

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

}

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

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())
);
}
}

一旦存儲庫測試定義完成,就可以為內(nèi)存存儲庫創(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;
}
}

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

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

項目的整體結(jié)構(gòu)如下圖所示:?

項目的結(jié)構(gòu)介紹:?

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

結(jié)論

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

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

責任編輯:華軒 來源: 51CTO
相關(guān)推薦

2017-02-21 17:25:51

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

2023-08-06 23:31:36

架構(gòu)系統(tǒng)RPC

2020-04-02 13:44:57

架構(gòu)Netflix數(shù)據(jù)

2019-12-16 08:08:39

六邊形架構(gòu)分層架構(gòu)架構(gòu)

2024-04-17 08:06:41

六邊形洋蔥架構(gòu)領(lǐng)域

2023-11-01 07:41:39

六邊形架構(gòu)適配器架構(gòu)

2023-12-13 10:06:28

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

2022-12-28 07:48:40

六邊形動畫CSS

2021-08-29 18:32:18

CSS

2025-02-24 07:39:53

2025-01-17 11:38:10

2023-10-30 10:12:20

2017-06-08 10:33:42

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

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

適配器模式客戶端
點贊
收藏

51CTO技術(shù)棧公眾號