用Quarkus實現乾淨清晰的Clean架構 - Sourced Blog
Quarkus迅速成為一個不容忽視的框架,因此,我決定再次嘗試一下,以檢視在編寫Quarkus應用程式時可以在多大程度上堅持clean Architecture(CA)原則。
我的起點是一個基本的Maven專案,該專案在執行CA時具有用於CRUD REST應用程式的5個標準模組:
- domain:領域實體和這些實體的閘道器介面
- app-api:應用程式的用例介面
- app-impl:使用領域來實現那些用例。取決於app-api和domain。
- infra-persistence:使用資料庫API實現域定義的閘道器。取決於domain。
- infra-web:使用REST將用例暴露於外界。取決於app-api。
此外,我們將建立一個main-partition模組,該模組將用作應用程式的可部署工件。
當您要使用Quarkus時,要做的第一件事是將BOM新增到專案的父POM中。此BOM將管理您將使用的依賴項的所有版本。您還需要在外掛管理中為maven專案配置標準外掛,例如compile和surefire外掛。當我們將使用Quarkus時,您還可以在外掛管理中在此處配置Quarkus外掛。最後但並非最不重要的一點是,您將配置一個外掛來為每個模組執行(如所示<build><plugins>...</plugins></build>),即Jandex外掛。由於Quarkus使用CDI,因此Jandex外掛會向每個模組新增一個索引檔案,該檔案包含該模組中使用的每個註釋以及指向其使用位置的連結。這將使使用CDI的生活更加輕鬆,並減少以後的工作量。
現在已經構建了基本結構,我們可以從構建功能正常的應用程式開始。為此,我們首先要確保main-partition建立了一個可執行的Quarkus應用程式。Quarkus提供的每個快速入門示例中都可以找到這種機制。
首先,將構建配置為使用Quarkus外掛:
<build> <plugins> <plugin> <groupId>io.quarkus</groupId> <artifactId>quarkus-maven-plugin</artifactId> <executions> <execution> <goals> <goal>build</goal> </goals> </execution> </executions> </plugin> </plugins> </build> |
接下來,除了quarkus-resteasy和quarkus-jdbc-mysql依賴關係之外,您還需要嚮應用程式的每個模組新增依賴關係。您可以將最後一個依賴項更改為您選擇的資料庫(請記住,如果以後要使用本機路由,則不能使用像H2這樣的嵌入式資料庫)。
(可選)您可以新增一個配置檔案,該配置檔案稍後可用於構建本機應用程式。這確實需要您在開發平臺上擁有其他工具(GraalVM native-image和XCode(如果使用的是OSX))。
<profiles> <profile> <id>native</id> <activation> <property> <name>native</name> </property> </activation> <properties> <quarkus.package.type>native</quarkus.package.type> </properties> </profile> </profiles> |
現在,如果您mvn package quarkus:dev從專案根目錄執行,將有一個正在執行的Quarkus應用程式!您不會看到很多東西,因為我們還沒有任何控制器或內容。
新增REST控制器
對於本練習,我們將從外而內進行工作。首先,我們將建立一個REST控制器,該控制器將返回客戶資料(在此示例中,該資料僅包含名稱)。
為了使用JAX-RS API,您需要向infra-webPOM 新增一個依賴項:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-jackson</artifactId> </dependency> |
基本的控制器程式碼如下所示:
@Path("/customer") @Produces(MediaType.APPLICATION_JSON) public class CustomerResource { @GET public List<JsonCustomer> list() { return getCustomers.getCustomer().stream() .map(response -> new JsonCustomer(response.getName())) .collect(Collectors.toList()); } public static class JsonCustomer { private String name; public JsonCustomer(String name) { this.name = name; } public String getName() { return name; } } |
如果我們現在執行該應用程式,您將能夠呼叫http://localhost:8080/customer並Joe以JSON格式檢視。
新增用例
接下來,我們將新增一個用例和該用例的實現。在app-api我們定義以下使用情況:
public interface GetCustomers { List<Response> getCustomers(); class Response { private String name; public Response(String name) { this.name = name; } public String getName() { return name; } } } |
在本文中,app-impl我們將為該介面建立一個基本實現。
@UseCase public class GetCustomersImpl implements GetCustomers { private CustomerGateway customerGateway; public GetCustomersImpl(CustomerGateway customerGateway) { this.customerGateway = customerGateway; } @Override public List<Response> getCustomers() { return Arrays.asList(new Response("Jim")); } } |
為了讓CDI看到GetCustomersImplBean,您需要使用UseCase如下定義的自定義註釋。您還可以使用標準ApplicationScoped和Transactional批註,但是建立自己的批註可以使您對這些批註進行邏輯分組,並使實現程式碼與CDI之類的框架脫鉤。
@ApplicationScoped @Transactional @Stereotype @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { } |
為了使用CDI註解,你必須以下依存增加的POM app-impl除了依賴於app-api和domain。
<dependency> <groupId>jakarta.enterprise</groupId> <artifactId>jakarta.enterprise.cdi-api</artifactId> </dependency> <dependency> <groupId>jakarta.transaction</groupId> <artifactId>jakarta.transaction-api</artifactId> </dependency> |
現在,我們需要更改REST控制器以使用app-api用例。
... private GetCustomers getCustomers; public CustomerResource(GetCustomers getCustomers) { this.getCustomers = getCustomers; } @GET public List<JsonCustomer> list() { return getCustomers.getCustomer().stream() .map(response -> new JsonCustomer(response.getName())) .collect(Collectors.toList()); } ... |
如果現在執行該應用程式並呼叫http://localhost:8080/customer,您將看到JimJSON格式。
定義和實施領域
因此,接下來:業務領域。在domain這裡是非常簡單,它由Customer和閘道器介面來獲得客戶。
public class Customer { private String name; public Customer(String name) { this.name = name; } public String getName() { return name; } } public interface CustomerGateway { List<Customer> getAllCustomers(); } |
在開始使用閘道器之前,我們還需要提供閘道器的實現。在中infra-persistence,我們將提供這樣的介面。
對於此實現,我們將在Quarkus中使用JPA支援,並使用Panache框架使生活更輕鬆。infra-persistence除了之外,您還必須新增以下依賴項domain:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-orm-panache</artifactId> </dependency> |
首先,我們為客戶定義JPA實體。
@Entity public class CustomerJpa { @Id @GeneratedValue private Long id; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } |
使用Panache,您可以選擇擴充套件實體PanacheEntity或使用儲存庫/ DAO模式。我不是真的很喜歡ActiveRecord模式,因此我選擇使用儲存庫,但是選擇取決於您。
@ApplicationScoped public class CustomerRepository implements PanacheRepository<CustomerJpa> { } |
現在我們有了JPA實體和儲存庫,我們可以實現Customer閘道器了。
@ApplicationScoped public class CustomerGatewayImpl implements CustomerGateway { private CustomerRepository customerRepository; @Inject public CustomerGatewayImpl(CustomerRepository customerRepository) { this.customerRepository = customerRepository; } @Override public List<Customer> getAllCustomers() { return customerRepository.findAll().stream() .map(c -> new Customer(c.getName())) .collect(Collectors.toList()); } } |
現在,我們可以在用例實現中更改程式碼以使用閘道器。
... private CustomerGateway customerGateway; @Inject public GetCustomersImpl(CustomerGateway customerGateway) { this.customerGateway = customerGateway; } @Override public List<Response> getCustomer() { return customerGateway.getAllCustomers().stream() .map(customer -> new GetCustomers.Response(customer.getName())) .collect(Collectors.toList()); } ... |
我們還不能執行我們的應用程式,因為我們現在需要為Quarkus應用程式配置必要的永續性引數。在src/main/resources/application.properties中main-partition,新增以下引數:
quarkus.datasource.url=jdbc:mysql://localhost/test quarkus.datasource.driver=com.mysql.cj.jdbc.Driver quarkus.hibernate-orm.dialect=org.hibernate.dialect.MySQL8Dialect quarkus.datasource.username=root quarkus.datasource.password=root quarkus.datasource.max-size=8 quarkus.datasource.min-size=2 quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.sql-load-script=import.sql |
如果現在執行該應用程式並呼叫http://localhost:8080/customer,您將看到Joe和JimJSON格式。現在,我們有了從REST到資料庫的完整應用程式。
更詳細測試點選標題見原文
相關文章
- Clean Architecture - 清晰簡潔的Android 應用架構Android應用架構
- 清晰架構(Clean Architecture)的Go微服務—重大升級架構Go微服務
- 帶你動手實現 MVP+Clean架構!MVP架構
- Flutter應用的Clean架構示例專案Flutter架構
- 乾淨架構在 Web 服務開發中的實踐架構Web
- 基於乾淨架構使用原始SQL和DDD實現.NET Core REST API開源案例架構SQLRESTAPI
- 經驗分享:乾淨整潔程式碼(clean code)的特點 - oliver
- 乾淨整潔程式碼(Clean Code)的本質是什麼? - mariocervera
- Clean架構中不好的部分 -James Hickey架構
- 否定洋蔥或clean架構的垂直切片架構 - Jimmy Bogard架構
- 推薦5款乾淨又實用的軟體
- Spring Boot的Clean架構教程與原始碼 - BaeldungSpring Boot架構原始碼
- 用Python實現圖片的清晰掃描Python
- Spring Boot中實現乾淨API響應Spring BootAPI
- [精選]Clean PHP Code(清晰的PHP程式碼思路)PHP
- TencentHub的架構實現架構
- 使用Spring Data等高階持久框架在乾淨架構下如何處理?Spring框架架構
- Android架構系列-MVP架構的實際應用Android架構MVP
- 【虹科乾貨】Lambda資料架構和Kappa資料架構——構建現代資料架構架構APP
- Clean架構能夠解決哪些問題? - jbogard架構
- 用c++實現淨現值的計算C++
- 人人都是架構師-清晰架構 | 京東物流技術團隊架構
- 前端插拔式 SPA 應用架構實現方案前端應用架構
- 唯品會架構師是如何實現架構重構的架構
- 手把手寫一個Clean(+mvp+rxjava)架構的DemoMVPRxJava架構
- 乾貨 | CDN搭配OSS最佳實踐 ——搭建動靜態分離的應用架構應用架構
- 如何清晰地描述一個專案架構架構
- MHA高可用架構的實現方式架構
- 沃達豐希臘公司用Quarkus代替Spring Boot - quarkusSpring Boot
- 如何實現彈性架構架構
- Kotlin如何實現MVP架構KotlinMVP架構
- 領域驅動設計和Clean架構之間的區別? - stackexchange架構
- 架構師必備:巧用Canal實現非同步、解耦的架構架構非同步解耦
- 乾貨篇:超多內容微服務架構實戰微服務架構
- 乾貨 | 攜程酒店實時數倉架構和案例架構
- 乾貨:軟體架構詳解架構
- MySQL一主一從架構的實現MySql架構
- 基於零信任架構的IDaaS實現架構