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
dtoMasalan:
src/main/java/com/example/app
├── controller
│ └── OrderController.java
├── service
│ └── OrderService.java
├── repository
│ └── OrderRepository.java
├── entity
│ └── Order.java
└── dto
└── OrderRequest.javaKichik 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‘qoladiNatija:
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.javaNima 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
└── catalogBu 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‘langanVaqt o‘tishi bilan:
hamma hammani chaqiradi
dependency tartibsiz bo‘ladi
o‘zgartirish xavfli bo‘ladi
microservice qilish juda qiyinlashadiModular 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 gaplashadiSpring 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.javaBu 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
└── SearchOrdersQueryHandlerNima 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 chiqarishRead side
Buyurtmalar ro‘yxati
Filter
Search
Pagination
Projection
Dashboard uchun tez o‘qishBitta 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.javaCommand:
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 = 300000Event sourcing’da esa holat emas, voqealar tarixi saqlanadi:
OrderCreated
OrderItemAdded
PaymentStarted
PaymentCompleted
OrderConfirmedOrder’ning hozirgi holati shu eventlardan qayta tiklanadi.
Oddiy holat vs Event Sourcing
Oddiy state-based model
Order status = DELIVEREDLekin 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. OrderDeliveredTarixning 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
RefundKod ham shularga qarab tuzilishi kerak.
Noto‘g‘ri yondashuv
controller
service
repository
entity
dtoBu 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
└── apiBu 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.PaymentServiceni order module ichidan chaqirib yuboradi.
Yoki:
order.repository.OrderRepositoryni boshqa module ishlatib yuboradi.
Natija:
module boundary buziladi
tight coupling paydo bo‘ladi
microservice qilish qiyinlashadi
testlar murakkablashadiBuni qanday nazorat qilamiz?
1. Package convention
order.internal faqat order ichida ishlatiladi
order.api boshqa module uchun public contract2. 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?
ordermodulepayment.internalichidagi 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
└── notificationBu structure’da:
Qism | Vazifasi |
|---|---|
| boshqa modullar foydalanishi mumkin bo‘lgan public contract |
| use case, command, query, orchestration |
| biznes model va biznes qoidalar |
| REST, Kafka consumer, scheduler |
| JPA, Redis, Kafka producer, external API |
| 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 → Repositoryyetadi.
Agar domain murakkab bo‘lsa:
Hexagonal + DDDyaxshi.
2. System kelajakda bo‘linadimi?
Agar kelajakda microservice qilish ehtimoli bo‘lsa:
Modular Monolith + Spring Modulithjuda yaxshi start.
3. Read/write load farq qiladimi?
Agar read juda ko‘p, write murakkab bo‘lsa:
CQRSko‘rib chiqiladi.
4. Audit va tarix muhimmi?
Agar har bir o‘zgarish tarixi muhim bo‘lsa:
Event Sourcingko‘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
DeliveryServiceTez 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
└── DeliveryStartedEventFlow:
1. User order yaratadi
2. Order module OrderCreatedEvent chiqaradi
3. Payment module eventni eshitadi
4. Payment tugasa PaymentCompletedEvent chiqadi
5. Delivery module delivery boshlaydiBu 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
RefundOrderHandlerXato 4: Modullar orasida ichki classlarni chaqirish
order -> payment.internal.PaymentServiceBu boundary buzadi.
Yaxshiroq:
order -> payment.api.PaymentFacadeyoki event orqali:
OrderCreatedEvent -> Payment moduleXato 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:
Layered Architecture'ni yaxshi tushunish
Package-by-feature / package-by-domain qilish
DDD basics: Entity, Value Object, Aggregate, Repository
Hexagonal Architecture: port va adapter
Modular Monolith: module boundary
Spring Modulith bilan module tekshirish
CQRS: command/query ajratish
Event Sourcing: faqat kerak bo‘lsa
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?"