Spring Modulith - bitta Spring Boot application ichida aniq chegaralangan modullar qurish uchun ishlatiladi.
Spring Modulith quyidagi mavzularni o‘z ichiga oladi: application modules & boundaries, inter-module event communication, module integration tests, documenting module structure, va modulith’dan microservices’ga evolyutsiya.
Spring’ning rasmiy hujjatlarida Spring Modulith alohida modullarni izolyatsiyada yoki boshqa modullar bilan birga integration test qilish imkonini berishi aytiladi. (Home) Shuningdek, modullarni bo‘sh bog‘langan saqlash uchun ular orasida Spring application event’lari orqali aloqa qilish tavsiya qilinadi. (Home)
1. Spring Modulith nima?
Spring Modulith - bu microservice emas. Bu modular monolith qurish usuli.
Ya’ni:
bitta application
bitta deploy
bitta database bo‘lishi mumkin
lekin ichida aniq ajratilgan biznes modullar borMasalan:
marketplace-app
├── order
├── payment
├── delivery
├── catalog
├── customer
└── notificationHammasi bitta Spring Boot project ichida. Lekin order module payment module ichki classlarini xohlagancha ishlata olmaydi.
2. Nega Spring Modulith kerak?
Oddiy monolith boshida juda qulay:
Controller → Service → RepositoryLekin project kattalashganda shunday bo‘lib ketadi:
OrderService -> PaymentRepository
PaymentService -> DeliveryService
NotificationService -> OrderRepository
CatalogService -> PaymentServiceYa’ni hamma classlar bir-biriga bog‘lanib ketadi.
Natija:
bitta joyni o‘zgartirsak boshqa joy buziladi
testlar og‘irlashadi
yangi developer projectni tushunishga qiynaladi
microservice’ga ajratish deyarli imkonsiz bo‘ladi
dependency tartibsiz bo‘ladi
Spring Modulith shuni oldini oladi:
Monolith ichida microservice’ga o‘xshash intizom beradi, lekin deploy murakkabligini oshirmaydi.
3. Modular Monolith vs Microservices
Mezon | Modular Monolith | Microservices |
|---|---|---|
Deploy | Bitta application | Har service alohida |
Network call | Yo‘q yoki kam | Ko‘p |
Transaction | Osonroq | Murakkab |
Monitoring | Osonroq | Murakkabroq |
Team mustaqilligi | O‘rtacha | Yuqori |
Boshlash narxi | Pastroq | Yuqoriroq |
Architecture intizomi | Kerak | Juda kerak |
Arxitektor uchun muhim qoida:
Avval modular monolith qilib to‘g‘ri chegaralarni topish, keyin kerak bo‘lsa microservice’ga ajratish - ko‘p holatda xavfsizroq yo‘l.
4. Application Modules & Boundaries
Spring Modulith’da module odatda package orqali aniqlanadi.
Masalan:
com.company.shop
├── order
├── payment
├── delivery
└── notificationHar bir yuqori darajadagi package alohida application module sifatida qaraladi.
Yaxshi structure
com.company.shop.order
├── OrderManagement.java
├── OrderCreatedEvent.java
├── internal
│ ├── OrderService.java
│ ├── OrderRepository.java
│ └── OrderJpaEntity.java
└── web
└── OrderController.javaBu yerda:
Qism | Vazifa |
|---|---|
| boshqa modullar ko‘rishi mumkin bo‘lgan public API |
| faqat |
| REST controller yoki input adapter |
| boshqa modullarga chiqariladigan event |
| module facade/public service |
Yomon structure
com.company.shop
├── controller
├── service
├── repository
├── entity
└── dtoBu texnik qatlam bo‘yicha bo‘lingan. Katta project’da biznes chegaralar yo‘qoladi.
Yaxshiroq:
com.company.shop
├── order
├── payment
├── delivery
└── customerBu package-by-domain.
5. Public API va internal classlar
Spring Modulith’da eng muhim tushuncha:
Boshqa modul faqat public API’dan foydalansin, internal classlarga kirmasin.
Masalan payment module:
payment
├── PaymentManagement.java
├── PaymentCompletedEvent.java
└── internal
├── PaymentService.java
├── PaymentRepository.java
└── PaymentProcessor.javaorder module quyidagini ishlatishi mumkin:
payment.PaymentManagement
payment.PaymentCompletedEventLekin buni ishlatmasligi kerak:
payment.internal.PaymentService
payment.internal.PaymentRepositoryChunki internal - modulning ichki implementatsiyasi.
6. Module facade
Module facade - boshqa modullar bilan gaplashish uchun public contract.
Masalan:
package com.company.shop.payment;
public interface PaymentManagement {
PaymentResult pay(PaymentRequest request);
}Implementation esa internal ichida turadi:
package com.company.shop.payment.internal;
import com.company.shop.payment.PaymentManagement;
@Service
class PaymentService implements PaymentManagement {
@Override
public PaymentResult pay(PaymentRequest request) {
// payment business logic
return new PaymentResult(true);
}
}Bu yondashuv yaxshi, chunki boshqa module faqat PaymentManagement bilan ishlaydi.
7. Inter-module communication
Modullar ikki xil yo‘l bilan gaplashadi:
1. Synchronous call
Bir module boshqa module’ning public API’sini chaqiradi.
@Service
class OrderService {
private final PaymentManagement paymentManagement;
OrderService(PaymentManagement paymentManagement) {
this.paymentManagement = paymentManagement;
}
void createOrder(CreateOrderCommand command) {
// order yaratish
paymentManagement.pay(new PaymentRequest(command.customerId(), command.amount()));
}
}Bu oddiy va tushunarli. Lekin coupling kuchliroq bo‘ladi.
2. Event-based communication
Bir module event chiqaradi, boshqa module uni eshitadi.
public record OrderCreatedEvent(
Long orderId,
Long customerId,
BigDecimal totalAmount
) {}Order module event publish qiladi:
@Service
class OrderService {
private final ApplicationEventPublisher events;
OrderService(ApplicationEventPublisher events) {
this.events = events;
}
@Transactional
void createOrder(CreateOrderCommand command) {
Order order = Order.create(command.customerId(), command.items());
// save order...
events.publishEvent(new OrderCreatedEvent(
order.getId(),
order.getCustomerId(),
order.getTotalAmount()
));
}
}Payment module event’ni eshitadi:
@Component
class PaymentOnOrderCreated {
@EventListener
void on(OrderCreatedEvent event) {
// payment boshlash
}
}Bu yondashuvda order module payment module borligini bilmaydi.
8. @ApplicationModuleListener
Spring Modulith’da event listener uchun maxsus annotation ishlatiladi:
@ApplicationModuleListener
void on(OrderCreatedEvent event) {
// boshqa module eventiga reaksiya
}Bu oddiy @EventListenerdan kuchliroq. Odatda quyidagi maqsadlarda ishlatiladi:
module’lar orasidagi event handling
transaction tugagandan keyin eventni qayta ishlash
async processing
module boundary’ni toza saqlash
Arxitektor nuqtayi nazaridan:
Agar bir module boshqa module’ning ichki service’ini chaqirmasdan event orqali ishlasa, coupling kamayadi.
9. Real flow: Order → Payment → Delivery
Online shop misoli:
1. User order yaratadi
2. Order module OrderCreatedEvent chiqaradi
3. Payment module eventni eshitadi
4. Payment muvaffaqiyatli bo‘lsa PaymentCompletedEvent chiqaradi
5. Delivery module PaymentCompletedEvent’ni eshitadi
6. Delivery boshlanadiStructure:
shop
├── order
│ ├── OrderManagement.java
│ ├── OrderCreatedEvent.java
│ └── internal
│ └── OrderService.java
│
├── payment
│ ├── PaymentCompletedEvent.java
│ └── internal
│ └── PaymentService.java
│
└── delivery
└── internal
└── DeliveryService.javaFlow:
OrderCreatedEvent
↓
Payment module
↓
PaymentCompletedEvent
↓
Delivery moduleBu hali microservice emas. Lekin architecture microservice’ga o‘xshab toza.
10. Module verification
Spring Modulith’ning eng katta foydasi - module boundary test.
Masalan:
class ModularityTests {
ApplicationModules modules =
ApplicationModules.of(ShopApplication.class);
@Test
void verifiesModularStructure() {
modules.verify();
}
}Bu test quyidagilarni tekshiradi:
module’lar orasida cyclic dependency bormi
internal package tashqaridan ishlatilganmi
module ruxsat berilmagan module’ga bog‘langanmi
package structure buzilganmi
Masalan, order module buni qilsa:
import com.company.shop.payment.internal.PaymentService;test yiqiladi.
Bu juda kuchli narsa:
Architecture faqat diagrammada emas, test bilan majburlanadi.
11. Module integration tests
Oddiy Spring Boot test ko‘pincha butun application context’ni ko‘taradi:
@SpringBootTest
class OrderTest {
}Katta project’da bu sekin.
Spring Modulith bilan faqat bitta modulni test qilish mumkin:
@ApplicationModuleTest
class OrderModuleTest {
@Test
void createsOrder() {
// faqat order module konteksti
}
}Rasmiy hujjatlarda Spring Modulith application module’larni izolyatsiyada yoki boshqa modullar bilan kombinatsiyada integration test qilish imkonini berishi ko‘rsatilgan. (Home)
Foydasi:
test tezroq
module mustaqilligi tekshiriladi
hidden dependency topiladi
architecture real ishlashi isbotlanadi
12. Published events testing
Event chiqdimi yoki yo‘qmi - test qilish mumkin.
Masalan:
@ApplicationModuleTest
class OrderModuleTest {
@Test
void publishesOrderCreatedEvent(
PublishedEvents events,
OrderManagement orders
) {
orders.createOrder(new CreateOrderCommand(...));
assertThat(events)
.contains(OrderCreatedEvent.class);
}
}Bu nima beradi?
Order yaratish use case’i payment’ni bevosita chaqirmaydi, faqat event chiqaradi. Test shuni isbotlaydi.
Spring Modulith PublishedEvents orqali test davomida publish qilingan eventlarni tekshirishga imkon beradi. (Home)
13. Documenting module structure
Spring Modulith module structure’ni dokumentatsiya qilishga ham yordam beradi.
Masalan:
@Test
void writeDocumentation() {
ApplicationModules modules =
ApplicationModules.of(ShopApplication.class);
new Documenter(modules)
.writeModulesAsPlantUml()
.writeIndividualModulesAsPlantUml();
}Natijada module dependency diagrammalar olish mumkin.
Bu arxitektor uchun foydali:
yangi developer projectni tez tushunadi
real dependency ko‘rinadi
diagramma koddan generate bo‘ladi
eski qo‘lda chizilgan diagrammadan ko‘ra ishonchliroq
14. Spring Modulith va DDD
Spring Modulith DDD bilan juda yaxshi mos keladi.
DDD’da bounded context bor:
Order Context
Payment Context
Delivery Context
Customer ContextSpring Modulith’da ular package/module sifatida ajratiladi:
com.company.shop.order
com.company.shop.payment
com.company.shop.delivery
com.company.shop.customerYa’ni:
DDD tushuncha | Spring Modulith’da |
|---|---|
Bounded Context | Application Module |
Domain Event | Spring Application Event |
Context API | Module public API |
Internal model |
|
Context boundary | Module verification |
15. Modulith’dan microservice’ga o‘tish
Spring Modulith’ning kuchli tomoni:
Avval module chegaralarini bitta application ichida to‘g‘ri topasiz, keyin kerak bo‘lsa alohida service qilib ajratasiz.
Masalan boshida:
shop-app
├── order
├── payment
└── deliveryKeyin traffic oshsa yoki team alohida ishlashi kerak bo‘lsa:
order-service
payment-service
delivery-serviceAgar oldindan module boundary yaxshi bo‘lsa, ajratish osonroq bo‘ladi.
Agar boshidan hamma joy aralash bo‘lsa:
OrderService -> PaymentRepository
PaymentService -> OrderRepository
DeliveryService -> PaymentServicemicroservice’ga ajratish juda og‘riqli bo‘ladi.
16. Qachon Spring Modulith ishlatish kerak?
Ishlatish yaxshi
project o‘rta yoki katta bo‘lsa
biznes domain aniq modullarga bo‘linsa
microservice qilishga shoshilmasak
monolith ichida tartib kerak bo‘lsa
jamoa bitta codebase’da ishlasa
module boundary’ni test bilan nazorat qilmoqchi bo‘lsangiz
Shart emas
juda kichik CRUD project
MVP juda tez yozilishi kerak bo‘lsa
domain hali aniq bo‘lmasa
jamoa package-by-domain’ni ham tushunmasa
architecture intizomi talab qilinmasa
17. Eng ko‘p xatolar
Xato 1: Module nomini texnik qilish
Yomon:
controller
service
repositoryYaxshi:
order
payment
delivery
customerXato 2: Internal classlarni tashqaridan ishlatish
Yomon:
import com.company.shop.payment.internal.PaymentService;Yaxshi:
import com.company.shop.payment.PaymentManagement;yoki event:
OrderCreatedEventXato 3: Event’ni hamma narsaga ishlatish
Event yaxshi, lekin hamma narsa event bo‘lishi shart emas.
Masalan, real vaqt javob kerak bo‘lsa:
Order -> Payment public API callAgar loose coupling kerak bo‘lsa:
OrderCreatedEvent -> Payment listenerXato 4: Bitta module ichida hamma narsani saqlash
Yomon:
core
common
sharedAgar hamma narsa commonga ketaversa, module boundary buziladi.
Xato 5: Modulith’ni microservice deb o‘ylash
Spring Modulith:
bitta process
bitta deploy
module boundary ichki darajadaMicroservice:
alohida process
alohida deploy
network communication
alohida observability18. Amaliy structure - tavsiya
com.company.shop
├── order
│ ├── OrderManagement.java
│ ├── OrderCreatedEvent.java
│ ├── package-info.java
│ └── internal
│ ├── OrderService.java
│ ├── OrderRepository.java
│ └── OrderEntity.java
│
├── payment
│ ├── PaymentManagement.java
│ ├── PaymentCompletedEvent.java
│ ├── package-info.java
│ └── internal
│ ├── PaymentService.java
│ └── PaymentRepository.java
│
├── delivery
│ ├── DeliveryManagement.java
│ ├── package-info.java
│ └── internal
│ └── DeliveryService.java
│
└── notification
├── NotificationManagement.java
└── internal
└── NotificationService.java19. Minimal dependency
Maven misol:
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-test</artifactId>
<scope>test</scope>
</dependency>Test starter rasmiy hujjatlarda module integration testing uchun ishlatilishi ko‘rsatilgan. (Home)
20. Arxitektor xulosasi
Spring Modulith’ning asosiy qiymati:
Monolith qulayligini saqlaydi.
Microservice intizomini beradi.
Module boundary’ni test bilan tekshiradi.
Event orqali coupling’ni kamaytiradi.
Keyinchalik microservice’ga o‘tishni osonlashtiradi.Eng muhim qoida:
Spring Modulith ishlatishdan maqsad annotation qo‘shish emas. Maqsad - biznes modullar orasidagi chegarani kodda ham, testda ham, dokumentatsiyada ham aniq qilish.