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

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

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

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

場景

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

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

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

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

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

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

六邊形架構(gòu)

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

(1)演示/控制器 ?

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

(3)持久層 ?

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

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

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

展示代碼

作為域的一部分,這個存儲庫端口看起來如何呢?它本質(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ù)決策。這個測試類將有一個屬性作為驗(yàn)證期望的存儲庫接口(端口)的實(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)建一個測試運(yùn)行時(集成測試): ?

Java 
public class StudentRepositoryInMemoryIT extends StudentRepositoryTest {

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

}

或者使用Postgres對JPA進(jìn)行更詳細(xì)的集成測試: ?

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

兩個測試運(yùn)行時都擴(kuò)展了相同的測試定義,因此可以確定,當(dāng)從內(nèi)存適配器切換到最終的全功能JPA持久性時,不會有任何測試受到影響,因?yàn)樗恍枰渲孟鄳?yīng)的測試運(yùn)行時。

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

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

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

  • student-domain:域定義模塊,包括實(shí)體、值對象、域事件、端口等。這個模塊不依賴于框架,盡可能使用Java。 ?
  • student-application:目前,這個模塊沒有代碼,因?yàn)樗隽吮疚牡姆秶W裱呅渭軜?gòu),該模塊編排對域模型的調(diào)用,成為域用例的入口點(diǎn)。 ?
  • student-repository-test:這個模塊包含存儲庫測試定義,不依賴于框架,只驗(yàn)證所提供的存儲庫端口的期望。 ?
  • student-repository-inmemory:域定義的存儲庫端口的內(nèi)存實(shí)現(xiàn)。它還包含集成測試,該測試為學(xué)生存儲庫測試的測試定義提供了端口的內(nèi)存適配器。 ?
  • student-repository-JPA:域定義的存儲庫端口的JPA實(shí)現(xiàn)。它還包含集成測試,該測試為學(xué)生存儲庫測試的測試定義提供了端口的內(nèi)存適配器。這個集成測試設(shè)置有點(diǎn)復(fù)雜,因?yàn)樗鼘⒁粋€基本的Spring場景和一個Postgres容器一起啟動。
  • student-shared-kernel:這個模塊不在本文討論范圍之內(nèi);它為設(shè)計(jì)項(xiàng)目的其余部分提供了一些實(shí)用程序類和接口。

結(jié)論

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

原文標(biāo)題:??Testing Repository Adapters With Hexagonal Architecture??,作者:David Cano

責(zé)任編輯:華軒 來源: 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-01-17 11:38:10

2025-02-24 07:39:53

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適配器

2025-09-05 09:07:00

2025-07-30 09:05:00

AI模型訓(xùn)練
點(diǎn)贊
收藏

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