Розробка бекенду сайту на Java (Quarkus)
Quarkus — Java-фреймворк, переосмислений для cloud-native і Kubernetes. Головна різниця від Spring Boot: Quarkus компілює додаток у нативний бінарник через GraalVM Native Image. Час холодного старту — 0.01–0.1 секунди проти 2–10 секунд у Spring Boot. Споживання пам'яті — 20–60 МБ проти 200–500 МБ.
Це не просто цифри для бенчмарків. У Kubernetes-оточенні з автомасштабуванням швидкий старт означає реальне горизонтальне масштабування: Pod піднімається за секунди, не хвилини. На Serverless — це різниця між робочим і нероблячим рішенням.
У JVM-режимі Quarkus конкурує з Spring Boot, у нативному — з Go.
Відмінності від Spring Boot
Quarkus використовує знайомі специфікації: JAX-RS, CDI, JPA, MicroProfile. Якщо знаєте Spring, перехід займає кілька днів. Ключові відмінності:
- Dependency Injection через CDI (
@ApplicationScoped,@RequestScoped), не Spring DI - REST через RESTEasy Reactive (JAX-RS), не Spring MVC
- ORM — Hibernate з Panache (Active Record або Repository pattern)
- Конфігурація через
application.propertiesабо YAML, MicroProfile Config
Структура та REST
// Product entity з Panache Active Record
@Entity
@Table(name = "products")
public class Product extends PanacheEntityBase {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
@Column(nullable = false, length = 255)
public String name;
@Column(unique = true)
public String slug;
@Column(precision = 10, scale = 2)
public BigDecimal price;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
public Category category;
public boolean isActive = true;
@Column(columnDefinition = "jsonb")
@Type(JsonType.class)
public Map<String, Object> attributes = new HashMap<>();
// Статичні методи Panache
public static List<Product> findActive() {
return list("isActive", true);
}
public static Page<Product> findActiveByCategory(Long categoryId, int page, int size) {
return find("category.id = ?1 and isActive = true", categoryId)
.page(page, size);
}
}
Resource (Controller)
@Path("/api/v1/products")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@ApplicationScoped
public class ProductResource {
@Inject
ProductService productService;
@GET
@Authenticated
public Response list(
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("20") @Max(100) int size,
@QueryParam("category_id") Long categoryId) {
PanacheQuery<Product> query = categoryId != null
? Product.find("category.id = ?1 and isActive = true", categoryId)
: Product.find("isActive", true);
List<Product> products = query
.page(page, size)
.list();
long total = query.count();
return Response.ok(new PagedResponse<>(
products.stream().map(ProductDto::from).toList(),
page, size, total
)).build();
}
@POST
@RolesAllowed("admin")
@Transactional
public Response create(@Valid CreateProductRequest request) {
Product product = productService.create(request);
return Response.status(Response.Status.CREATED)
.entity(ProductDto.from(product))
.build();
}
@GET
@Path("/{id}")
public ProductDto get(@PathParam("id") Long id) {
return Product.findByIdOptional(id)
.map(ProductDto::from)
.orElseThrow(NotFoundException::new);
}
@DELETE
@Path("/{id}")
@RolesAllowed("admin")
@Transactional
public Response delete(@PathParam("id") Long id) {
boolean deleted = Product.deleteById(id);
return deleted ? Response.noContent().build() : Response.status(404).build();
}
}
Reactive endpoint
Quarkus з RESTEasy Reactive підтримує неблокуючі операції нативно:
@GET
@Path("/search")
public Uni<List<ProductDto>> search(@QueryParam("q") String query) {
return Product.<Product>find("name like ?1", "%" + query + "%")
.list()
.map(products -> products.stream().map(ProductDto::from).toList());
}
// Реактивна робота з БД через Hibernate Reactive
@GET
@Path("/{id}/with-reviews")
public Uni<ProductWithReviews> getWithReviews(@PathParam("id") Long id) {
return Panache.withTransaction(() ->
Product.<Product>findById(id)
.flatMap(product -> Review.<Review>find("product.id", id).list()
.map(reviews -> new ProductWithReviews(product, reviews)))
);
}
Безпека через SmallRye JWT
# application.properties
mp.jwt.verify.publickey.location=META-INF/resources/publicKey.pem
mp.jwt.verify.issuer=https://myapp.com
quarkus.http.auth.permission.authenticated.paths=/api/v1/*
quarkus.http.auth.permission.authenticated.policy=authenticated
quarkus.http.auth.permission.public.paths=/api/v1/auth/*,/api/v1/products
quarkus.http.auth.permission.public.policy=permit
@Path("/api/v1/auth")
public class AuthResource {
@Inject
@Claim(standard = Claims.sub)
Optional<JsonString> subject;
@POST
@Path("/login")
@PermitAll
public Response login(@Valid LoginRequest request) {
User user = User.findByEmail(request.email())
.orElseThrow(() -> new UnauthorizedException("Invalid credentials"));
if (!BCrypt.verifyer().verify(request.password().toCharArray(), user.passwordHash).verified) {
throw new UnauthorizedException("Invalid credentials");
}
String token = Jwt.issuer("https://myapp.com")
.subject(user.id.toString())
.groups(Set.of(user.role))
.claim("email", user.email)
.expiresIn(Duration.ofHours(1))
.sign();
return Response.ok(new LoginResponse(token)).build();
}
}
Нативна збірка
# JVM-режим (звичайна збірка)
./mvnw package -DskipTests
java -jar target/quarkus-app/quarkus-run.jar
# Нативний образ — потребує GraalVM або Docker
./mvnw package -Pnative -DskipTests
# Нативний в Docker без локального GraalVM
./mvnw package -Pnative -Dquarkus.native.container-build=true
docker build -f src/main/docker/Dockerfile.native -t myapp .
# Розмір образу: ~100MB (проти ~500MB JVM Spring Boot)
# Startup: ~50ms (проти 3-8s Spring Boot)
# Пам'ять: ~30MB (проти ~200MB Spring Boot)
Інтеграція Kafka
// Producer
@ApplicationScoped
public class ProductEventProducer {
@Inject
@Channel("products-out")
Emitter<ProductEvent> emitter;
public void publishCreated(Product product) {
emitter.send(Message.of(new ProductEvent("created", product.id, product.name)));
}
}
// Consumer
@ApplicationScoped
public class InventoryConsumer {
@Incoming("inventory-updates")
public void onInventoryUpdate(InventoryUpdateEvent event) {
Product.update("stock = ?1 where id = ?2", event.stock(), event.productId());
}
}
Dev режим
Quarkus Dev Mode — один із найкращих hot-reload механізмів у Java-екосистемі:
./mvnw quarkus:dev
# Будь-які зміни застосовуються без перезапуску
# Dev UI доступний на localhost:8080/q/dev
# Вбудований Dev Services: PostgreSQL, Redis, Kafka — запускаються автоматично в Docker
Графік розробки
- Налаштування проекту + Dev Services + міграції Flyway — 3–5 днів
- Entities (Panache) + Resources — 1–1,5 тижні
- Security + JWT — 3–5 днів
- Reactive endpoints якщо потрібні — додаткова тиждень
- Налаштування нативної збірки — 2–5 днів (часто потребує reflection hints)
- Тести (QuarkusTest + RestAssured) — 1 тиждень
Корпоративний бекенд: 7–14 тижнів. Quarkus окупається у Kubernetes-середовищі, особливо коли потрібне швидке автомасштабування або Serverless-деплой.







