Spring Modulith

02.07.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Spring Boot | 16 daqiqa o'qish

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 bor

Masalan:

marketplace-app
 ├── order
 ├── payment
 ├── delivery
 ├── catalog
 ├── customer
 └── notification

Hammasi 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 → Repository

Lekin project kattalashganda shunday bo‘lib ketadi:

OrderService -> PaymentRepository
PaymentService -> DeliveryService
NotificationService -> OrderRepository
CatalogService -> PaymentService

Ya’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
 └── notification

Har 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.java

Bu yerda:

Qism

Vazifa

order package root

boshqa modullar ko‘rishi mumkin bo‘lgan public API

internal

faqat order module ichida ishlatiladi

web

REST controller yoki input adapter

OrderCreatedEvent

boshqa modullarga chiqariladigan event

OrderManagement

module facade/public service


Yomon structure

com.company.shop
 ├── controller
 ├── service
 ├── repository
 ├── entity
 └── dto

Bu texnik qatlam bo‘yicha bo‘lingan. Katta project’da biznes chegaralar yo‘qoladi.

Yaxshiroq:

com.company.shop
 ├── order
 ├── payment
 ├── delivery
 └── customer

Bu 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.java

order module quyidagini ishlatishi mumkin:

payment.PaymentManagement
payment.PaymentCompletedEvent

Lekin buni ishlatmasligi kerak:

payment.internal.PaymentService
payment.internal.PaymentRepository

Chunki 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 boshlanadi

Structure:

shop
 ├── order
 │    ├── OrderManagement.java
 │    ├── OrderCreatedEvent.java
 │    └── internal
 │         └── OrderService.java
 │
 ├── payment
 │    ├── PaymentCompletedEvent.java
 │    └── internal
 │         └── PaymentService.java
 │
 └── delivery
      └── internal
           └── DeliveryService.java

Flow:

OrderCreatedEvent
        ↓
Payment module
        ↓
PaymentCompletedEvent
        ↓
Delivery module

Bu 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 Context

Spring Modulith’da ular package/module sifatida ajratiladi:

com.company.shop.order
com.company.shop.payment
com.company.shop.delivery
com.company.shop.customer

Ya’ni:

DDD tushuncha

Spring Modulith’da

Bounded Context

Application Module

Domain Event

Spring Application Event

Context API

Module public API

Internal model

internal package

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
 └── delivery

Keyin traffic oshsa yoki team alohida ishlashi kerak bo‘lsa:

order-service
payment-service
delivery-service

Agar oldindan module boundary yaxshi bo‘lsa, ajratish osonroq bo‘ladi.

Agar boshidan hamma joy aralash bo‘lsa:

OrderService -> PaymentRepository
PaymentService -> OrderRepository
DeliveryService -> PaymentService

microservice’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
repository

Yaxshi:

order
payment
delivery
customer

Xato 2: Internal classlarni tashqaridan ishlatish

Yomon:

import com.company.shop.payment.internal.PaymentService;

Yaxshi:

import com.company.shop.payment.PaymentManagement;

yoki event:

OrderCreatedEvent

Xato 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 call

Agar loose coupling kerak bo‘lsa:

OrderCreatedEvent -> Payment listener

Xato 4: Bitta module ichida hamma narsani saqlash

Yomon:

core
common
shared

Agar hamma narsa commonga ketaversa, module boundary buziladi.


Xato 5: Modulith’ni microservice deb o‘ylash

Spring Modulith:

bitta process
bitta deploy
module boundary ichki darajada

Microservice:

alohida process
alohida deploy
network communication
alohida observability

18. 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.java

19. 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.