Architecture with Spring

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

Arxitektor darajasida Spring’ni bilish - bu faqat @Service, @Repository, @Controller yozish emas. Bu katta tizimni qanday bo‘lish, qaysi joyda qaysi pattern ishlatish, modullar orasidagi bog‘liqlikni boshqarish, kelajakda microservice yoki modulith qilishga tayyorlash degani.

Architecture with Spring: Hexagonal Architecture, Modular Monolith, CQRS, Event Sourcing, DDD mapping va package-by-domain enforcement mavzularidan iborat.


1. Architecture with Spring nima?

Architecture with Spring - Spring Boot yordamida application’ni shunchaki ishlaydigan qilib emas, balki:

  • tushunarli

  • test qilish oson

  • o‘zgartirishga qulay

  • biznesga mos

  • katta jamoada boshqariladigan

  • kelajakda bo‘linadigan

qilib loyihalashdir.

Oddiy developer savoli:

"Bu endpoint ishlaydimi?"

Arxitektor savoli:

"Bu tizim 2 yildan keyin ham oson o‘zgaradimi? Yangi modul qo‘shsak eski joylar buzilmaydimi? Biznes qoidalar framework’ga qaram bo‘lib qolmadimi?"


2. Oddiy layered architecture yetmay qoladigan joy

Junior/Middle bosqichida ko‘p ishlatiladigan tuzilma:

controller
service
repository
entity
dto

Masalan:

src/main/java/com/example/app
 ├── controller
 │    └── OrderController.java
 ├── service
 │    └── OrderService.java
 ├── repository
 │    └── OrderRepository.java
 ├── entity
 │    └── Order.java
 └── dto
      └── OrderRequest.java

Kichik project uchun yaxshi. Lekin katta project’da muammo chiqadi:

controller ichida hamma domainlar aralashadi
service ichida biznes logic ko‘payib ketadi
repository to‘g‘ridan-to‘g‘ri hamma joyga kiradi
entity biznes modeli o‘rniga database modeli bo‘lib qoladi
modullar orasida chegaralar yo‘qoladi

Natija:

Project ishlaydi, lekin o‘zgartirish xavfli bo‘ladi.

Arxitektor darajada maqsad - faqat qatlamlar emas, biznes chegaralarini ham to‘g‘ri ajratish.


3. Hexagonal Architecture in Spring Boot

Asosiy g‘oya

Hexagonal Architecture yoki Ports and Adapters Architecture shuni aytadi:

Biznes logic tashqi texnologiyalarga qaram bo‘lmasligi kerak.

Ya’ni biznes qoidalar:

  • Spring MVC’ga qaram bo‘lmasin

  • JPA’ga qaram bo‘lmasin

  • Kafka’ga qaram bo‘lmasin

  • Redis’ga qaram bo‘lmasin

  • tashqi API’ga qaram bo‘lmasin

Spring, database, Kafka, REST API - bular adapter. Markazda esa domain turadi.


Oddiy ko‘rinish

                REST Controller
                      |
                      v
               Input Adapter
                      |
                      v
                Application Service
                      |
                      v
                   Domain
                      |
                      v
              Output Port Interface
                      |
                      v
              JPA / Kafka / External API Adapter

Package structure misol

com.company.order
 ├── domain
 │    ├── Order.java
 │    ├── OrderItem.java
 │    └── OrderStatus.java
 │
 ├── application
 │    ├── port
 │    │    ├── in
 │    │    │    └── CreateOrderUseCase.java
 │    │    └── out
 │    │         └── OrderRepositoryPort.java
 │    └── service
 │         └── CreateOrderService.java
 │
 ├── adapter
 │    ├── in
 │    │    └── web
 │    │         └── OrderController.java
 │    └── out
 │         └── persistence
 │              ├── JpaOrderRepository.java
 │              ├── SpringDataOrderRepository.java
 │              └── OrderEntity.java

Nima uchun bu yaxshi?

Chunki CreateOrderService database qanday ishlashini bilmaydi.

U faqat interface bilan ishlaydi:

public interface OrderRepositoryPort {
    Order save(Order order);
    Optional<Order> findById(OrderId id);
}

Service:

@Service
public class CreateOrderService implements CreateOrderUseCase {

    private final OrderRepositoryPort orderRepository;

    public CreateOrderService(OrderRepositoryPort orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Override
    public Order create(CreateOrderCommand command) {
        Order order = Order.create(command.customerId(), command.items());
        return orderRepository.save(order);
    }
}

JPA adapter:

@Repository
public class JpaOrderRepository implements OrderRepositoryPort {

    private final SpringDataOrderRepository repository;

    public JpaOrderRepository(SpringDataOrderRepository repository) {
        this.repository = repository;
    }

    @Override
    public Order save(Order order) {
        OrderEntity entity = OrderMapper.toEntity(order);
        OrderEntity saved = repository.save(entity);
        return OrderMapper.toDomain(saved);
    }
}

Bu yerda muhim nuqta:

Domain va application service JPA’ni bilmaydi. JPA tashqi adapter.


4. Spring Boot’da Hexagonal Architecture qachon kerak?

Kerak bo‘ladi:

  • biznes logic murakkab bo‘lsa

  • project uzoq yashasa

  • testability muhim bo‘lsa

  • database keyin almashishi mumkin bo‘lsa

  • tashqi integratsiyalar ko‘p bo‘lsa

  • microservice darajasidagi service bo‘lsa

Kerak bo‘lmasligi mumkin:

  • oddiy CRUD admin panel

  • 5-10 ta endpointli kichik project

  • tez MVP qilish kerak bo‘lsa

  • biznes logic deyarli yo‘q bo‘lsa

Arxitektor qarori shunday bo‘ladi:

Har joyga Hexagonal tiqish shart emas. Lekin biznes logic muhim joyda juda foydali.


5. Modular Monolith with Spring Modulith

Modular Monolith nima?

Monolith degani bitta application. Lekin modular monolith degani:

Application bitta deploy bo‘ladi, lekin ichida aniq ajratilgan biznes modullar bo‘ladi.

Masalan marketplace:

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

Bu hali microservice emas. Hammasi bitta Spring Boot app ichida. Lekin har modul o‘z chegarasiga ega.


Oddiy monolith muammosi

OrderService -> PaymentRepository
PaymentService -> UserRepository
UserService -> OrderRepository
NotificationService -> hammasiga bog‘langan

Vaqt o‘tishi bilan:

hamma hammani chaqiradi
dependency tartibsiz bo‘ladi
o‘zgartirish xavfli bo‘ladi
microservice qilish juda qiyinlashadi

Modular monolith yondashuvi

order module faqat order logic’ni biladi
payment module faqat payment logic’ni biladi
delivery module faqat delivery logic’ni biladi

modullar bir-biri bilan to‘g‘ridan-to‘g‘ri ichki class orqali emas,
public API yoki event orqali gaplashadi

Spring Modulith nima beradi?

Spring Modulith quyidagilarga yordam beradi:

  • application module chegaralarini tekshiradi

  • module dependency’larini nazorat qiladi

  • module integration test yozishga yordam beradi

  • event-based communication’ni tartibga soladi

  • modul strukturasini dokumentatsiya qilishga yordam beradi

Misol structure:

com.company.marketplace
 ├── order
 │    ├── OrderManagement.java
 │    ├── internal
 │    │    └── OrderService.java
 │    └── events
 │         └── OrderCreatedEvent.java
 │
 ├── payment
 │    ├── PaymentManagement.java
 │    └── internal
 │         └── PaymentService.java
 │
 └── delivery
      ├── DeliveryManagement.java
      └── internal
           └── DeliveryService.java

Bu yerda internal ichidagi classlarni boshqa module to‘g‘ridan-to‘g‘ri ishlatmasligi kerak.


6. CQRS implementation patterns in Spring

CQRS nima?

CQRS - Command Query Responsibility Segregation.

Ya’ni:

Ma’lumotni o‘zgartirish va ma’lumotni o‘qish alohida model bilan ishlaydi.

Oddiy CRUD’da:

OrderService
 ├── createOrder()
 ├── updateOrder()
 ├── cancelOrder()
 ├── getOrder()
 └── listOrders()

CQRS’da:

Command side:
 ├── CreateOrderCommandHandler
 ├── CancelOrderCommandHandler
 └── PayOrderCommandHandler

Query side:
 ├── GetOrderQueryHandler
 └── SearchOrdersQueryHandler

Nima uchun CQRS kerak?

Chunki write va read ehtiyojlari har xil bo‘ladi.

Masalan:

Write side

Buyurtma yaratish
Payment tekshirish
Stock kamaytirish
Domain validation
Transaction
Event chiqarish

Read side

Buyurtmalar ro‘yxati
Filter
Search
Pagination
Projection
Dashboard uchun tez o‘qish

Bitta model bilan ikkalasini qilish ba’zida projectni og‘irlashtiradi.


Spring Boot’da oddiy CQRS misol

order
 ├── command
 │    ├── CreateOrderCommand.java
 │    ├── CreateOrderHandler.java
 │    └── CancelOrderHandler.java
 │
 ├── query
 │    ├── GetOrderQuery.java
 │    ├── GetOrderHandler.java
 │    └── OrderViewRepository.java
 │
 └── domain
      └── Order.java

Command:

public record CreateOrderCommand(
        Long customerId,
        List<Long> productIds
) {}

Handler:

@Service
public class CreateOrderHandler {

    private final OrderRepositoryPort orderRepository;

    public CreateOrderHandler(OrderRepositoryPort orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Transactional
    public Long handle(CreateOrderCommand command) {
        Order order = Order.create(command.customerId(), command.productIds());
        orderRepository.save(order);
        return order.getId();
    }
}

Query:

@Service
public class GetOrderHandler {

    private final OrderViewRepository repository;

    public GetOrderHandler(OrderViewRepository repository) {
        this.repository = repository;
    }

    public OrderView handle(Long orderId) {
        return repository.findViewById(orderId)
                .orElseThrow(() -> new OrderNotFoundException(orderId));
    }
}

CQRS har doim kerakmi?

Yo‘q.

CQRS foydali bo‘ladi:

  • read/write logic juda farq qilsa

  • reporting ko‘p bo‘lsa

  • dashboard og‘ir bo‘lsa

  • event-driven system bo‘lsa

  • write model murakkab domain bo‘lsa

Kerak emas:

  • oddiy CRUD

  • kichik admin panel

  • kam endpointli service

  • jamoa tajribasi yetarli bo‘lmasa


7. Event Sourcing with Spring & Axon Framework

Event Sourcing nima?

Oddiy tizimda database’da oxirgi holat saqlanadi:

orders table:
id = 1
status = PAID
total = 300000

Event sourcing’da esa holat emas, voqealar tarixi saqlanadi:

OrderCreated
OrderItemAdded
PaymentStarted
PaymentCompleted
OrderConfirmed

Order’ning hozirgi holati shu eventlardan qayta tiklanadi.


Oddiy holat vs Event Sourcing

Oddiy state-based model

Order status = DELIVERED

Lekin savol:

Qachon yaratildi?
Kim to‘ladi?
Qachon status o‘zgardi?
Nega cancel bo‘lmadi?

Bular uchun alohida audit kerak bo‘ladi.

Event Sourcing

1. OrderCreated
2. PaymentCompleted
3. DeliveryStarted
4. OrderDelivered

Tarixning o‘zi asosiy ma’lumot bo‘ladi.


Qachon Event Sourcing kerak?

Kerak bo‘ladi:

  • audit juda muhim bo‘lsa

  • moliya/payment tizimlari

  • banking

  • order lifecycle murakkab bo‘lsa

  • voqealar tarixi biznes uchun qimmatli bo‘lsa

  • state’ni qayta tiklash kerak bo‘lsa

Kerak bo‘lmasligi mumkin:

  • oddiy CRUD

  • report-only tizim

  • kichik project

  • jamoa event sourcing tajribasiga ega bo‘lmasa


Axon Framework nima qiladi?

Axon Framework Java/Spring ekotizimida:

  • command handling

  • event publishing

  • event store

  • aggregate

  • saga

  • CQRS

  • event sourcing

kabi narsalarni tartibli qilishga yordam beradi.

Lekin Arxitektor qaror:

Axon kuchli, lekin murakkab. Uni faqat haqiqiy ehtiyoj bo‘lsa tanlash kerak.


8. DDD mapping to Spring layers

DDD nima?

DDD - Domain-Driven Design.

Asosiy g‘oya:

Kod strukturasi biznes tiliga mos bo‘lishi kerak.

Masalan, e-commerce’da biznes so‘zlari:

Order
Payment
Customer
Product
Cart
Delivery
Invoice
Refund

Kod ham shularga qarab tuzilishi kerak.


Noto‘g‘ri yondashuv

controller
service
repository
entity
dto

Bu texnik qatlamlarga qarab bo‘lingan. Katta project’da biznes context yo‘qoladi.


Yaxshiroq yondashuv: package-by-domain

com.company.marketplace
 ├── order
 │    ├── domain
 │    ├── application
 │    ├── adapter
 │    └── api
 │
 ├── payment
 │    ├── domain
 │    ├── application
 │    ├── adapter
 │    └── api
 │
 └── delivery
      ├── domain
      ├── application
      ├── adapter
      └── api

Bu yerda project biznes bo‘yicha o‘qiladi.


DDD elementlari

Entity

Identity bor obyekt.

public class Order {
    private OrderId id;
    private CustomerId customerId;
    private OrderStatus status;
}

Value Object

Identity yo‘q, qiymati bilan teng.

public record Money(BigDecimal amount, String currency) {}

Aggregate

Bir nechta entity/value object’larni boshqaradigan domain chegarasi.

public class Order {
    private List<OrderItem> items;

    public void addItem(Product product, int quantity) {
        // business rule here
    }

    public void confirm() {
        if (items.isEmpty()) {
            throw new OrderCannotBeConfirmedException();
        }
        this.status = OrderStatus.CONFIRMED;
    }
}

Repository

Domain obyektni saqlash interface’i.

public interface OrderRepository {
    Order save(Order order);
    Optional<Order> findById(OrderId id);
}

Muhim:

DDD’da repository database table uchun emas, aggregate uchun ishlaydi.


9. Package-by-domain enforcement

Muammo

Developerlar vaqt o‘tib qoidani buzadi:

payment.internal.PaymentService

ni order module ichidan chaqirib yuboradi.

Yoki:

order.repository.OrderRepository

ni boshqa module ishlatib yuboradi.

Natija:

module boundary buziladi
tight coupling paydo bo‘ladi
microservice qilish qiyinlashadi
testlar murakkablashadi

Buni qanday nazorat qilamiz?

1. Package convention

order.internal faqat order ichida ishlatiladi
order.api boshqa module uchun public contract

2. ArchUnit tests

ArchUnit bilan architecture qoidalarini test qilamiz.

Misol:

@AnalyzeClasses(packages = "com.company.marketplace")
public class ArchitectureTest {

    @ArchTest
    static final ArchRule order_should_not_access_payment_internal =
            noClasses()
                    .that().resideInAPackage("..order..")
                    .should().accessClassesThat()
                    .resideInAPackage("..payment.internal..");
}

Bu test nima qiladi?

order module payment.internal ichidagi classlarni ishlatsa, test yiqiladi.

Bu Arxitektor darajasida juda muhim.


10. Architecture with Spring uchun ideal structure

Katta, biznes logic ko‘p project uchun yaxshi structure:

com.company.marketplace
 ├── order
 │    ├── api
 │    │    ├── OrderFacade.java
 │    │    └── OrderCreatedEvent.java
 │    │
 │    ├── application
 │    │    ├── command
 │    │    ├── query
 │    │    └── service
 │    │
 │    ├── domain
 │    │    ├── Order.java
 │    │    ├── OrderItem.java
 │    │    ├── OrderStatus.java
 │    │    └── OrderRepository.java
 │    │
 │    ├── adapter
 │    │    ├── in
 │    │    │    └── web
 │    │    └── out
 │    │         ├── persistence
 │    │         └── messaging
 │    │
 │    └── internal
 │         └── OrderInternalService.java
 │
 ├── payment
 ├── delivery
 └── notification

Bu structure’da:

Qism

Vazifasi

api

boshqa modullar foydalanishi mumkin bo‘lgan public contract

application

use case, command, query, orchestration

domain

biznes model va biznes qoidalar

adapter/in

REST, Kafka consumer, scheduler

adapter/out

JPA, Redis, Kafka producer, external API

internal

faqat shu modul ichida ishlatiladigan classlar


11. Arxitektor qanday qaror qiladi?

Arxitektor har doim pattern tanlashdan oldin savol beradi:

1. Biznes murakkabmi?

Agar faqat CRUD bo‘lsa:

Controller → Service → Repository

yetadi.

Agar domain murakkab bo‘lsa:

Hexagonal + DDD

yaxshi.


2. System kelajakda bo‘linadimi?

Agar kelajakda microservice qilish ehtimoli bo‘lsa:

Modular Monolith + Spring Modulith

juda yaxshi start.


3. Read/write load farq qiladimi?

Agar read juda ko‘p, write murakkab bo‘lsa:

CQRS

ko‘rib chiqiladi.


4. Audit va tarix muhimmi?

Agar har bir o‘zgarish tarixi muhim bo‘lsa:

Event Sourcing

ko‘rib chiqiladi.


5. Team buni ko‘tara oladimi?

Eng muhim savol:

Pattern chiroyli, lekin jamoa tushunmasa project battar murakkablashadi.

Arxitektor faqat texnik emas, jamoa darajasini ham hisobga oladi.


12. Real hayotiy misol: Online Shop

Oddiy variant:

OrderController
OrderService
OrderRepository
PaymentService
DeliveryService

Tez yoziladi, lekin katta bo‘lsa chalkashadi.


Arxitektor varianti:

order module
 ├── CreateOrderUseCase
 ├── Order aggregate
 ├── OrderRepositoryPort
 ├── JpaOrderAdapter
 └── OrderCreatedEvent

payment module
 ├── PaymentFacade
 ├── Payment aggregate
 └── PaymentCompletedEvent

delivery module
 ├── DeliveryFacade
 └── DeliveryStartedEvent

Flow:

1. User order yaratadi
2. Order module OrderCreatedEvent chiqaradi
3. Payment module eventni eshitadi
4. Payment tugasa PaymentCompletedEvent chiqadi
5. Delivery module delivery boshlaydi

Bu yondashuvda modullar kuchli ajratilgan bo‘ladi.


13. Eng ko‘p qilinadigan xatolar

Xato 1: Har joyga Hexagonal Architecture ishlatish

Kichik CRUD project’da bu ortiqcha bo‘lishi mumkin.


Xato 2: Domain modelni JPA entity bilan aralashtirish

Ba’zida mumkin, lekin murakkab domain’da yaxshi emas.

@Entity
public class Order {
    // biznes logic + JPA annotation + relation + serialization
}

Bu class haddan tashqari ko‘p mas’uliyat oladi.


Xato 3: Service classni "God Service" qilish

OrderService
 ├── createOrder()
 ├── payOrder()
 ├── deliverOrder()
 ├── refundOrder()
 ├── sendNotification()
 ├── updateStock()
 └── generateInvoice()

Bu yomon signal.

Yaxshiroq:

CreateOrderHandler
PayOrderHandler
CancelOrderHandler
RefundOrderHandler

Xato 4: Modullar orasida ichki classlarni chaqirish

order -> payment.internal.PaymentService

Bu boundary buzadi.

Yaxshiroq:

order -> payment.api.PaymentFacade

yoki event orqali:

OrderCreatedEvent -> Payment module

Xato 5: Patternni biznes ehtiyojsiz tanlash

CQRS, Event Sourcing, Hexagonal, DDD - bular kuchli. Lekin noto‘g‘ri joyda ishlatilsa projectni og‘irlashtiradi.


14. Qisqa qoida

Arxitektor darajada Spring architecture uchun asosiy qoida:

Business logic framework’dan mustaqil bo‘lsin.
Modullar chegarasi aniq bo‘lsin.
Ichki classlar tashqariga sizib chiqmasin.
Read va write murakkab bo‘lsa ajratilsin.
Architecture test bilan nazorat qilinsin.

15. Amaliy mini roadmap

Bu mavzuni o‘rganishdagi ketma-ketlik:

  1. Layered Architecture'ni yaxshi tushunish

  2. Package-by-feature / package-by-domain qilish

  3. DDD basics: Entity, Value Object, Aggregate, Repository

  4. Hexagonal Architecture: port va adapter

  5. Modular Monolith: module boundary

  6. Spring Modulith bilan module tekshirish

  7. CQRS: command/query ajratish

  8. Event Sourcing: faqat kerak bo‘lsa

  9. ArchUnit bilan architecture qoidalarini test qilish


Xulosa

Architecture with Spring - bu Spring Boot project’ni professional, uzoq yashaydigan va oson kengayadigan qilib qurish san’ati.

Boshlang'ich Spring’da endpoint yozadi.
Junior layer ajratadi.
Middle transaction, security, caching, testingni chuqur biladi.
Senior performance va distributed system bilan ishlaydi.
Arxitektor esa quyidagini hal qiladi:

"Bu tizimning chegaralari qayerda? Qaysi module nima uchun javobgar? Framework biznes logicni bosib ketmadimi? 2 yildan keyin bu projectni bo‘lish, kengaytirish, test qilish mumkinmi?"