Karena kami terus kami eksplorasi kerangka Jawa baru dan minat Anda pada musim semi Boot buku , kita sedang melihat kerangka Quarkus baru untuk Java. Anda dapat menemukan penjelasan mendetailnya di sini , dan hari ini kami mengusulkan untuk membaca terjemahan artikel sederhana yang menunjukkan betapa nyamannya mengikuti arsitektur yang bersih menggunakan Quarkus .
Quarkus dengan cepat mendapatkan status kerangka kerja yang tidak dapat dihindari. Oleh karena itu, saya memutuskan untuk membahasnya sekali lagi dan memeriksa sejauh mana ia sesuai dengan prinsip Arsitektur Murni.
Sebagai titik awal, saya mengambil proyek Maven sederhana yang memiliki 5 modul standar untuk membuat aplikasi CRUD REST mengikuti prinsip arsitektur bersih:
domain: objek domain dan antarmuka gateway untuk objek iniapp-api: antarmuka aplikasi yang sesuai dengan kasus praktisapp-impl: implementasi kasus-kasus ini melalui area subjek. Tergantung padaapp-apidandomain.infra-persistence: Menerapkan gateway yang memungkinkan domain berinteraksi dengan API database. Tergantungdomain.infra-web: Membuka kasus yang dipertimbangkan untuk berinteraksi dengan dunia luar menggunakan REST. Tergantungapp-api.
Selain itu, kita akan membuat modul
main-partitionyang akan berfungsi sebagai artefak aplikasi yang dapat diterapkan.
Ketika berencana untuk bekerja dengan Quarkus, langkah pertama adalah menambahkan spesifikasi BOM ke file POM proyek Anda. BOM ini akan mengelola semua versi dependensi yang Anda gunakan. Anda juga perlu mengkonfigurasi plugin standar untuk proyek maven di alat manajemen plugin Anda, seperti plugin yang pasti . Saat Anda bekerja dengan Quarkus, Anda juga akan mengkonfigurasi plugin dengan nama yang sama di sini. Last but not least, di sini Anda perlu mengonfigurasi plugin untuk bekerja dengan setiap modul (di <build> <plugins> ... </plugins> </build>), yaitu plugin Jandex... Karena Quarkus menggunakan CDI, plugin Jandex menambahkan file indeks ke setiap modul; file tersebut berisi rekaman dari semua penjelasan yang digunakan dalam modul ini dan tautan yang menunjukkan di mana penjelasan tersebut digunakan. Hasilnya, CDI jauh lebih mudah ditangani, dengan lebih sedikit pekerjaan yang harus diselesaikan nanti.
Sekarang setelah struktur dasarnya siap, Anda dapat mulai membuat aplikasi lengkap. Untuk melakukan ini, Anda perlu memastikan bahwa partisi-utama membuat aplikasi Quarkus yang dapat dieksekusi. Mekanisme ini diilustrasikan dalam contoh "mulai cepat" yang disediakan di Quarkus.
Pertama, kami mengonfigurasi build untuk menggunakan plugin 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>
Selanjutnya, mari tambahkan dependensi ke setiap modul aplikasi, yang akan bersama dengan dependensi
quarkus-resteasydan quarkus-jdbc-mysql. Pada dependensi terakhir, Anda dapat mengganti database dengan yang paling Anda sukai (mengingat nanti kita akan pergi ke jalur pengembangan asli, dan oleh karena itu kita tidak dapat menggunakan database yang disematkan, misalnya, H2).
Atau, Anda dapat menambahkan profil sehingga Anda dapat membangun aplikasi asli nanti. Untuk melakukan ini, Anda benar-benar membutuhkan stand pengembangan tambahan (GraalVM,
native-imagedan XCode jika Anda menggunakan OSX).
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
Sekarang, jika Anda menjalankan
mvn package quarkus:devdari root proyek, Anda memiliki aplikasi Quarkus yang berfungsi! Belum banyak yang bisa dilihat, karena kami belum memiliki pengontrol atau konten apa pun.
Menambahkan pengontrol REST
Dalam latihan ini, mari kita beralih dari pinggiran ke inti. Pertama, mari buat pengontrol REST yang akan mengembalikan data pengguna (dalam contoh ini, ini hanya namanya).
Untuk menggunakan JAX-RS API, dependensi harus ditambahkan ke POM infra-web:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
Kode pengontrol paling sederhana terlihat seperti ini:
@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;
}
}
Jika kita menjalankan aplikasinya sekarang, kita dapat memanggil localhost : 8080 / customer dan melihatnya
Joedalam format JSON.
Tambahkan kasus tertentu
Selanjutnya, mari tambahkan kasus dan implementasi untuk kasus praktis ini. Mari
app-apitentukan kasus berikut:
public interface GetCustomers {
List<Response> getCustomers();
class Response {
private String name;
public Response(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
The
app-implmembuat implementasi sederhana dari interface ini.
@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"));
}
}
Agar CDI dapat melihat komponennya
GetCustomersImpl, Anda memerlukan anotasi khusus UseCaseseperti yang ditentukan di bawah ini. Anda juga dapat menggunakan ApplicationScoped dan anotasi standar Transactional, tetapi dengan membuat anotasi Anda sendiri, Anda mendapatkan kemampuan untuk mengelompokkan kode secara lebih logis dan melepaskan kode implementasi Anda dari kerangka kerja seperti CDI.
@ApplicationScoped
@Transactional
@Stereotype
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
}
Untuk menggunakan penjelasan CDI, Anda harus menambahkan dependensi berikut ke file POM
app-implselain dependensi app-apidan domain.
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.transaction</groupId>
<artifactId>jakarta.transaction-api</artifactId>
</dependency>
Selanjutnya, kita perlu memodifikasi pengontrol REST untuk menggunakannya dalam kasus
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());
}
...
Jika sekarang Anda menjalankan aplikasi dan memanggil localhost : 8080 / customer, Anda akan melihatnya
Jimdalam format JSON.
Definisi dan implementasi domain
Selanjutnya kita akan fokus pada domain. Esensi di sini
domaincukup sederhana, terdiri dari Customerantarmuka gateway di mana kami akan menerima konsumen.
public class Customer {
private String name;
public Customer(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public interface CustomerGateway {
List<Customer> getAllCustomers();
}
Kami juga perlu menyediakan implementasi gateway sebelum kami dapat mulai menggunakannya. Kami menyediakan antarmuka seperti itu di
infra-persistence.
Untuk implementasi ini, kita akan menggunakan dukungan JPA yang tersedia di Quarkus, dan juga menggunakan kerangka Panache , yang akan membuat hidup kita sedikit lebih mudah. Selain domain, kita harus menambahkan
infra-persistenceketergantungan berikut ke:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
Pertama, kami mendefinisikan entitas JPA yang sesuai dengan konsumen.
@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;
}
}
Saat bekerja dengan Panache, Anda dapat memilih salah satu dari dua opsi: entitas Anda akan mewarisi PanacheEntity, atau Anda akan menggunakan pola repositori / DAO. Saya bukan penggemar pola ActiveRecord, jadi saya akan berhenti di repositori sendiri, tetapi apa yang akan Anda kerjakan terserah Anda.
@ApplicationScoped
public class CustomerRepository implements PanacheRepository<CustomerJpa> {
}
Sekarang setelah kita memiliki entitas dan repositori JPA, kita dapat mengimplementasikan gateway
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());
}
}
Sekarang Anda dapat mengubah kode dalam implementasi kasus kami, sehingga menggunakan gateway.
...
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());
}
...
Kami belum dapat memulai aplikasi kami, karena aplikasi Quarkus masih perlu dikonfigurasi dengan parameter persistensi yang diperlukan. Di
src/main/resources/application.propertiesmodul, main-partitiontambahkan parameter berikut.
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
Untuk melihat data asli, kami juga akan menambahkan file
import.sqlke direktori yang sama tempat data ditambahkan.
insert into CustomerJpa(id, name) values(1, 'Joe');
insert into CustomerJpa(id, name) values(2, 'Jim');
Jika Anda sekarang menjalankan aplikasi dan panggilan localhost : 8080 / pelanggan, Anda akan melihat
Joehal itu Jimdalam format JSON juga. Jadi, kami memiliki aplikasi yang lengkap, dari REST hingga database.
Opsi asli
Jika Anda ingin membangun aplikasi asli, maka Anda perlu melakukannya menggunakan perintah
mvn package -Pnative. Ini dapat memakan waktu sekitar beberapa menit, tergantung pada apa perkembangan Anda. Quarkus cukup cepat saat startup dan tanpa dukungan asli, dimulai dalam 2-3 detik, tetapi ketika dikompilasi menjadi executable asli menggunakan GraalVM, waktu yang sesuai dikurangi menjadi kurang dari 100 milidetik. Untuk aplikasi Java, itu sangat cepat.
Menguji
Anda dapat menguji aplikasi Quarkus menggunakan kerangka kerja pengujian Quarkus yang sesuai. Jika Anda menganotasi pengujian
@QuarkusTest, JUnit akan meluncurkan konteks Quarkus terlebih dahulu, lalu menjalankan pengujian. Tes seluruh aplikasi main-partitionakan terlihat seperti ini:
@QuarkusTest
public class CustomerResourceTest {
@Test
public void testList() {
given()
.when().get("/customer")
.then()
.statusCode(200)
.body("$.size()", is(2),
"name", containsInAnyOrder("Joe", "Jim"));
}
}
Kesimpulan
Dalam banyak hal, Quarkus adalah pesaing tangguh Spring Boot. Menurut pendapat saya, beberapa hal di Quarkus lebih baik diselesaikan. Meskipun app-impl memiliki dependensi framework, itu hanya dependensi untuk anotasi (dalam kasus Spring, saat kami menambahkan
spring-contextuntuk mendapatkan @Component, kami menambahkan banyak dependensi inti Spring). Jika Anda tidak menyukai ini, Anda juga dapat menambahkan file Java ke bagian utama, menggunakan penjelasan @Producesdari CDI dan membuat komponen di sana; dalam hal ini, Anda tidak memerlukan dependensi tambahan dalam app-impl. Tetapi untuk beberapa alasan, jakarta.enterprise.cdi-apisaya ingin melihat kecanduan di sana bukan kecanduan spring-context.
Quarkus cepat, sangat cepat. Ini lebih cepat daripada Spring Boot dengan aplikasi jenis ini. Karena, menurut Arsitektur Bersih, sebagian besar (jika tidak semua) dependensi kerangka kerja harus berada di luar aplikasi, pilihan antara Quarkus dan Spring Boot menjadi jelas. Dalam hal ini, keuntungan dari Quarkus adalah bahwa itu segera dibuat dengan mempertimbangkan dukungan GraalVM, dan oleh karena itu, dengan biaya usaha yang minimal, ini memungkinkan Anda untuk mengubah aplikasi menjadi yang asli. Spring Boot masih tertinggal dari Quarkus dalam hal ini, tapi saya yakin itu akan segera menyusul.
Namun, bereksperimen dengan Quarkus juga membantu saya menyadari banyaknya kemalangan yang menanti mereka yang mencoba menggunakan Quarkus dengan server aplikasi klasik Jakarta EE. Meskipun belum banyak yang bisa dilakukan dengan Quarkus, pembuat kodenya mendukung berbagai teknologi yang belum mudah digunakan dalam konteks Jakarta EE dengan server aplikasi tradisional. Quarkus mencakup semua hal dasar yang dibutuhkan oleh orang-orang yang memahami Jakarta EE, dan pengembangannya jauh lebih lancar. Akan sangat menarik untuk melihat bagaimana ekosistem Jawa dapat menangani persaingan semacam ini.
Semua kode untuk proyek ini diposting di Github .