<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Jaxongir a.k.a — Blog</title>
        <description>Java, Spring va taqsimlangan tizimlar bo'yicha tajriba yozuvlari.</description>
        <link>https://blog.jtscorp.uz</link>
        <atom:link href="https://blog.jtscorp.uz/feed.xml" rel="self" type="application/rss+xml"/>
        <lastBuildDate>Wed, 12 Aug 2026 09:19:25 +0000</lastBuildDate>
        <language>en</language>
        <copyright>© Jtscorp</copyright>
        <generator>Feedamic: the Atom and RSS Feed generator for Statamic</generator>
        <item>
            <title><![CDATA[Architecture with Spring]]></title>
            <link>https://blog.jtscorp.uz/architecture-with-spring</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/architecture-with-spring</guid>
            <pubDate>Thu, 02 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p>Arxitektor darajasida Spring’ni bilish - bu faqat <code>@Service</code>, <code>@Repository</code>, <code>@Controller</code> yozish emas. Bu <strong>katta tizimni qanday bo‘lish</strong>, <strong>qaysi joyda qaysi pattern ishlatish</strong>, <strong>modullar orasidagi bog‘liqlikni boshqarish</strong>, <strong>kelajakda microservice yoki modulith qilishga tayyorlash</strong> degani.</p><p><strong>Architecture with Spring</strong>: Hexagonal Architecture, Modular Monolith, CQRS, Event Sourcing, DDD mapping va package-by-domain enforcement mavzularidan iborat.</p><hr><h3>1. Architecture with Spring nima?</h3><p><strong>Architecture with Spring</strong> - Spring Boot yordamida application’ni shunchaki ishlaydigan qilib emas, balki:</p><ul><li><p><strong>tushunarli</strong></p></li><li><p><strong>test qilish oson</strong></p></li><li><p><strong>o‘zgartirishga qulay</strong></p></li><li><p><strong>biznesga mos</strong></p></li><li><p><strong>katta jamoada boshqariladigan</strong></p></li><li><p><strong>kelajakda bo‘linadigan</strong></p></li></ul><p>qilib loyihalashdir.</p><p>Oddiy developer savoli:</p><blockquote><p>&quot;Bu endpoint ishlaydimi?&quot;</p></blockquote><p>Arxitektor savoli:</p><blockquote><p>&quot;Bu tizim 2 yildan keyin ham oson o‘zgaradimi? Yangi modul qo‘shsak eski joylar buzilmaydimi? Biznes qoidalar framework’ga qaram bo‘lib qolmadimi?&quot;</p></blockquote><hr><h3>2. Oddiy layered architecture yetmay qoladigan joy</h3><p>Junior/Middle bosqichida ko‘p ishlatiladigan tuzilma:</p><pre><code class="language-text">controller
service
repository
entity
dto</code></pre><p>Masalan:</p><pre><code class="language-text">src/main/java/com/example/app
 ├── controller
 │    └── OrderController.java
 ├── service
 │    └── OrderService.java
 ├── repository
 │    └── OrderRepository.java
 ├── entity
 │    └── Order.java
 └── dto
      └── OrderRequest.java</code></pre><p>Kichik project uchun yaxshi. Lekin katta project’da muammo chiqadi:</p><pre><code class="language-text">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</code></pre><p>Natija:</p><blockquote><p>Project ishlaydi, lekin o‘zgartirish xavfli bo‘ladi.</p></blockquote><p>Arxitektor darajada maqsad - <strong>faqat qatlamlar emas, biznes chegaralarini ham to‘g‘ri ajratish</strong>.</p><hr><h3>3. Hexagonal Architecture in Spring Boot</h3><h4>Asosiy g‘oya</h4><p>Hexagonal Architecture yoki <strong>Ports and Adapters Architecture</strong> shuni aytadi:</p><blockquote><p>Biznes logic tashqi texnologiyalarga qaram bo‘lmasligi kerak.</p></blockquote><p>Ya’ni biznes qoidalar:</p><ul><li><p>Spring MVC’ga qaram bo‘lmasin</p></li><li><p>JPA’ga qaram bo‘lmasin</p></li><li><p>Kafka’ga qaram bo‘lmasin</p></li><li><p>Redis’ga qaram bo‘lmasin</p></li><li><p>tashqi API’ga qaram bo‘lmasin</p></li></ul><p>Spring, database, Kafka, REST API - bular <strong>adapter</strong>. Markazda esa <strong>domain</strong> turadi.</p><hr><h4>Oddiy ko‘rinish</h4><pre><code class="language-text">                REST Controller
                      |
                      v
               Input Adapter
                      |
                      v
                Application Service
                      |
                      v
                   Domain
                      |
                      v
              Output Port Interface
                      |
                      v
              JPA / Kafka / External API Adapter
</code></pre><hr><h2>Package structure misol</h2><pre><code class="language-text">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</code></pre><hr><h4>Nima uchun bu yaxshi?</h4><p>Chunki <code>CreateOrderService</code> database qanday ishlashini bilmaydi.</p><p>U faqat interface bilan ishlaydi:</p><pre><code class="language-java">public interface OrderRepositoryPort {
    Order save(Order order);
    Optional&lt;Order&gt; findById(OrderId id);
}</code></pre><p>Service:</p><pre><code class="language-java">@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);
    }
}</code></pre><p>JPA adapter:</p><pre><code class="language-java">@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);
    }
}</code></pre><p>Bu yerda muhim nuqta:</p><blockquote><p>Domain va application service JPA’ni bilmaydi. JPA tashqi adapter.</p></blockquote><hr><h3>4. Spring Boot’da Hexagonal Architecture qachon kerak?</h3><h4>Kerak bo‘ladi:</h4><ul><li><p>biznes logic murakkab bo‘lsa</p></li><li><p>project uzoq yashasa</p></li><li><p>testability muhim bo‘lsa</p></li><li><p>database keyin almashishi mumkin bo‘lsa</p></li><li><p>tashqi integratsiyalar ko‘p bo‘lsa</p></li><li><p>microservice darajasidagi service bo‘lsa</p></li></ul><h4>Kerak bo‘lmasligi mumkin:</h4><ul><li><p>oddiy CRUD admin panel</p></li><li><p>5-10 ta endpointli kichik project</p></li><li><p>tez MVP qilish kerak bo‘lsa</p></li><li><p>biznes logic deyarli yo‘q bo‘lsa</p></li></ul><p>Arxitektor qarori shunday bo‘ladi:</p><blockquote><p>Har joyga Hexagonal tiqish shart emas. Lekin biznes logic muhim joyda juda foydali.</p></blockquote><hr><h3>5. Modular Monolith with Spring Modulith</h3><h4>Modular Monolith nima?</h4><p>Monolith degani bitta application. Lekin <strong>modular monolith</strong> degani:</p><blockquote><p>Application bitta deploy bo‘ladi, lekin ichida aniq ajratilgan biznes modullar bo‘ladi.</p></blockquote><p>Masalan marketplace:</p><pre><code class="language-text">marketplace-app
 ├── order
 ├── payment
 ├── delivery
 ├── user
 ├── notification
 └── catalog</code></pre><p>Bu hali microservice emas. Hammasi bitta Spring Boot app ichida. Lekin har modul o‘z chegarasiga ega.</p><hr><h4>Oddiy monolith muammosi</h4><pre><code class="language-text">OrderService -&gt; PaymentRepository
PaymentService -&gt; UserRepository
UserService -&gt; OrderRepository
NotificationService -&gt; hammasiga bog‘langan</code></pre><p>Vaqt o‘tishi bilan:</p><pre><code class="language-text">hamma hammani chaqiradi
dependency tartibsiz bo‘ladi
o‘zgartirish xavfli bo‘ladi
microservice qilish juda qiyinlashadi</code></pre><hr><h4>Modular monolith yondashuvi</h4><pre><code class="language-text">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</code></pre><hr><h4>Spring Modulith nima beradi?</h4><p>Spring Modulith quyidagilarga yordam beradi:</p><ul><li><p>application module chegaralarini tekshiradi</p></li><li><p>module dependency’larini nazorat qiladi</p></li><li><p>module integration test yozishga yordam beradi</p></li><li><p>event-based communication’ni tartibga soladi</p></li><li><p>modul strukturasini dokumentatsiya qilishga yordam beradi</p></li></ul><p>Misol structure:</p><pre><code class="language-text">com.company.marketplace
 ├── order
 │    ├── OrderManagement.java
 │    ├── internal
 │    │    └── OrderService.java
 │    └── events
 │         └── OrderCreatedEvent.java
 │
 ├── payment
 │    ├── PaymentManagement.java
 │    └── internal
 │         └── PaymentService.java
 │
 └── delivery
      ├── DeliveryManagement.java
      └── internal
           └── DeliveryService.java</code></pre><p>Bu yerda <code>internal</code> ichidagi classlarni boshqa module to‘g‘ridan-to‘g‘ri ishlatmasligi kerak.</p><hr><h3>6. CQRS implementation patterns in Spring</h3><h2>CQRS nima?</h2><p>CQRS - <strong>Command Query Responsibility Segregation</strong>.</p><p>Ya’ni:</p><blockquote><p>Ma’lumotni o‘zgartirish va ma’lumotni o‘qish alohida model bilan ishlaydi.</p></blockquote><p>Oddiy CRUD’da:</p><pre><code class="language-text">OrderService
 ├── createOrder()
 ├── updateOrder()
 ├── cancelOrder()
 ├── getOrder()
 └── listOrders()</code></pre><p>CQRS’da:</p><pre><code class="language-text">Command side:
 ├── CreateOrderCommandHandler
 ├── CancelOrderCommandHandler
 └── PayOrderCommandHandler

Query side:
 ├── GetOrderQueryHandler
 └── SearchOrdersQueryHandler</code></pre><hr><h4>Nima uchun CQRS kerak?</h4><p>Chunki write va read ehtiyojlari har xil bo‘ladi.</p><p>Masalan:</p><h4>Write side</h4><pre><code class="language-text">Buyurtma yaratish
Payment tekshirish
Stock kamaytirish
Domain validation
Transaction
Event chiqarish</code></pre><h4>Read side</h4><pre><code class="language-text">Buyurtmalar ro‘yxati
Filter
Search
Pagination
Projection
Dashboard uchun tez o‘qish</code></pre><p>Bitta model bilan ikkalasini qilish ba’zida projectni og‘irlashtiradi.</p><hr><h4>Spring Boot’da oddiy CQRS misol</h4><pre><code class="language-text">order
 ├── command
 │    ├── CreateOrderCommand.java
 │    ├── CreateOrderHandler.java
 │    └── CancelOrderHandler.java
 │
 ├── query
 │    ├── GetOrderQuery.java
 │    ├── GetOrderHandler.java
 │    └── OrderViewRepository.java
 │
 └── domain
      └── Order.java</code></pre><p>Command:</p><pre><code class="language-java">public record CreateOrderCommand(
        Long customerId,
        List&lt;Long&gt; productIds
) {}</code></pre><p>Handler:</p><pre><code class="language-java">@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();
    }
}</code></pre><p>Query:</p><pre><code class="language-java">@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(() -&gt; new OrderNotFoundException(orderId));
    }
}</code></pre><hr><h4>CQRS har doim kerakmi?</h4><p>Yo‘q.</p><p>CQRS foydali bo‘ladi:</p><ul><li><p>read/write logic juda farq qilsa</p></li><li><p>reporting ko‘p bo‘lsa</p></li><li><p>dashboard og‘ir bo‘lsa</p></li><li><p>event-driven system bo‘lsa</p></li><li><p>write model murakkab domain bo‘lsa</p></li></ul><p>Kerak emas:</p><ul><li><p>oddiy CRUD</p></li><li><p>kichik admin panel</p></li><li><p>kam endpointli service</p></li><li><p>jamoa tajribasi yetarli bo‘lmasa</p></li></ul><hr><h3>7. Event Sourcing with Spring &amp; Axon Framework</h3><h4>Event Sourcing nima?</h4><p>Oddiy tizimda database’da oxirgi holat saqlanadi:</p><pre><code class="language-text">orders table:
id = 1
status = PAID
total = 300000</code></pre><p>Event sourcing’da esa holat emas, <strong>voqealar tarixi</strong> saqlanadi:</p><pre><code class="language-text">OrderCreated
OrderItemAdded
PaymentStarted
PaymentCompleted
OrderConfirmed</code></pre><p>Order’ning hozirgi holati shu eventlardan qayta tiklanadi.</p><hr><h4>Oddiy holat vs Event Sourcing</h4><h4>Oddiy state-based model</h4><pre><code class="language-text">Order status = DELIVERED</code></pre><p>Lekin savol:</p><pre><code class="language-text">Qachon yaratildi?
Kim to‘ladi?
Qachon status o‘zgardi?
Nega cancel bo‘lmadi?</code></pre><p>Bular uchun alohida audit kerak bo‘ladi.</p><h4>Event Sourcing</h4><pre><code class="language-text">1. OrderCreated
2. PaymentCompleted
3. DeliveryStarted
4. OrderDelivered</code></pre><p>Tarixning o‘zi asosiy ma’lumot bo‘ladi.</p><hr><h4>Qachon Event Sourcing kerak?</h4><p>Kerak bo‘ladi:</p><ul><li><p>audit juda muhim bo‘lsa</p></li><li><p>moliya/payment tizimlari</p></li><li><p>banking</p></li><li><p>order lifecycle murakkab bo‘lsa</p></li><li><p>voqealar tarixi biznes uchun qimmatli bo‘lsa</p></li><li><p>state’ni qayta tiklash kerak bo‘lsa</p></li></ul><p>Kerak bo‘lmasligi mumkin:</p><ul><li><p>oddiy CRUD</p></li><li><p>report-only tizim</p></li><li><p>kichik project</p></li><li><p>jamoa event sourcing tajribasiga ega bo‘lmasa</p></li></ul><hr><h4>Axon Framework nima qiladi?</h4><p>Axon Framework Java/Spring ekotizimida:</p><ul><li><p>command handling</p></li><li><p>event publishing</p></li><li><p>event store</p></li><li><p>aggregate</p></li><li><p>saga</p></li><li><p>CQRS</p></li><li><p>event sourcing</p></li></ul><p>kabi narsalarni tartibli qilishga yordam beradi.</p><p>Lekin Arxitektor qaror:</p><blockquote><p>Axon kuchli, lekin murakkab. Uni faqat haqiqiy ehtiyoj bo‘lsa tanlash kerak.</p></blockquote><hr><h3>8. DDD mapping to Spring layers</h3><h4>DDD nima?</h4><p>DDD - <strong>Domain-Driven Design</strong>.</p><p>Asosiy g‘oya:</p><blockquote><p>Kod strukturasi biznes tiliga mos bo‘lishi kerak.</p></blockquote><p>Masalan, e-commerce’da biznes so‘zlari:</p><pre><code class="language-text">Order
Payment
Customer
Product
Cart
Delivery
Invoice
Refund</code></pre><p>Kod ham shularga qarab tuzilishi kerak.</p><hr><h4>Noto‘g‘ri yondashuv</h4><pre><code class="language-text">controller
service
repository
entity
dto</code></pre><p>Bu texnik qatlamlarga qarab bo‘lingan. Katta project’da biznes context yo‘qoladi.</p><hr><h4>Yaxshiroq yondashuv: package-by-domain</h4><pre><code class="language-text">com.company.marketplace
 ├── order
 │    ├── domain
 │    ├── application
 │    ├── adapter
 │    └── api
 │
 ├── payment
 │    ├── domain
 │    ├── application
 │    ├── adapter
 │    └── api
 │
 └── delivery
      ├── domain
      ├── application
      ├── adapter
      └── api</code></pre><p>Bu yerda project biznes bo‘yicha o‘qiladi.</p><hr><h4>DDD elementlari</h4><h4>Entity</h4><p>Identity bor obyekt.</p><pre><code class="language-java">public class Order {
    private OrderId id;
    private CustomerId customerId;
    private OrderStatus status;
}</code></pre><h4>Value Object</h4><p>Identity yo‘q, qiymati bilan teng.</p><pre><code class="language-java">public record Money(BigDecimal amount, String currency) {}</code></pre><h4>Aggregate</h4><p>Bir nechta entity/value object’larni boshqaradigan domain chegarasi.</p><pre><code class="language-java">public class Order {
    private List&lt;OrderItem&gt; items;

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

    public void confirm() {
        if (items.isEmpty()) {
            throw new OrderCannotBeConfirmedException();
        }
        this.status = OrderStatus.CONFIRMED;
    }
}</code></pre><h4>Repository</h4><p>Domain obyektni saqlash interface’i.</p><pre><code class="language-java">public interface OrderRepository {
    Order save(Order order);
    Optional&lt;Order&gt; findById(OrderId id);
}</code></pre><p>Muhim:</p><blockquote><p>DDD’da repository database table uchun emas, aggregate uchun ishlaydi.</p></blockquote><hr><h3>9. Package-by-domain enforcement</h3><h4>Muammo</h4><p>Developerlar vaqt o‘tib qoidani buzadi:</p><pre><code class="language-java">payment.internal.PaymentService</code></pre><p>ni <code>order</code> module ichidan chaqirib yuboradi.</p><p>Yoki:</p><pre><code class="language-java">order.repository.OrderRepository</code></pre><p>ni boshqa module ishlatib yuboradi.</p><p>Natija:</p><pre><code class="language-text">module boundary buziladi
tight coupling paydo bo‘ladi
microservice qilish qiyinlashadi
testlar murakkablashadi</code></pre><hr><h4>Buni qanday nazorat qilamiz?</h4><h4>1. Package convention</h4><pre><code class="language-text">order.internal faqat order ichida ishlatiladi
order.api boshqa module uchun public contract</code></pre><h4>2. ArchUnit tests</h4><p>ArchUnit bilan architecture qoidalarini test qilamiz.</p><p>Misol:</p><pre><code class="language-java">@AnalyzeClasses(packages = &quot;com.company.marketplace&quot;)
public class ArchitectureTest {

    @ArchTest
    static final ArchRule order_should_not_access_payment_internal =
            noClasses()
                    .that().resideInAPackage(&quot;..order..&quot;)
                    .should().accessClassesThat()
                    .resideInAPackage(&quot;..payment.internal..&quot;);
}</code></pre><p>Bu test nima qiladi?</p><blockquote><p><code>order</code> module <code>payment.internal</code> ichidagi classlarni ishlatsa, test yiqiladi.</p></blockquote><p>Bu Arxitektor darajasida juda muhim.</p><hr><h3>10. Architecture with Spring uchun ideal structure</h3><p>Katta, biznes logic ko‘p project uchun yaxshi structure:</p><pre><code class="language-text">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</code></pre><p>Bu structure’da:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Qism</p></th><th rowspan="1" colspan="1"><p>Vazifasi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>api</code></p></td><td rowspan="1" colspan="1"><p>boshqa modullar foydalanishi mumkin bo‘lgan public contract</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>application</code></p></td><td rowspan="1" colspan="1"><p>use case, command, query, orchestration</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>domain</code></p></td><td rowspan="1" colspan="1"><p>biznes model va biznes qoidalar</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>adapter/in</code></p></td><td rowspan="1" colspan="1"><p>REST, Kafka consumer, scheduler</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>adapter/out</code></p></td><td rowspan="1" colspan="1"><p>JPA, Redis, Kafka producer, external API</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>internal</code></p></td><td rowspan="1" colspan="1"><p>faqat shu modul ichida ishlatiladigan classlar</p></td></tr></tbody></table><hr><h3>11. Arxitektor qanday qaror qiladi?</h3><p>Arxitektor har doim pattern tanlashdan oldin savol beradi:</p><h4>1. Biznes murakkabmi?</h4><p>Agar faqat CRUD bo‘lsa:</p><pre><code class="language-text">Controller → Service → Repository</code></pre><p>yetadi.</p><p>Agar domain murakkab bo‘lsa:</p><pre><code class="language-text">Hexagonal + DDD</code></pre><p>yaxshi.</p><hr><h4>2. System kelajakda bo‘linadimi?</h4><p>Agar kelajakda microservice qilish ehtimoli bo‘lsa:</p><pre><code class="language-text">Modular Monolith + Spring Modulith</code></pre><p>juda yaxshi start.</p><hr><h4>3. Read/write load farq qiladimi?</h4><p>Agar read juda ko‘p, write murakkab bo‘lsa:</p><pre><code class="language-text">CQRS</code></pre><p>ko‘rib chiqiladi.</p><hr><h4>4. Audit va tarix muhimmi?</h4><p>Agar har bir o‘zgarish tarixi muhim bo‘lsa:</p><pre><code class="language-text">Event Sourcing</code></pre><p>ko‘rib chiqiladi.</p><hr><h4>5. Team buni ko‘tara oladimi?</h4><p>Eng muhim savol:</p><blockquote><p>Pattern chiroyli, lekin jamoa tushunmasa project battar murakkablashadi.</p></blockquote><p>Arxitektor faqat texnik emas, jamoa darajasini ham hisobga oladi.</p><hr><h3>12. Real hayotiy misol: Online Shop</h3><h4>Oddiy variant:</h4><pre><code class="language-text">OrderController
OrderService
OrderRepository
PaymentService
DeliveryService</code></pre><p>Tez yoziladi, lekin katta bo‘lsa chalkashadi.</p><hr><h4>Arxitektor varianti:</h4><pre><code class="language-text">order module
 ├── CreateOrderUseCase
 ├── Order aggregate
 ├── OrderRepositoryPort
 ├── JpaOrderAdapter
 └── OrderCreatedEvent

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

delivery module
 ├── DeliveryFacade
 └── DeliveryStartedEvent</code></pre><p>Flow:</p><pre><code class="language-text">1. User order yaratadi
2. Order module OrderCreatedEvent chiqaradi
3. Payment module eventni eshitadi
4. Payment tugasa PaymentCompletedEvent chiqadi
5. Delivery module delivery boshlaydi</code></pre><p>Bu yondashuvda modullar kuchli ajratilgan bo‘ladi.</p><hr><h3>13. Eng ko‘p qilinadigan xatolar</h3><h4>Xato 1: Har joyga Hexagonal Architecture ishlatish</h4><p>Kichik CRUD project’da bu ortiqcha bo‘lishi mumkin.</p><hr><h4>Xato 2: Domain modelni JPA entity bilan aralashtirish</h4><p>Ba’zida mumkin, lekin murakkab domain’da yaxshi emas.</p><pre><code class="language-java">@Entity
public class Order {
    // biznes logic + JPA annotation + relation + serialization
}</code></pre><p>Bu class haddan tashqari ko‘p mas’uliyat oladi.</p><hr><h4>Xato 3: Service classni &quot;God Service&quot; qilish</h4><pre><code class="language-java">OrderService
 ├── createOrder()
 ├── payOrder()
 ├── deliverOrder()
 ├── refundOrder()
 ├── sendNotification()
 ├── updateStock()
 └── generateInvoice()</code></pre><p>Bu yomon signal.</p><p>Yaxshiroq:</p><pre><code class="language-text">CreateOrderHandler
PayOrderHandler
CancelOrderHandler
RefundOrderHandler</code></pre><hr><h4>Xato 4: Modullar orasida ichki classlarni chaqirish</h4><pre><code class="language-java">order -&gt; payment.internal.PaymentService</code></pre><p>Bu boundary buzadi.</p><p>Yaxshiroq:</p><pre><code class="language-java">order -&gt; payment.api.PaymentFacade</code></pre><p>yoki event orqali:</p><pre><code class="language-text">OrderCreatedEvent -&gt; Payment module</code></pre><hr><h4>Xato 5: Patternni biznes ehtiyojsiz tanlash</h4><p>CQRS, Event Sourcing, Hexagonal, DDD - bular kuchli. Lekin noto‘g‘ri joyda ishlatilsa projectni og‘irlashtiradi.</p><hr><h3>14. Qisqa qoida</h3><p>Arxitektor darajada Spring architecture uchun asosiy qoida:</p><pre><code class="language-text">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.</code></pre><hr><h3>15. Amaliy mini roadmap</h3><p>Bu mavzuni o‘rganishdagi ketma-ketlik:</p><ol start="1"><li><p><strong>Layered Architecture</strong>&#039;ni yaxshi tushunish</p></li><li><p><strong>Package-by-feature / package-by-domain</strong> qilish</p></li><li><p><strong>DDD basics</strong>: Entity, Value Object, Aggregate, Repository</p></li><li><p><strong>Hexagonal Architecture</strong>: port va adapter</p></li><li><p><strong>Modular Monolith</strong>: module boundary</p></li><li><p><strong>Spring Modulith</strong> bilan module tekshirish</p></li><li><p><strong>CQRS</strong>: command/query ajratish</p></li><li><p><strong>Event Sourcing</strong>: faqat kerak bo‘lsa</p></li><li><p><strong>ArchUnit</strong> bilan architecture qoidalarini test qilish</p></li></ol><hr><h3>Xulosa</h3><p><strong>Architecture with Spring</strong> - bu Spring Boot project’ni professional, uzoq yashaydigan va oson kengayadigan qilib qurish san’ati.</p><p>Boshlang&#039;ich Spring’da endpoint yozadi.<br>Junior layer ajratadi.<br>Middle transaction, security, caching, testingni chuqur biladi.<br>Senior performance va distributed system bilan ishlaydi.<br>Arxitektor esa quyidagini hal qiladi:</p><blockquote><p>&quot;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?&quot;</p></blockquote>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Platform Standardization]]></title>
            <link>https://blog.jtscorp.uz/platform-standardization</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/platform-standardization</guid>
            <pubDate>Thu, 02 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Platform standardization</strong> - kompaniyada yoki katta jamoada Spring Boot projectlarni <strong>bitta standart asosida</strong> yuritish degani.</p><p>Ya’ni har bir team quyidagilarni o‘zi boshidan o‘ylab chiqmaydi:</p><pre><code class="language-text">Logging qanday bo‘ladi?
Security qanday qo‘shiladi?
Tracing qanday ishlaydi?
Exception response formati qanday?
Dependency versionlarni kim boshqaradi?
Project structure qanday bo‘ladi?
Actuator, metrics, health check doim bormi?</code></pre><p>Arxitektor darajasida maqsad:</p><blockquote><p>Har bir yangi Spring Boot service bir xil sifat, bir xil structure, bir xil observability, bir xil security va bir xil dependency boshqaruvi bilan boshlansin.</p></blockquote><p>Platform Standardization maqolasi quyidagi qismlardan iborat: <strong>internal Spring Boot starter library</strong>, <strong>centralized security configuration starter</strong>, <strong>shared observability setup</strong>, <strong>BOM</strong>, <strong>project template strategy</strong>, va <strong>ArchUnit bilan convention enforcement</strong>.</p><hr><h3>1. Nega Platform Standardization kerak?</h3><p>Katta kompaniyada 10 ta Spring Boot service bo‘lsa, odatda bunday muammo chiqadi:</p><pre><code class="language-text">user-service      → Spring Boot 3.2
payment-service   → Spring Boot 3.1
order-service     → log format boshqa
delivery-service  → security config boshqa
billing-service   → actuator yopiq
catalog-service   → tracing yo‘q
notification      → exception response boshqa</code></pre><p>Boshida bu kichik muammo ko‘rinadi. Lekin production’da katta og‘riq beradi:</p><ul><li><p>loglarni qidirish qiyinlashadi</p></li><li><p>security xatolar ko‘payadi</p></li><li><p>dependency conflict chiqadi</p></li><li><p>monitoring bir xil bo‘lmaydi</p></li><li><p>yangi service yaratish sekinlashadi</p></li><li><p>har team bir xil muammoni qayta yechadi</p></li><li><p>incident vaqtida tizimni tushunish qiyinlashadi</p></li></ul><p>Platform standardization shuni hal qiladi:</p><pre><code class="language-text">Har service bir xil asosdan boshlanadi.
Har service minimal production talablariga javob beradi.
Har team infrastructure detallarini qayta yozmaydi.</code></pre><hr><h3>2. Platform team nima qiladi?</h3><p>Platform team yoki architect team odatda quyidagilarni beradi:</p><pre><code class="language-text">company-spring-boot-starter
company-security-starter
company-observability-starter
company-error-handling-starter
company-bom
company-project-template
architecture-tests
coding conventions</code></pre><p>Bu narsalar product teamlarga tayyor beriladi.</p><p>Product developer shunchaki dependency qo‘shadi:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;com.company.platform&lt;/groupId&gt;
    &lt;artifactId&gt;company-web-starter&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>Va avtomatik keladi:</p><pre><code class="language-text">standard logging
standard error format
standard security headers
standard actuator config
standard tracing
standard metrics
standard correlation-id
standard OpenAPI config</code></pre><hr><h3>3. Internal Spring Boot Starter Library</h3><h4>Starter nima?</h4><p>Spring Boot starter - bu bir nechta dependency va configuration’ni bitta package qilib berish usuli.</p><p>Spring Boot rasmiy hujjatlarida auto-configuration jar dependency’lar asosida application’ni avtomatik sozlashga harakat qilishi aytiladi. Masalan, classpath’da kerakli kutubxona bo‘lsa va user o‘zi bean bermagan bo‘lsa, Spring Boot kerakli bean’ni auto-config qiladi. (<a href="https://docs.spring.io/spring-boot/reference/using/auto-configuration.html?utm_source=chatgpt.com" title="Auto-configuration :: Spring Boot">Home</a>)</p><p>Custom starter esa kompaniya ichida shunday ishlaydi:</p><pre><code class="language-text">company-security-starter qo‘shildi
        ↓
SecurityFilterChain avtomatik sozlandi
        ↓
JWT decoder avtomatik ulandi
        ↓
public endpointlar standart bo‘ldi
        ↓
security headers qo‘shildi</code></pre><p>Spring Boot rasmiy hujjatlarida custom auto-configuration odatda starter bilan bog‘lanishi, starter esa auto-configuration code va kerakli kutubxonalarni birga berishi tushuntirilgan. (<a href="https://docs.spring.io/spring-boot/reference/features/developing-auto-configuration.html?utm_source=chatgpt.com" title="Creating Your Own Auto-configuration">Home</a>)</p><hr><h4>Nega internal starter kerak?</h4><p>Chunki har projectda shu kodni qayta yozish xato:</p><pre><code class="language-java">@Configuration
public class SecurityConfig {
    // 150 qator security sozlama
}</code></pre><pre><code class="language-java">@Configuration
public class ObservabilityConfig {
    // tracing, metrics, MDC
}</code></pre><pre><code class="language-java">@ControllerAdvice
public class GlobalExceptionHandler {
    // standard error response
}</code></pre><p>Yaxshiroq:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;com.company.platform&lt;/groupId&gt;
    &lt;artifactId&gt;company-service-starter&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>Va common konfiguratsiya avtomatik ulanadi.</p><hr><h3>4. Internal starter structure</h3><p>Masalan:</p><pre><code class="language-text">company-platform
 ├── company-web-starter
 ├── company-security-starter
 ├── company-observability-starter
 ├── company-exception-starter
 ├── company-data-starter
 └── company-bom</code></pre><p>Har starter alohida mas’uliyatga ega.</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Starter</p></th><th rowspan="1" colspan="1"><p>Vazifasi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>company-web-starter</code></p></td><td rowspan="1" colspan="1"><p>REST convention, Jackson config, CORS, API response</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>company-security-starter</code></p></td><td rowspan="1" colspan="1"><p>JWT, OAuth2 resource server, headers, auth rules</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>company-observability-starter</code></p></td><td rowspan="1" colspan="1"><p>Micrometer, tracing, logging MDC</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>company-exception-starter</code></p></td><td rowspan="1" colspan="1"><p>error response format, global exception handler</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>company-data-starter</code></p></td><td rowspan="1" colspan="1"><p>JPA convention, auditing, transaction config</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>company-test-starter</code></p></td><td rowspan="1" colspan="1"><p>Testcontainers, WireMock, test utilities</p></td></tr></tbody></table><hr><h3>5. Starter ichida auto-configuration</h3><p>Spring Boot 3.x’da custom auto-configuration odatda shunday yoziladi:</p><pre><code class="language-java">package com.company.platform.web;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
public class CompanyWebAutoConfiguration {

    @Bean
    public CorrelationIdFilter correlationIdFilter() {
        return new CorrelationIdFilter();
    }
}</code></pre><p>Keyin <code>AutoConfiguration.imports</code> faylida ro‘yxatdan o‘tkaziladi:</p><pre><code class="language-text">META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports</code></pre><p>Ichida:</p><pre><code class="language-text">com.company.platform.web.CompanyWebAutoConfiguration</code></pre><p>Shunda starter dependency sifatida qo‘shilganda Spring Boot uni avtomatik ko‘radi.</p><hr><h3>6. <code>@ConditionalOnMissingBean</code></h3><p>Internal starter majburlab emas, default berib ishlashi kerak.</p><p>Yomon:</p><pre><code class="language-java">@Bean
public ObjectMapper objectMapper() {
    return new ObjectMapper();
}</code></pre><p>Bu product team custom <code>ObjectMapper</code> bermoqchi bo‘lsa conflict qiladi.</p><p>Yaxshi:</p><pre><code class="language-java">@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
    return new ObjectMapper()
            .findAndRegisterModules();
}</code></pre><p>Bu nimani anglatadi?</p><pre><code class="language-text">Agar application o‘z ObjectMapper bean’ini bermagan bo‘lsa,
platform default ObjectMapper beradi.
Agar application o‘zi bergan bo‘lsa,
platform aralashmaydi.</code></pre><p>Arxitektor qoida:</p><blockquote><p>Platform default beradi, lekin product teamga override imkonini qoldiradi.</p></blockquote><hr><h3>7. Centralized Security Configuration Starter</h3><p>Security har service’da boshqacha yozilsa, xavf oshadi.</p><p>Masalan, bitta service’da:</p><pre><code class="language-java">.requestMatchers(&quot;/actuator/**&quot;).permitAll()</code></pre><p>boshqasida:</p><pre><code class="language-java">.anyRequest().permitAll()</code></pre><p>boshqasida CSRF noto‘g‘ri disable qilingan.</p><p>Bunday holatda production’da security incident bo‘lishi oson.</p><hr><h4><code>company-security-starter</code></h4><p>Bu starter quyidagilarni standart qiladi:</p><pre><code class="language-text">JWT validation
OAuth2 Resource Server config
public endpoint convention
role/scope mapping
security headers
CORS policy
actuator endpoint security
method security
audit logging</code></pre><p>Misol:</p><pre><code class="language-java">@AutoConfiguration
@EnableMethodSecurity
public class CompanySecurityAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(auth -&gt; auth
                        .requestMatchers(&quot;/actuator/health&quot;, &quot;/actuator/info&quot;).permitAll()
                        .requestMatchers(&quot;/api/public/**&quot;).permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth -&gt; oauth.jwt())
                .build();
    }
}</code></pre><p>Bu bilan har service default secure bo‘ladi.</p><hr><h4>Security starter’da ehtiyot bo‘lish kerak</h4><p>Platform security starter haddan tashqari qattiq bo‘lsa, teamlar uni chetlab o‘tadi.</p><p>Yaxshi starter:</p><pre><code class="language-text">secure default beradi
override qilishga ruxsat beradi
config property orqali sozlanadi
documentation bor
test utilities beradi</code></pre><p>Masalan:</p><pre><code class="language-yaml">company:
  security:
    public-paths:
      - /api/public/**
      - /swagger-ui/**
    required-scope: orders.read</code></pre><hr><h3>8. Shared Observability Setup</h3><p>Observability - production’da systemni ko‘ra olish:</p><pre><code class="language-text">logs
metrics
traces
health checks
alerts
correlation-id</code></pre><p>Har service’da observability bir xil bo‘lishi kerak.</p><hr><h4><code>company-observability-starter</code></h4><p>Bu starter quyidagilarni beradi:</p><pre><code class="language-text">Actuator endpoints
Micrometer metrics
Prometheus registry
traceId/spanId logging
correlation-id filter
structured logging
custom business metrics
health indicators</code></pre><p>Misol:</p><pre><code class="language-text">Request keldi
        ↓
CorrelationIdFilter correlation-id yaratdi
        ↓
MDC ichiga qo‘ydi
        ↓
Loglarda correlationId chiqdi
        ↓
Trace systemga yuborildi
        ↓
Prometheus metrics yig‘di</code></pre><p>Log formati hamma service’da bir xil bo‘lsa, incident vaqtida juda katta foyda beradi.</p><hr><h4>Standard log format</h4><p>Masalan:</p><pre><code class="language-json">{
  &quot;timestamp&quot;: &quot;2026-05-21T10:15:00Z&quot;,
  &quot;level&quot;: &quot;INFO&quot;,
  &quot;service&quot;: &quot;order-service&quot;,
  &quot;traceId&quot;: &quot;abc123&quot;,
  &quot;spanId&quot;: &quot;def456&quot;,
  &quot;correlationId&quot;: &quot;req-789&quot;,
  &quot;message&quot;: &quot;Order created&quot;
}</code></pre><p>Bu format har service’da bir xil bo‘lsa:</p><pre><code class="language-text">Kibana / Grafana Loki / Datadog’da qidirish oson bo‘ladi.
Trace bilan log bog‘lanadi.
Incident debugging tezlashadi.</code></pre><hr><h3>9. Standard Error Handling Starter</h3><p>Har service xatoni har xil qaytarsa, frontend va boshqa service’lar qiynaladi.</p><p>Yomon:</p><pre><code class="language-json">{
  &quot;error&quot;: &quot;Bad request&quot;
}</code></pre><p>Boshqa service:</p><pre><code class="language-json">{
  &quot;message&quot;: &quot;Validation failed&quot;,
  &quot;code&quot;: 400
}</code></pre><p>Boshqa service:</p><pre><code class="language-json">{
  &quot;timestamp&quot;: &quot;...&quot;,
  &quot;status&quot;: 400,
  &quot;path&quot;: &quot;/orders&quot;
}</code></pre><p>Yaxshi standart:</p><pre><code class="language-json">{
  &quot;type&quot;: &quot;VALIDATION_ERROR&quot;,
  &quot;message&quot;: &quot;Validation failed&quot;,
  &quot;traceId&quot;: &quot;abc123&quot;,
  &quot;details&quot;: [
    {
      &quot;field&quot;: &quot;email&quot;,
      &quot;message&quot;: &quot;must be a valid email&quot;
    }
  ]
}</code></pre><hr><h4>Global exception starter</h4><pre><code class="language-java">@RestControllerAdvice
public class CompanyExceptionHandler {

    @ExceptionHandler(ValidationException.class)
    public ResponseEntity&lt;ApiError&gt; handleValidation(ValidationException ex) {
        return ResponseEntity.badRequest().body(
                ApiError.validation(ex.getMessage())
        );
    }

    @ExceptionHandler(EntityNotFoundException.class)
    public ResponseEntity&lt;ApiError&gt; handleNotFound(EntityNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
                ApiError.notFound(ex.getMessage())
        );
    }
}</code></pre><p>Buni har service’da qayta yozish kerak emas. Starter beradi.</p><hr><h3>10. BOM - Bill of Materials</h3><h4>BOM nima?</h4><p>BOM - dependency versionlarni markazdan boshqarish.</p><p>Masalan, har project o‘zi yozsa:</p><pre><code class="language-xml">&lt;spring.boot.version&gt;3.3.1&lt;/spring.boot.version&gt;
&lt;spring.cloud.version&gt;2023.0.2&lt;/spring.cloud.version&gt;
&lt;mapstruct.version&gt;1.5.5&lt;/mapstruct.version&gt;
&lt;testcontainers.version&gt;1.19.8&lt;/testcontainers.version&gt;</code></pre><p>Bitta projectda boshqa, ikkinchisida boshqa bo‘lib ketadi.</p><p>BOM bilan:</p><pre><code class="language-xml">&lt;dependencyManagement&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.company.platform&lt;/groupId&gt;
            &lt;artifactId&gt;company-bom&lt;/artifactId&gt;
            &lt;version&gt;1.4.0&lt;/version&gt;
            &lt;type&gt;pom&lt;/type&gt;
            &lt;scope&gt;import&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/dependencyManagement&gt;</code></pre><p>Keyin dependency version yozilmaydi:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.testcontainers&lt;/groupId&gt;
    &lt;artifactId&gt;postgresql&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>Spring Boot Gradle plugin hujjatlarida dependency’larni boshqarish uchun <code>io.spring.dependency-management</code> plugin yoki Gradle native BOM support ishlatilishi mumkinligi aytilgan. (<a href="https://docs.spring.io/spring-boot/gradle-plugin/managing-dependencies.html?utm_source=chatgpt.com" title="Managing Dependencies :: Spring Boot">Home</a>) Gradle rasmiy hujjatlarida BOM dependency version constraints’larni markaziy boshqarish uchun ishlatilishi ko‘rsatilgan. (<a href="https://docs.gradle.org/current/userguide/platforms.html?utm_source=chatgpt.com" title="Platforms">Gradle Documentation</a>)</p><hr><h4>Company BOM ichida nima bo‘ladi?</h4><pre><code class="language-text">Spring Boot version
Spring Cloud version
Spring Modulith version
MapStruct version
Testcontainers version
OpenAPI version
Micrometer version
Resilience4j version
Kafka client version
PostgreSQL driver version</code></pre><p>Foydasi:</p><pre><code class="language-text">dependency conflict kamayadi
upgrade markazdan qilinadi
CVE patch qilish osonlashadi
teamlar version tanlashga vaqt sarflamaydi
build reproducible bo‘ladi</code></pre><hr><h3>11. Project Template Strategy</h3><p>Har yangi service noldan yaratilmasligi kerak.</p><p>Yomon:</p><pre><code class="language-text">Developer start.spring.io ga kiradi
dependencylarni taxminan tanlaydi
package structure o‘zi qiladi
security keyin qo‘shiladi
observability esdan chiqadi</code></pre><p>Yaxshi:</p><pre><code class="language-text">company project template ishlatiladi
default package structure bor
starterlar tayyor
CI/CD pipeline bor
Dockerfile bor
Actuator bor
test skeleton bor
README bor</code></pre><p>Spring Initializr rasmiy hujjatlarida o‘z custom Initializr instance’ingizni yaratish, dependencylar ro‘yxati va project attribute constraints’larni sozlash mumkinligi aytilgan. (<a href="https://docs.spring.io/initializr/docs/current/reference/html/?utm_source=chatgpt.com" title="Spring Initializr Reference Guide">Home</a>)</p><hr><h4>Template ichida nima bo‘lishi kerak?</h4><pre><code class="language-text">src/main/java/com/company/service
 ├── ServiceApplication.java
 ├── config
 ├── common
 ├── module1
 └── module2

src/test/java
 ├── ArchitectureTest.java
 ├── IntegrationTestBase.java
 └── TestcontainersConfig.java

Dockerfile
docker-compose.yml
README.md
openapi.yml
.github/workflows/build.yml yoki .gitlab-ci.yml</code></pre><p>Minimum production template:</p><pre><code class="language-text">Health check
Readiness/liveness
Structured logging
Security default
Exception format
OpenAPI config
Testcontainers setup
Architecture test
Docker build
CI quality gates</code></pre><hr><h3>12. Archetype yoki Custom Initializr?</h3><h4>Maven Archetype</h4><p>Oddiy project skeleton yaratish uchun ishlatiladi.</p><p>Foydasi:</p><pre><code class="language-text">sodda
tez
ichki template uchun yetarli</code></pre><p>Kamchiligi:</p><pre><code class="language-text">dynamic dependency tanlash qiyinroq
UI yo‘q
enterprise customization cheklangan</code></pre><hr><h4>Custom Spring Initializr</h4><p>Katta platform uchun kuchliroq.</p><p>Foydasi:</p><pre><code class="language-text">ichki start.company.com qilish mumkin
team dependency tanlaydi
Java version constraint beriladi
Spring Boot version nazorat qilinadi
company starterlar default chiqadi</code></pre><p>Masalan:</p><pre><code class="language-text">start.company.uz
 ├── Web API Service
 ├── Kafka Consumer Service
 ├── Batch Job
 ├── Modulith Application
 └── Library Project</code></pre><p>Arxitektor qaror:</p><blockquote><p>Kichik kompaniyada template yetadi. Katta kompaniyada custom Initializr yoki internal developer portal foydaliroq.</p></blockquote><hr><h3>13. Enforcing Conventions - ArchUnit Tests</h3><p>Documentation yetarli emas. Qoidalar test bilan tekshirilishi kerak.</p><p>ArchUnit user guide’da architecture qoidalarini concise syntax bilan yozish va layered architecture kabi tayyor qoidalar mavjudligi ko‘rsatilgan. (<a href="https://www.archunit.org/userguide/html/000_Index.html?utm_source=chatgpt.com" title="ArchUnit User Guide">ArchUnit</a>)</p><hr><h4>Misol: Controller repository chaqirmasin</h4><pre><code class="language-java">@AnalyzeClasses(packages = &quot;com.company&quot;)
class ArchitectureTest {

    @ArchTest
    static final ArchRule controllers_should_not_access_repositories =
            noClasses()
                    .that().resideInAPackage(&quot;..controller..&quot;)
                    .should().accessClassesThat()
                    .resideInAPackage(&quot;..repository..&quot;);
}</code></pre><p>Bu nimani himoya qiladi?</p><pre><code class="language-text">Controller → Service → Repository tartibini</code></pre><hr><h4>Misol: internal package tashqaridan ishlatilmasin</h4><pre><code class="language-java">@ArchTest
static final ArchRule internal_packages_should_not_be_accessed =
        noClasses()
                .that().resideOutsideOfPackage(&quot;..order..&quot;)
                .should().accessClassesThat()
                .resideInAPackage(&quot;..order.internal..&quot;);</code></pre><p>Bu modular architecture’ni himoya qiladi.</p><hr><h4>Misol: Service classlar <code>@Transactional</code> bilan ishlasin</h4><pre><code class="language-java">@ArchTest
static final ArchRule services_should_be_transactional =
        classes()
                .that().resideInAPackage(&quot;..application..&quot;)
                .and().haveSimpleNameEndingWith(&quot;Service&quot;)
                .should().beAnnotatedWith(Transactional.class);</code></pre><p>Ehtiyot bo‘lish kerak: bunday qoida hamma project uchun mos bo‘lmasligi mumkin. Shuning uchun platform convention aniq yozilishi kerak.</p><hr><h3>14. Standard Package Structure</h3><p>Platform team quyidagi structure’ni majburlashi mumkin:</p><pre><code class="language-text">com.company.&lt;service&gt;
 ├── &lt;domain-module&gt;
 │    ├── api
 │    ├── application
 │    ├── domain
 │    ├── adapter
 │    │    ├── in
 │    │    │    └── web
 │    │    └── out
 │    │         ├── persistence
 │    │         └── messaging
 │    └── internal
 │
 ├── config
 └── ServiceApplication.java</code></pre><p>Yoki sodda service uchun:</p><pre><code class="language-text">com.company.&lt;service&gt;
 ├── controller
 ├── service
 ├── repository
 ├── dto
 ├── config
 └── exception</code></pre><p>Arxitektor muhim qaror qiladi:</p><blockquote><p>Hamma service’ga bir xil murakkab architecture tiqilmaydi. Service turiga qarab template bo‘ladi.</p></blockquote><hr><h3>15. Service Type bo‘yicha standartlar</h3><table><tbody><tr><th rowspan="1" colspan="1"><p>Service turi</p></th><th rowspan="1" colspan="1"><p>Tavsiya structure</p></th></tr><tr><td rowspan="1" colspan="1"><p>Simple CRUD</p></td><td rowspan="1" colspan="1"><p>Layered architecture</p></td></tr><tr><td rowspan="1" colspan="1"><p>Business-heavy service</p></td><td rowspan="1" colspan="1"><p>Hexagonal + DDD</p></td></tr><tr><td rowspan="1" colspan="1"><p>Katta monolith</p></td><td rowspan="1" colspan="1"><p>Spring Modulith</p></td></tr><tr><td rowspan="1" colspan="1"><p>Kafka consumer</p></td><td rowspan="1" colspan="1"><p>Message-driven template</p></td></tr><tr><td rowspan="1" colspan="1"><p>Batch job</p></td><td rowspan="1" colspan="1"><p>Spring Batch template</p></td></tr><tr><td rowspan="1" colspan="1"><p>API Gateway</p></td><td rowspan="1" colspan="1"><p>Gateway-specific template</p></td></tr><tr><td rowspan="1" colspan="1"><p>Library</p></td><td rowspan="1" colspan="1"><p>Starter/library template</p></td></tr></tbody></table><p>Bu juda muhim. Chunki platform standardization degani bitta qolipni hammaga majburlash emas.</p><hr><h3>16. Versioning Strategy</h3><p>Platform starterlar ham versionlanadi.</p><p>Masalan:</p><pre><code class="language-text">company-platform-bom: 1.0.0
company-platform-bom: 1.1.0
company-platform-bom: 2.0.0</code></pre><p>Semantic versioning:</p><pre><code class="language-text">PATCH 1.0.1 → bug fix
MINOR 1.1.0 → backward compatible feature
MAJOR 2.0.0 → breaking change</code></pre><p>Product service <code>company-bom</code> versionini yangilaydi:</p><pre><code class="language-xml">&lt;company.platform.version&gt;1.4.0&lt;/company.platform.version&gt;</code></pre><p>Va barcha common dependencylar boshqariladi.</p><hr><h3>17. Platform upgrade process</h3><p>Yaxshi platform team shunday ishlaydi:</p><pre><code class="language-text">1. Yangi Spring Boot version chiqadi
2. Platform team compatibility tekshiradi
3. company-bom update qilinadi
4. starterlar test qilinadi
5. migration guide yoziladi
6. sample service yangilanadi
7. product teamlarga upgrade PR ochiladi</code></pre><p>Bu Spring Boot upgrade’ni har team alohida boshdan o‘tkazmasligini ta’minlaydi.</p><hr><h3>18. Security patch va CVE boshqaruvi</h3><p>Agar har service dependency versionni o‘zi boshqarsa:</p><pre><code class="language-text">Log4j / Netty / Jackson / Tomcat CVE chiqsa,
qaysi service qaysi versionda ekanini topish qiyin.</code></pre><p>BOM bilan:</p><pre><code class="language-text">company-bom update qilinadi
barcha service bir xil patched versionga o‘tadi
CI tekshiradi</code></pre><p>Platform standardization production xavfsizlik uchun ham kerak.</p><hr><h3>19. Developer Experience - DX</h3><p>Platform standardization faqat control emas. Developer hayotini osonlashtirishi kerak.</p><p>Yomon platform:</p><pre><code class="language-text">ko‘p qoida
kam dokumentatsiya
override qiyin
starterlar magic qiladi
xato chiqqanda tushunarsiz</code></pre><p>Yaxshi platform:</p><pre><code class="language-text">tez project yaratadi
defaultlar xavfsiz
documentation aniq
sample project bor
starterlar override bo‘ladi
error message tushunarli
migration guide bor</code></pre><p>Arxitektor uchun muhim savol:</p><blockquote><p>Platform team product teamlarga yordam beryaptimi yoki faqat cheklov qo‘yyaptimi?</p></blockquote><hr><h3>20. Real kompaniya misoli</h3><p>Tasavvur qilamiz, sizda 20 ta Spring Boot service bor.</p><p>Platform standardization oldin:</p><pre><code class="language-text">Har service boshqa log format.
Security config har xil.
Exception response har xil.
Actuator ba’zilarida ochiq, ba’zilarida yopiq.
Dependency versionlar chalkash.
Yangi service yaratish 2-3 kun.</code></pre><p>Platform standardization keyin:</p><pre><code class="language-text">Yangi service 15 daqiqada template’dan yaratiladi.
Security default tayyor.
Observability default tayyor.
Error response bir xil.
Dependencylar BOM orqali boshqariladi.
Architecture testlar bor.
CI pipeline standart.</code></pre><p>Bu Arxitektor darajasidagi katta yutuq.</p><hr><h3>21. Minimal platform starter misoli</h3><h4>Auto-configuration</h4><pre><code class="language-java">@AutoConfiguration
@EnableConfigurationProperties(CompanyProperties.class)
public class CompanyPlatformAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public CorrelationIdFilter correlationIdFilter() {
        return new CorrelationIdFilter();
    }

    @Bean
    @ConditionalOnMissingBean
    public ApiErrorFactory apiErrorFactory() {
        return new ApiErrorFactory();
    }
}</code></pre><h4>Properties</h4><pre><code class="language-java">@ConfigurationProperties(prefix = &quot;company.platform&quot;)
public record CompanyProperties(
        boolean correlationIdEnabled,
        String serviceName
) {}</code></pre><h4><code>application.yml</code></h4><pre><code class="language-yaml">company:
  platform:
    correlation-id-enabled: true
    service-name: order-service</code></pre><p>Bu starter barcha service’larda bir xil correlation-id va error handling beradi.</p><hr><h3>22. Platform standardization’da nimalarni majburlash kerak?</h3><h4>Majburiy bo‘lishi kerak</h4><pre><code class="language-text">dependency BOM
security baseline
structured logging
health/readiness endpoint
metrics
trace-id/correlation-id
standard error response
CI quality checks
architecture tests
Docker/build standard</code></pre><h4>Tavsiya bo‘lishi mumkin</h4><pre><code class="language-text">package structure
Hexagonal architecture
CQRS
Spring Modulith
specific mapper library
specific HTTP client style</code></pre><h4>Majburlamaslik kerak</h4><pre><code class="language-text">hamma service’da bir xil murakkab architecture
keraksiz abstraction
ortiqcha custom framework
teamga mos kelmaydigan pattern</code></pre><hr><h3>23. Eng ko‘p xatolar</h3><h4>Xato 1: Platform starter juda ko‘p magic qiladi</h4><p>Developer nima bo‘layotganini tushunmaydi.</p><p>Yaxshi starter:</p><pre><code class="language-text">default beradi
log qiladi
override imkon beradi
documentation beradi</code></pre><hr><h4>Xato 2: Product team ehtiyojini eshitmaslik</h4><p>Platform team:</p><pre><code class="language-text">&quot;Biz shunday dedik, shunday bo‘ladi.&quot;</code></pre><p>desa, teamlar workaround qiladi.</p><p>To‘g‘ri yondashuv:</p><pre><code class="language-text">Platform product sifatida yuritiladi.
Developerlar uning foydalanuvchisi.</code></pre><hr><h4>Xato 3: BOM update qilinmaydi</h4><p>BOM bor, lekin eskirib qolsa, foydasi kamayadi.</p><p>Platform team doim:</p><pre><code class="language-text">Spring Boot update
security patch
dependency compatibility
CVE monitoring</code></pre><p>qilishi kerak.</p><hr><h4>Xato 4: Convention faqat dokumentatsiyada qoladi</h4><p>Dokumentatsiya yaxshi, lekin test bo‘lmasa qoida buziladi.</p><p>Yaxshi:</p><pre><code class="language-text">README + template + ArchUnit + CI</code></pre><hr><h4>Xato 5: Hamma narsani common library’ga tashlash</h4><p><code>common</code> library haddan tashqari kattalashib ketadi:</p><pre><code class="language-text">common-utils
common-domain
common-service
common-repository</code></pre><p>Natija:</p><pre><code class="language-text">hamma service common’ga qaram
version upgrade xavfli
domainlar aralashadi</code></pre><p>Qoida:</p><blockquote><p>Common library faqat haqiqatan umumiy infrastructure narsalar uchun. Business logic common’ga chiqmasin.</p></blockquote><hr><h3>24. Amaliy roadmap</h3><p>Platform standardization’ni bosqichma-bosqich qilish:</p><pre><code class="language-text">1. Hozirgi service’larni audit qilish
2. Umumiy muammolarni topish
3. Minimal company-bom yaratish
4. company-observability-starter yaratish
5. company-security-starter yaratish
6. standard error handling starter qo‘shish
7. project template yaratish
8. ArchUnit architecture tests qo‘shish
9. CI pipeline standard qilish
10. migration guide yozish</code></pre><p>Birdan hammasini qilish shart emas. Avval eng ko‘p takrorlanadigan va eng xavfli joylardan boshlanadi:</p><pre><code class="language-text">Security
Observability
Dependency management
Error handling</code></pre><hr><h3>25. Arxitektor xulosasi</h3><p><strong>Platform standardization</strong> - Spring Boot projectlar soni ko‘payganda majburiy darajaga chiqadigan architecture mavzu.</p><p>Asosiy g‘oya:</p><pre><code class="language-text">Har team bir xil infrastructure muammolarini qayta yechmasin.
Har service production-ready default bilan boshlansin.
Dependency, security, logging, monitoring va structure markazdan boshqarilsin.
Lekin product teamlar kerak joyda override qila olsin.</code></pre><p>Eng muhim qoida:</p><blockquote><p>Platform standardization control uchun emas, tezlik va sifat uchun qilinadi.</p></blockquote><p>Yaxshi platform developerga shuni beradi:</p><pre><code class="language-text">kamroq boilerplate
kamroq xato
tezroq start
osonroq monitoring
xavfsizroq default
osonroq upgrade</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Multi-tenancy & SaaS Patterns]]></title>
            <link>https://blog.jtscorp.uz/multi-tenancy-saas-patterns</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/multi-tenancy-saas-patterns</guid>
            <pubDate>Thu, 02 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Multi-tenancy</strong> - bitta application bir nechta mijoz, kompaniya yoki organization’ga xizmat qilishi, lekin ularning ma’lumotlari bir-biridan ajratilgan bo‘lishi degani.</p><p>SaaS’da <code>tenant</code> odatda bitta mijoz tashkilot bo‘ladi:</p><pre><code class="language-text">Tenant A = Najot Ta&#039;lim
Tenant B = PDP Academy
Tenant C = IT Park</code></pre><p>Bitta system:</p><pre><code class="language-text">app.company.com</code></pre><p>lekin ichida har tenant alohida ko‘rinadi:</p><pre><code class="language-text">najot.company.com
pdp.company.com
itpark.company.com</code></pre><p>Bu mavzu quyidagilarni o‘z ichiga oladi: <strong>schema-per-tenant vs DB-per-tenant</strong>, <strong>TenantContext propagation</strong>, <strong>Dynamic DataSource routing</strong>, <strong>Hibernate multi-tenancy strategies</strong>, va <strong>tenant-aware caching</strong>.</p><hr><h2>1. Multi-tenancy nima uchun kerak?</h2><p>Agar biz SaaS product qilsangiz, har bir mijoz uchun alohida application ko‘tarish qimmat:</p><pre><code class="language-text">client-a-app
client-a-db

client-b-app
client-b-db

client-c-app
client-c-db</code></pre><p>Bu modelda:</p><ul><li><p>deployment ko‘payadi</p></li><li><p>monitoring murakkablashadi</p></li><li><p>upgrade qilish qiyinlashadi</p></li><li><p>infra xarajat oshadi</p></li><li><p>bug fix hamma joyga alohida chiqadi</p></li></ul><p>Multi-tenant SaaS’da esa:</p><pre><code class="language-text">bitta app cluster
ko‘p tenant
tenant bo‘yicha data isolation
bitta platform
bitta release pipeline</code></pre><hr><h2>2. Tenant nima?</h2><p><strong>Tenant</strong> - systemdan mustaqil foydalanuvchi tashkilot sifatida foydalanadigan birlik.</p><p>Masalan:</p><pre><code class="language-text">Tenant = company
Tenant = school
Tenant = marketplace seller
Tenant = bank branch
Tenant = franchise
Tenant = organization</code></pre><p>Application ichida deyarli hamma narsa tenant bilan bog‘lanadi:</p><pre><code class="language-text">users
orders
payments
products
roles
settings
subscriptions
invoices
audit logs</code></pre><hr><h2>3. Tenant aniqlash usullari</h2><p>Request kelganda system birinchi bo‘lib so‘raydi:</p><blockquote><p>Bu request qaysi tenantga tegishli?</p></blockquote><p>Tenant quyidagi joylardan olinishi mumkin.</p><hr><h4>1. Subdomain orqali</h4><pre><code class="language-text">najot.myapp.com
pdp.myapp.com</code></pre><p>Bu SaaS’da juda ko‘p ishlatiladi.</p><p>Afzallik:</p><pre><code class="language-text">tenant aniq
URL chiroyli
branding uchun qulay</code></pre><p>Kamchilik:</p><pre><code class="language-text">DNS/wildcard certificate kerak
local development biroz murakkab</code></pre><hr><h4>2. Path orqali</h4><pre><code class="language-text">/api/tenants/najot/orders
/api/tenants/pdp/orders</code></pre><p>Afzallik:</p><pre><code class="language-text">oddiy
test qilish oson
gateway bilan oson</code></pre><p>Kamchilik:</p><pre><code class="language-text">URL har doim tenant bilan ifloslanadi
xavfsizlikda ehtiyot kerak</code></pre><hr><h4>3. Header orqali</h4><pre><code class="language-http">X-Tenant-Id: najot</code></pre><p>Afzallik:</p><pre><code class="language-text">internal API uchun qulay
mobile/backend communication uchun yaxshi</code></pre><p>Kamchilik:</p><pre><code class="language-text">client headerni qalbakilashtirishi mumkin
auth bilan tekshirish shart</code></pre><hr><h4>4. JWT claim orqali</h4><pre><code class="language-json">{
  &quot;sub&quot;: &quot;user-123&quot;,
  &quot;tenant_id&quot;: &quot;najot&quot;,
  &quot;roles&quot;: [&quot;ADMIN&quot;]
}</code></pre><p>Bu eng xavfsiz variantlardan biri, agar token ishonchli identity provider tomonidan berilgan bo‘lsa.</p><p>Spring Security resource server multi-tenancy hujjatida multi-tenant resource server bir nechta bearer token verification strategiyasiga ega bo‘lishi va odatda tenant’ni resolve qilish hamda propagate qilish kerakligi aytiladi. (<a href="https://docs.spring.io/spring-security/reference/reactive/oauth2/resource-server/multitenancy.html?utm_source=chatgpt.com" title="OAuth 2.0 Resource Server Multi-tenancy">Home</a>)</p><hr><h4>4. Eng muhim qoida: tenantni clientdan ko‘r-ko‘rona olmaslik</h4><p>Yomon:</p><pre><code class="language-text">X-Tenant-Id: pdp
Authorization: user najot tenantiga tegishli</code></pre><p>Agar system faqat headerga ishonsa, user boshqa tenant ma’lumotiga kira olishi mumkin.</p><p>To‘g‘ri yondashuv:</p><pre><code class="language-text">1. User token tekshiriladi
2. Token ichidagi tenant yoki user membership tekshiriladi
3. Requestdagi tenant shu userga ruxsatlimi - tekshiriladi
4. Keyin TenantContext o‘rnatiladi</code></pre><p>Ya’ni:</p><blockquote><p>Tenant resolution - security decision bilan bog‘liq.</p></blockquote><hr><h2>5. Multi-tenancy data isolation modellari</h2><p>Asosiy 3 ta model bor:</p><pre><code class="language-text">1. Shared database + tenant_id column
2. Schema-per-tenant
3. Database-per-tenant</code></pre><hr><h2>6. Shared database + tenant_id column</h2><p>Bitta database, bitta schema, har table’da <code>tenant_id</code> bor.</p><pre><code class="language-text">orders table
 ├── id
 ├── tenant_id
 ├── customer_id
 ├── amount
 └── status</code></pre><p>Misol:</p><table><tbody><tr><th rowspan="1" colspan="1"><p style="text-align: center;">id</p></th><th rowspan="1" colspan="1"><p style="text-align: center;">tenant_id</p></th><th rowspan="1" colspan="1"><p style="text-align: center;">amount</p></th></tr><tr><td rowspan="1" colspan="1"><p style="text-align: center;">1</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">najot</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">100000</p></td></tr><tr><td rowspan="1" colspan="1"><p style="text-align: center;">2</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">pdp</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">250000</p></td></tr><tr><td rowspan="1" colspan="1"><p style="text-align: center;">3</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">najot</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">90000</p></td></tr></tbody></table><p>Query:</p><pre><code class="language-sql">SELECT *
FROM orders
WHERE tenant_id = &#039;najot&#039;;</code></pre><hr><h4>Afzalliklari</h4><pre><code class="language-text">eng sodda
eng arzon
bitta migration
bitta connection pool
analytics/report qilish osonroq
kichik/medium SaaS uchun yaxshi</code></pre><hr><h4>Kamchiliklari</h4><pre><code class="language-text">data leak xavfi yuqori
har query tenant filter talab qiladi
katta tenant boshqa tenantlarga ta’sir qilishi mumkin
backup/restore tenant bo‘yicha qiyin
compliance talablariga mos kelmasligi mumkin</code></pre><p>Eng katta xavf:</p><pre><code class="language-sql">SELECT * FROM orders;</code></pre><p>Agar <code>tenant_id</code> filter unutilsa, hamma tenant data chiqadi.</p><hr><h2>7. Shared DB modelda himoya</h2><h4>1. Har table’da tenant_id</h4><pre><code class="language-sql">CREATE TABLE orders (
    id BIGSERIAL PRIMARY KEY,
    tenant_id VARCHAR(64) NOT NULL,
    amount NUMERIC NOT NULL
);</code></pre><h4>2. Composite index</h4><pre><code class="language-sql">CREATE INDEX idx_orders_tenant_id_id
ON orders (tenant_id, id);</code></pre><h4>3. Unique constraint tenant bilan</h4><p>Yomon:</p><pre><code class="language-sql">CREATE UNIQUE INDEX ux_users_email
ON users (email);</code></pre><p>Bu global email unique qiladi.</p><p>Yaxshi:</p><pre><code class="language-sql">CREATE UNIQUE INDEX ux_users_tenant_email
ON users (tenant_id, email);</code></pre><p>Chunki turli tenantlarda bir xil email bo‘lishi mumkin yoki bo‘lmasligi biznes qaroriga bog‘liq.</p><hr><h2>8. Schema-per-tenant</h2><p>Bitta database, lekin har tenant alohida schema’da.</p><pre><code class="language-text">database: saas_db

schema: tenant_najot
 ├── orders
 ├── users
 └── payments

schema: tenant_pdp
 ├── orders
 ├── users
 └── payments</code></pre><p>Query:</p><pre><code class="language-sql">SELECT * FROM tenant_najot.orders;</code></pre><p>yoki connection uchun current schema o‘rnatiladi.</p><hr><h4>Afzalliklari</h4><pre><code class="language-text">data isolation shared-table modeldan kuchliroq
tenant bo‘yicha backup/restore osonroq
bitta DB server ichida boshqariladi
ba’zi compliance holatlariga mosroq</code></pre><hr><h4>Kamchiliklari</h4><pre><code class="language-text">migration har schema uchun yuradi
schema soni ko‘payganda boshqarish qiyinlashadi
connection/session current schema to‘g‘ri o‘rnatilishi shart
cross-tenant reporting murakkabroq</code></pre><hr><h2>9. Database-per-tenant</h2><p>Har tenant uchun alohida database.</p><pre><code class="language-text">tenant_najot_db
tenant_pdp_db
tenant_itpark_db</code></pre><p>Application requestga qarab kerakli database’ga ulanadi.</p><hr><h4>Afzalliklari</h4><pre><code class="language-text">eng kuchli isolation
tenant bo‘yicha backup/restore oson
katta tenantni alohida scale qilish mumkin
compliance uchun yaxshi
noisy neighbor muammosi kamayadi</code></pre><hr><h4>Kamchiliklari</h4><pre><code class="language-text">eng qimmat
ko‘p DataSource/connection pool kerak
migration murakkab
monitoring murakkab
yangi tenant provisioning murakkab
cross-tenant analytics qiyin</code></pre><hr><h2>10. Model tanlash jadvali</h2><table><tbody><tr><th rowspan="1" colspan="1"><p style="text-align: center;">Model</p></th><th rowspan="1" colspan="1"><p style="text-align: center;">Isolation</p></th><th rowspan="1" colspan="1"><p style="text-align: center;">Cost</p></th><th rowspan="1" colspan="1"><p style="text-align: center;">Complexity</p></th><th rowspan="1" colspan="1"><p style="text-align: center;">Qachon yaxshi</p></th></tr><tr><td rowspan="1" colspan="1"><p style="text-align: center;">Shared DB + <code>tenant_id</code></p></td><td rowspan="1" colspan="1"><p style="text-align: center;">Pastroq</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">Past</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">Past/O‘rta</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">Kichik/medium SaaS</p></td></tr><tr><td rowspan="1" colspan="1"><p style="text-align: center;">Schema-per-tenant</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">O‘rta/Yuqori</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">O‘rta</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">O‘rta/Yuqori</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">B2B SaaS, o‘rta isolation</p></td></tr><tr><td rowspan="1" colspan="1"><p style="text-align: center;">DB-per-tenant</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">Yuqori</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">Yuqori</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">Yuqori</p></td><td rowspan="1" colspan="1"><p style="text-align: center;">Enterprise, compliance, yirik tenantlar</p></td></tr></tbody></table><p>Arxitektor qaror:</p><pre><code class="language-text">Agar tenantlar ko‘p va kichik bo‘lsa → shared DB + tenant_id
Agar B2B va isolation muhim bo‘lsa → schema-per-tenant
Agar enterprise/compliance talab kuchli bo‘lsa → DB-per-tenant</code></pre><hr><h2>11. Hybrid model</h2><p>Real hayotda ko‘pincha hybrid bo‘ladi:</p><pre><code class="language-text">small tenants → shared database
enterprise tenants → dedicated database
regulated tenants → dedicated cluster</code></pre><p>Masalan:</p><pre><code class="language-text">Free/Starter plan      → shared DB
Business plan          → schema-per-tenant
Enterprise/Government  → DB-per-tenant</code></pre><p>Bu SaaS monetization bilan ham bog‘liq.</p><hr><h2>12. TenantContext nima?</h2><p><code>TenantContext</code> - hozirgi request qaysi tenantga tegishli ekanini application ichida saqlash usuli.</p><p>Imperative Spring MVC’da ko‘p hollarda <code>ThreadLocal</code> ishlatiladi:</p><pre><code class="language-java">public final class TenantContext {

    private static final ThreadLocal&lt;String&gt; CURRENT_TENANT = new ThreadLocal&lt;&gt;();

    private TenantContext() {}

    public static void setTenantId(String tenantId) {
        CURRENT_TENANT.set(tenantId);
    }

    public static String getTenantId() {
        return CURRENT_TENANT.get();
    }

    public static void clear() {
        CURRENT_TENANT.remove();
    }
}</code></pre><hr><h2>13. Tenant filter</h2><p>Request boshida tenant aniqlanadi:</p><pre><code class="language-java">@Component
public class TenantFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain
    ) throws ServletException, IOException {

        try {
            String tenantId = resolveTenant(request);

            // security check: user shu tenantga tegishlimi?
            TenantContext.setTenantId(tenantId);

            filterChain.doFilter(request, response);
        } finally {
            TenantContext.clear();
        }
    }

    private String resolveTenant(HttpServletRequest request) {
        return request.getHeader(&quot;X-Tenant-Id&quot;);
    }
}</code></pre><p>Eng muhim joy:</p><pre><code class="language-java">finally {
    TenantContext.clear();
}</code></pre><p>Agar <code>clear()</code> qilinmasa, thread pool sabab keyingi request eski tenant bilan ishlashi mumkin.</p><hr><h2>14. Dynamic DataSource Routing</h2><p>DB-per-tenant yoki ba’zi schema-per-tenant holatlarida requestga qarab DataSource tanlanadi.</p><p>Spring’da bu uchun <code>AbstractRoutingDataSource</code> ishlatiladi. Uning <code>determineCurrentLookupKey()</code> metodi odatda thread-bound context’dan lookup key olish uchun implement qilinadi. (<a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSource.html?utm_source=chatgpt.com" title="Class AbstractRoutingDataSource">Home</a>)</p><p>Misol:</p><pre><code class="language-java">public class TenantRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.getTenantId();
    }
}</code></pre><p>Config:</p><pre><code class="language-java">@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        Map&lt;Object, Object&gt; dataSources = new HashMap&lt;&gt;();

        dataSources.put(&quot;najot&quot;, najotDataSource());
        dataSources.put(&quot;pdp&quot;, pdpDataSource());

        TenantRoutingDataSource routingDataSource = new TenantRoutingDataSource();
        routingDataSource.setTargetDataSources(dataSources);
        routingDataSource.setDefaultTargetDataSource(defaultDataSource());

        return routingDataSource;
    }
}</code></pre><p>Flow:</p><pre><code class="language-text">Request keldi
   ↓
TenantFilter tenantId aniqladi
   ↓
TenantContext.set(&quot;najot&quot;)
   ↓
Repository query ishladi
   ↓
AbstractRoutingDataSource determineCurrentLookupKey()
   ↓
najot DataSource tanlandi</code></pre><hr><h2>15. Dynamic DataSource’da katta ehtiyot</h2><h4>Muammo 1: Transaction oldin ochilib ketishi</h4><p>Agar transaction boshlanganda tenant hali set qilinmagan bo‘lsa, noto‘g‘ri database tanlanadi.</p><p>To‘g‘ri tartib:</p><pre><code class="language-text">Security filter
Tenant resolution filter
Transaction
Repository</code></pre><hr><h4>Muammo 2: Connection pool soni portlab ketadi</h4><p>Agar 500 tenant va har birida Hikari pool bo‘lsa:</p><pre><code class="language-text">500 tenants × 10 connections = 5000 DB connections</code></pre><p>Bu database’ni yiqitishi mumkin.</p><p>Yechimlar:</p><pre><code class="language-text">tenantlarni plan bo‘yicha ajratish
lazy DataSource creation
pool size limit
inactive tenant pool close
shared pool strategy
dedicated DB faqat enterprise tenantlarga</code></pre><hr><h4>Muammo 3: Runtime tenant qo‘shish</h4><p>Yangi tenant ro‘yxatdan o‘tsa, DataSource map yangilanishi kerak.</p><p>Oddiy <code>setTargetDataSources</code> static map bilan qiyin bo‘lishi mumkin.</p><p>Production’da ko‘pincha:</p><pre><code class="language-text">TenantRegistry
DataSourceProvider
cache with TTL
dynamic pool creation
tenant provisioning workflow</code></pre><p>kerak bo‘ladi.</p><hr><h2>16. Hibernate multi-tenancy strategies</h2><p>Hibernate multi-tenancy uchun asosan quyidagi yondashuvlar ishlatiladi:</p><pre><code class="language-text">DATABASE
SCHEMA
DISCRIMINATOR / tenant_id</code></pre><p>Hibernate hujjatlarida multi-tenancy support second-level cache bilan ishlashi, cache key ichida tenant identifier kodlanishi aytiladi. (<a href="https://docs.hibernate.org/orm/4.3/devguide/en-US/html/ch16.html?utm_source=chatgpt.com" title="Chapter 16. Multi-tenancy">docs.hibernate.org</a>)</p><hr><h4>DATABASE strategy</h4><p>Har tenant alohida database connection oladi.</p><pre><code class="language-text">tenant_id → database</code></pre><p>Yaxshi:</p><pre><code class="language-text">isolation kuchli
enterprise tenantlar uchun yaxshi</code></pre><p>Qiyin:</p><pre><code class="language-text">ko‘p connection pool
migration murakkab
tenant provisioning murakkab</code></pre><hr><h4>SCHEMA strategy</h4><p>Bitta database, har tenant alohida schema.</p><pre><code class="language-text">tenant_id → schema</code></pre><p>Implementation:</p><pre><code class="language-text">MultiTenantConnectionProvider
CurrentTenantIdentifierResolver</code></pre><p><code>CurrentTenantIdentifierResolver</code> current tenantni beradi.</p><hr><h4>DISCRIMINATOR strategy</h4><p>Bitta table ichida <code>tenant_id</code> column.</p><pre><code class="language-text">tenant_id → row filter</code></pre><p>Bu eng arzon, lekin eng ehtiyot talab qiladigan model.</p><hr><h2>17. Tenant-aware caching</h2><p>Cache multi-tenancy’da juda xavfli joy.</p><p>Yomon cache key:</p><pre><code class="language-java">@Cacheable(cacheNames = &quot;orders&quot;, key = &quot;#orderId&quot;)
public Order getOrder(Long orderId) {
    ...
}</code></pre><p>Agar <code>orderId = 10</code> ikki tenantda ham bo‘lsa:</p><pre><code class="language-text">najot order 10
pdp order 10</code></pre><p>cache noto‘g‘ri tenant data qaytarishi mumkin.</p><p>Yaxshi:</p><pre><code class="language-java">@Cacheable(
    cacheNames = &quot;orders&quot;,
    key = &quot;T(com.company.TenantContext).getTenantId() + &#039;:&#039; + #orderId&quot;
)
public Order getOrder(Long orderId) {
    ...
}</code></pre><p>Yoki custom <code>KeyGenerator</code>:</p><pre><code class="language-java">@Component(&quot;tenantAwareKeyGenerator&quot;)
public class TenantAwareKeyGenerator implements KeyGenerator {

    @Override
    public Object generate(Object target, Method method, Object... params) {
        return TenantContext.getTenantId() + &quot;:&quot; +
                method.getName() + &quot;:&quot; +
                Arrays.deepToString(params);
    }
}</code></pre><p>Usage:</p><pre><code class="language-java">@Cacheable(
    cacheNames = &quot;orders&quot;,
    keyGenerator = &quot;tenantAwareKeyGenerator&quot;
)
public Order getOrder(Long orderId) {
    ...
}</code></pre><p>Qoida:</p><blockquote><p>Cache key ichida tenant bo‘lmasa, multi-tenant system’da data leak xavfi bor.</p></blockquote><hr><h2>18. Tenant-aware authorization</h2><p>Multi-tenancy faqat database emas. Authorization ham tenant-aware bo‘lishi kerak.</p><p>Masalan user:</p><pre><code class="language-text">Ali → najot tenantida ADMIN
Ali → pdp tenantida VIEWER</code></pre><p>Demak role global emas, tenantga bog‘langan.</p><p>Yomon model:</p><pre><code class="language-text">user_roles:
ali = ADMIN</code></pre><p>Yaxshi model:</p><pre><code class="language-text">tenant_user_roles:
ali + najot = ADMIN
ali + pdp = VIEWER</code></pre><p>Spring Security method-level authorization annotationlar orqali method, class va interface darajasida authorization qo‘llash imkonini beradi. (<a href="https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html?utm_source=chatgpt.com" title="Method Security">Home</a>)</p><p>Masalan:</p><pre><code class="language-java">@PreAuthorize(&quot;@tenantSecurity.canAccessOrder(#orderId, authentication)&quot;)
public OrderDto getOrder(Long orderId) {
    ...
}</code></pre><hr><h2>19. TenantContext propagation: <code>@Async</code>, scheduler, messaging</h2><p><code>TenantContext</code> faqat HTTP requestda emas, boshqa joylarda ham kerak bo‘ladi:</p><pre><code class="language-text">@Async
Kafka listener
Rabbit listener
Scheduled job
Domain event listener
WebFlux reactive flow
Batch job</code></pre><hr><h4><code>@Async</code> muammosi</h4><p><code>ThreadLocal</code> boshqa threadda avtomatik o‘tmaydi.</p><pre><code class="language-java">@Async
public void sendEmail() {
    TenantContext.getTenantId(); // null bo‘lishi mumkin
}</code></pre><p>Yechim:</p><pre><code class="language-text">TaskDecorator orqali tenantni ko‘chirish</code></pre><p>Misol:</p><pre><code class="language-java">public class TenantTaskDecorator implements TaskDecorator {

    @Override
    public Runnable decorate(Runnable runnable) {
        String tenantId = TenantContext.getTenantId();

        return () -&gt; {
            try {
                TenantContext.setTenantId(tenantId);
                runnable.run();
            } finally {
                TenantContext.clear();
            }
        };
    }
}</code></pre><p>Executor config:</p><pre><code class="language-java">@Bean
public ThreadPoolTaskExecutor applicationTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setTaskDecorator(new TenantTaskDecorator());
    return executor;
}</code></pre><hr><h2>20. Reactive WebFlux’da TenantContext</h2><p>WebFlux’da <code>ThreadLocal</code> xavfli, chunki request bitta thread’da boshlanib boshqa thread’da davom etishi mumkin.</p><p>Reactor’da context uchun <code>Context</code> ishlatiladi. Reactor hujjatlarida <code>contextCapture</code> operatori <code>ThreadLocal</code> qiymatlarini subscription vaqtida capture qilib Reactor Context’ga o‘tkazishi mumkinligi aytiladi. (<a href="https://projectreactor.io/docs/core/release/reference/advanced-contextPropagation.html?utm_source=chatgpt.com" title="Context-Propagation Support">projectreactor.io</a>) Micrometer Context Propagation hujjatida context qiymatlari Reactor context va ThreadLocal orasida ko‘chirilishi mumkinligi ko‘rsatiladi. (<a href="https://docs.micrometer.io/context-propagation/reference/purpose.html?utm_source=chatgpt.com" title="Purpose :: Micrometer Context Propagation">Micrometer Documentation</a>)</p><p>Reactive yondashuv:</p><pre><code class="language-java">return chain.filter(exchange)
        .contextWrite(context -&gt; context.put(&quot;tenantId&quot;, tenantId));</code></pre><p>Keyin:</p><pre><code class="language-java">Mono.deferContextual(ctx -&gt; {
    String tenantId = ctx.get(&quot;tenantId&quot;);
    return service.process(tenantId);
});</code></pre><p>Qoida:</p><pre><code class="language-text">Spring MVC → ThreadLocal ishlashi mumkin
Spring WebFlux → Reactor Context ishlatish kerak</code></pre><hr><h2>21. Kafka/Rabbit message’da tenant</h2><p>Event yoki message yuborilganda tenant ham ketishi kerak.</p><p>Yomon:</p><pre><code class="language-json">{
  &quot;orderId&quot;: 123,
  &quot;status&quot;: &quot;PAID&quot;
}</code></pre><p>Yaxshi:</p><pre><code class="language-json">{
  &quot;tenantId&quot;: &quot;najot&quot;,
  &quot;orderId&quot;: 123,
  &quot;status&quot;: &quot;PAID&quot;
}</code></pre><p>Yoki message header:</p><pre><code class="language-text">X-Tenant-Id: najot</code></pre><p>Consumer:</p><pre><code class="language-java">@KafkaListener(topics = &quot;orders&quot;)
public void listen(OrderPaidEvent event) {
    try {
        TenantContext.setTenantId(event.tenantId());
        orderService.handlePaid(event);
    } finally {
        TenantContext.clear();
    }
}</code></pre><p>Qoida:</p><blockquote><p>Async message’da tenant bo‘lmasa, consumer qaysi tenant database yoki cache bilan ishlashini bilmaydi.</p></blockquote><hr><h2>22. Tenant provisioning</h2><p>SaaS’da yangi tenant yaratish oddiy insert emas. Bu workflow.</p><p>Masalan:</p><pre><code class="language-text">1. Tenant record yaratish
2. Plan/subscription tanlash
3. DB/schema yaratish
4. Migration yuritish
5. Default roles yaratish
6. Admin user yaratish
7. Default settings yaratish
8. DNS/subdomain sozlash
9. Welcome email yuborish
10. Tenant status ACTIVE qilish</code></pre><p>Tenant state:</p><pre><code class="language-text">CREATING
MIGRATING
ACTIVE
SUSPENDED
DELETING
FAILED</code></pre><p>Agar provisioning o‘rtada yiqilsa, retry va cleanup kerak.</p><hr><h2>23. Migration strategy</h2><h4>Shared DB</h4><p>Migration oddiyroq:</p><pre><code class="language-text">Flyway/Liquibase bitta database uchun yuradi</code></pre><h4>Schema-per-tenant</h4><p>Migration har schema uchun yurishi kerak:</p><pre><code class="language-text">tenant_najot → V1, V2, V3
tenant_pdp   → V1, V2, V3
tenant_itpark → V1, V2, V3</code></pre><h4>DB-per-tenant</h4><p>Migration har database uchun yuradi:</p><pre><code class="language-text">tenant_najot_db
tenant_pdp_db
tenant_itpark_db</code></pre><p>Bu yerda migration orchestration kerak:</p><pre><code class="language-text">qaysi tenant qaysi versionda?
migration failed bo‘lsa nima bo‘ladi?
rolling migration mumkinmi?
downtime kerakmi?</code></pre><hr><h2>24. Tenant-aware observability</h2><p>Log, metric va trace tenant-aware bo‘lishi kerak, lekin ehtiyot bilan.</p><p>Log:</p><pre><code class="language-json">{
  &quot;service&quot;: &quot;order-service&quot;,
  &quot;tenantId&quot;: &quot;najot&quot;,
  &quot;traceId&quot;: &quot;abc123&quot;,
  &quot;message&quot;: &quot;Order created&quot;
}</code></pre><p>Metrics’da esa tenant label ehtiyot talab qiladi.</p><p>Yomon:</p><pre><code class="language-text">http_requests_total{tenantId=&quot;tenant-123456&quot;}</code></pre><p>Agar tenantlar juda ko‘p bo‘lsa, metric cardinality portlab ketadi.</p><p>Yaxshi:</p><pre><code class="language-text">tenant_tier=&quot;enterprise&quot;
region=&quot;uz&quot;</code></pre><p>yoki faqat enterprise tenantlar uchun alohida metric.</p><p>Qoida:</p><blockquote><p>Loglarda tenantId foydali. Metrics label’da tenantId ehtiyot bilan ishlatiladi.</p></blockquote><hr><h2>25. Tenant-aware rate limiting</h2><p>SaaS’da har tenantga limit bo‘lishi mumkin:</p><pre><code class="language-text">Free plan: 100 req/min
Business: 1000 req/min
Enterprise: custom</code></pre><p>Rate limit key:</p><pre><code class="language-text">tenantId + endpoint</code></pre><p>Masalan:</p><pre><code class="language-text">najot:/api/orders → 1000 req/min
pdp:/api/orders   → 100 req/min</code></pre><p>Bu noisy neighbor muammosini kamaytiradi.</p><hr><h2>26. Tenant-aware feature flags</h2><p>SaaS’da feature tenant bo‘yicha yoqiladi:</p><pre><code class="language-text">tenant najot → AI_REPORTS enabled
tenant pdp → AI_REPORTS disabled
tenant enterprise-x → BETA_CHECKOUT enabled</code></pre><p>Model:</p><pre><code class="language-text">tenant_features
 ├── tenant_id
 ├── feature_key
 └── enabled</code></pre><p>Feature flag check:</p><pre><code class="language-java">if (featureFlags.isEnabled(&quot;AI_REPORTS&quot;, TenantContext.getTenantId())) {
    // new feature
}</code></pre><hr><h2>27. Tenant configuration</h2><p>Har tenantda alohida settings bo‘lishi mumkin:</p><pre><code class="language-text">branding
currency
timezone
language
payment provider
notification templates
tax rules
invoice prefix
SLA plan</code></pre><p>Bu settings cache qilinadi, lekin cache tenant-aware bo‘lishi shart.</p><hr><h2>28. Data deletion va compliance</h2><p>Multi-tenant SaaS’da tenant o‘chirish katta masala.</p><p>Shared DB’da:</p><pre><code class="language-sql">DELETE FROM orders WHERE tenant_id = &#039;najot&#039;;
DELETE FROM users WHERE tenant_id = &#039;najot&#039;;
DELETE FROM payments WHERE tenant_id = &#039;najot&#039;;</code></pre><p>Bu xavfli va murakkab.</p><p>DB-per-tenant’da:</p><pre><code class="language-text">drop database tenant_najot_db</code></pre><p>ancha toza, lekin backup retention va audit talablarini hisobga olish kerak.</p><p>Arxitektor qaror:</p><blockquote><p>Agar tenant offboarding, compliance, data residency muhim bo‘lsa, DB-per-tenant yoki schema-per-tenant kuchliroq.</p></blockquote><hr><h2>29. Data residency</h2><p>Ba’zi tenantlar data ma’lum regionda turishini talab qiladi:</p><pre><code class="language-text">UZ region
EU region
US region</code></pre><p>Bu holda tenant registry kerak:</p><pre><code class="language-text">tenant_id → region → cluster/database</code></pre><p>Masalan:</p><pre><code class="language-text">najot → uz-central → db-uz-1
enterprise-eu → eu-west → db-eu-2</code></pre><p>Request routing:</p><pre><code class="language-text">Gateway tenantni aniqlaydi
tenant regionni topadi
tegishli clusterga yo‘naltiradi</code></pre><hr><h2>30. Real structure: Spring Boot SaaS</h2><pre><code class="language-text">com.company.saas
 ├── tenant
 │    ├── Tenant.java
 │    ├── TenantRegistry.java
 │    ├── TenantContext.java
 │    ├── TenantFilter.java
 │    └── TenantProvisioningService.java
 │
 ├── order
 │    ├── api
 │    ├── application
 │    ├── domain
 │    └── adapter
 │
 ├── payment
 │    ├── api
 │    ├── application
 │    ├── domain
 │    └── adapter
 │
 └── platform
      ├── security
      ├── datasource
      ├── cache
      └── observability</code></pre><p><code>tenant</code> module infrastructure emas, core platform module bo‘ladi.</p><hr><h2>31. Minimal shared DB implementation</h2><p>Entity:</p><pre><code class="language-java">@MappedSuperclass
public abstract class TenantAwareEntity {

    @Column(name = &quot;tenant_id&quot;, nullable = false, updatable = false)
    private String tenantId;

    @PrePersist
    void assignTenant() {
        this.tenantId = TenantContext.getTenantId();
    }

    public String getTenantId() {
        return tenantId;
    }
}</code></pre><p>Order:</p><pre><code class="language-java">@Entity
@Table(name = &quot;orders&quot;)
public class OrderEntity extends TenantAwareEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private BigDecimal amount;
}</code></pre><p>Lekin bu yetarli emas. Read querylarda ham tenant filter bo‘lishi kerak.</p><p>Repository method:</p><pre><code class="language-java">List&lt;OrderEntity&gt; findByTenantIdAndStatus(String tenantId, OrderStatus status);</code></pre><p>Yoki specification/global filter yondashuvi ishlatiladi.</p><hr><h2>32. Eng xavfli xatolar</h2><h4>Xato 1: Tenant filter unutilishi</h4><pre><code class="language-java">repository.findById(orderId);</code></pre><p>Yomon, chunki <code>orderId</code> boshqa tenantga tegishli bo‘lishi mumkin.</p><p>Yaxshi:</p><pre><code class="language-java">repository.findByIdAndTenantId(orderId, TenantContext.getTenantId());</code></pre><hr><h4>Xato 2: Cache key’da tenant yo‘q</h4><pre><code class="language-java">key = &quot;#id&quot;</code></pre><p>Yomon.</p><p>Yaxshi:</p><pre><code class="language-java">key = &quot;tenantId + &#039;:&#039; + #id&quot;</code></pre><hr><h4>Xato 3: TenantContext clear qilinmaydi</h4><p>Thread pool sabab bir tenant ikkinchi tenant requestiga sizib o‘tishi mumkin.</p><hr><h4>Xato 4: Headerga ko‘r-ko‘rona ishonish</h4><p><code>X-Tenant-Id</code> har doim token/user membership bilan tekshirilishi kerak.</p><hr><h4>Xato 5: Migration strategy oldindan o‘ylanmagan</h4><p>Tenantlar ko‘paygandan keyin schema yoki database migration juda og‘ir bo‘ladi.</p><hr><h4>Xato 6: Metrics’da tenantId label qilish</h4><p>Tenantlar ko‘p bo‘lsa, monitoring system cardinality muammosiga tushadi.</p><hr><h2>33. Arxitektor decision checklist</h2><p>Multi-tenancy tanlashdan oldin savollar:</p><pre><code class="language-text">Tenantlar soni qancha bo‘ladi?
Har tenant data hajmi qancha?
Enterprise tenant bormi?
Compliance talablari bormi?
Data residency kerakmi?
Tenant bo‘yicha backup/restore kerakmi?
Cross-tenant analytics kerakmi?
Har tenant alohida customization qiladimi?
Rate limit tenant bo‘yicha bo‘ladimi?
Tenant offboarding qanday ishlaydi?</code></pre><hr><h2>34. Qisqa tavsiya</h2><h4>Startup SaaS uchun</h4><pre><code class="language-text">Shared DB + tenant_id
strict repository convention
tenant-aware cache
tenant-aware auth
strong tests</code></pre><h3>B2B SaaS uchun</h3><pre><code class="language-text">Schema-per-tenant
tenant provisioning workflow
migration orchestration
tenant-aware observability</code></pre><h3>Enterprise SaaS uchun</h3><pre><code class="language-text">Hybrid model
enterprise tenants → DB-per-tenant
small tenants → shared/schema
data residency
dedicated backup/restore</code></pre><hr><h2>35. Arxitektor xulosasi</h2><p><strong>Multi-tenancy &amp; SaaS patterns</strong> - bu faqat <code>tenant_id</code> column qo‘shish emas.</p><p>Bu quyidagi qarorlar majmuasi:</p><pre><code class="language-text">tenant qanday aniqlanadi?
tenant security qanday tekshiriladi?
data isolation qaysi modelda bo‘ladi?
cache tenant-awaremi?
async flow’da tenant yo‘qolmaydimi?
migration qanday yuradi?
tenant provisioning qanday ishlaydi?
observability tenantni ko‘rsatadimi?
enterprise tenantlar qanday ajratiladi?</code></pre><p>Eng muhim qoida:</p><blockquote><p>Multi-tenant system’da har bir query, cache, message, log, permission va background job tenant kontekstini hisobga olishi kerak.</p></blockquote><p>Qisqa formula:</p><pre><code class="language-text">Tenant resolution
+ Tenant authorization
+ Tenant context propagation
+ Tenant data isolation
+ Tenant-aware cache/security/observability
= Production-ready SaaS architecture</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Migration & Upgrade Strategy]]></title>
            <link>https://blog.jtscorp.uz/migration-upgrade-strategy</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/migration-upgrade-strategy</guid>
            <pubDate>Thu, 02 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Migration &amp; upgrade strategy</strong> - bu Spring Boot yoki Java application’ni yangi versiyaga ko‘chirishni &quot;dependency versionni almashtirish&quot; deb emas, <strong>risk boshqaruvi, compatibility, rollout, test va rollback strategiyasi</strong> sifatida ko‘rish.</p><p>Bu mavzu quyidagilarni o‘z ichiga oladi: <strong>Spring Boot 2 → 3 migration path</strong>, <strong>Java EE / Jakarta EE namespace migration</strong>, <strong>Strangler Fig pattern</strong>, <strong>dependency convergence &amp; CVE management</strong>, va <strong>API backward compatibility</strong>.</p><hr><h3>1. Migration nima?</h3><p>Migration - tizimni eski texnologik holatdan yangi holatga ko‘chirish.</p><p>Misollar:</p><pre><code class="language-text">Spring Boot 2.7 → Spring Boot 3.x
Java 11 → Java 17 / 21
javax.* → jakarta.*
Spring Security 5 → Spring Security 6
Hibernate 5 → Hibernate 6
Monolith → Modular monolith
Monolith → Microservices
REST API v1 → REST API v2
Old database schema → new database schema</code></pre><p>Arxitektor savoli:</p><blockquote><p>&quot;Qanday qilib tizimni yangilaymiz, lekin production’ni buzmaymiz?&quot;</p></blockquote><hr><h3>2. Upgrade nima?</h3><p>Upgrade - mavjud dependency, framework yoki runtime’ni yangi versiyaga ko‘tarish.</p><p>Masalan:</p><pre><code class="language-xml">&lt;spring-boot.version&gt;2.7.18&lt;/spring-boot.version&gt;</code></pre><p>dan:</p><pre><code class="language-xml">&lt;spring-boot.version&gt;3.3.x&lt;/spring-boot.version&gt;</code></pre><p>yoki:</p><pre><code class="language-xml">&lt;spring-boot.version&gt;3.5.x&lt;/spring-boot.version&gt;</code></pre><p>ga o‘tish.</p><p>Spring Boot rasmiy upgrade hujjatlarida 2.x’dan ko‘tarilishda Spring Boot migration guide va release notes’ni tekshirish tavsiya qilinadi. (<a href="https://docs.spring.io/spring-boot/upgrading.html?utm_source=chatgpt.com" title="Upgrading Spring Boot">Home</a>)</p><hr><h3>3. Arxitektor darajada migration nimani anglatadi?</h3><p>Developerlar odatda ko‘pincha shunday o‘ylaydi:</p><pre><code class="language-text">versionni almashtiramiz
compile errorlarni tuzatamiz
testdan o‘tkazamiz
deploy qilamiz</code></pre><p>Arxitektorlar esa kengroq qaraydi:</p><pre><code class="language-text">qaysi service birinchi upgrade qilinadi?
qaysi dependencylar conflict beradi?
qaysi API backward compatible qoladi?
rollback qanday bo‘ladi?
database migration qaytariladimi?
clientlar buzilmaydimi?
security patchlar yopildimi?
monitoring migrationdan keyin ishlaydimi?</code></pre><p>Ya’ni migration - texnik refactor emas, <strong>production risk management</strong>.</p><hr><h3>4. Nega upgrade strategiya kerak?</h3><p>Agar upgrade tartibsiz qilinsa:</p><pre><code class="language-text">service compile bo‘lmaydi
runtime’da ClassNotFoundException chiqadi
javax/jakarta conflict bo‘ladi
security config buziladi
Hibernate querylar boshqacha ishlaydi
API response o‘zgarib ketadi
clientlar yiqiladi
rollback qiyinlashadi</code></pre><p>Katta system’da eng xavfli holat:</p><pre><code class="language-text">&quot;Hammasini bir PR’da upgrade qilamiz.&quot;</code></pre><p>To‘g‘ri strategiya:</p><pre><code class="language-text">audit
step-by-step upgrade
compatibility layer
test coverage
canary rollout
monitoring
rollback plan</code></pre><hr><h3>5. Spring Boot 2 → 3 migration path</h3><p>Spring Boot 3 migration eng katta o‘zgarishlardan biri, chunki u:</p><pre><code class="language-text">Java 17+ talab qiladi
Spring Framework 6 asosida ishlaydi
Jakarta EE 9 API ishlatadi
javax.* o‘rniga jakarta.* ishlatadi
Spring Security 6 o‘zgarishlari bor
Hibernate 6 o‘zgarishlari bor</code></pre><p>Spring jamoasi Spring Boot 3.0 Spring Framework 6.0 asosida bo‘lishi, Java 17 yoki undan yuqorini talab qilishi va Jakarta EE 9 <code>jakarta.*</code> API’lariga o‘tishini e’lon qilgan. (<a href="https://spring.io/blog/2022/05/24/preparing-for-spring-boot-3-0?utm_source=chatgpt.com" title="Preparing for Spring Boot 3.0">Home</a>)</p><hr><h4>Tavsiya migration yo‘li</h4><p>Spring Boot 3 migration guide Spring Boot 3.0&#039;ga o‘tishdan oldin eng oxirgi <code>2.7.x</code> versiyaga upgrade qilishni tavsiya qiladi. (<a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide?utm_source=chatgpt.com" title="Spring Boot 3.0 Migration Guide">GitHub</a>)</p><p>Amaliy yo‘l:</p><pre><code class="language-text">1. Spring Boot 2.x ichida eng oxirgi 2.7.x ga o‘tish
2. Java versionni 17+ qilish
3. Deprecated API’larni tozalash
4. Spring Security 5.8 migration qilish
5. javax.* importlarni jakarta.* ga tayyorlash
6. Third-party dependency compatibility tekshirish
7. Spring Boot 3.x ga o‘tish
8. Hibernate 6 / Security 6 / validation o‘zgarishlarini tuzatish
9. Full regression test
10. Canary deploy</code></pre><hr><h3>6. Nega avval 2.7.x?</h3><p>Agar biz to‘g‘ridan-to‘g‘ri eski 2.3 yoki 2.4&#039;dan Boot 3&#039;ga o‘tsangiz, xatolar juda ko‘payadi.</p><p>Yomon:</p><pre><code class="language-text">Spring Boot 2.3 → Spring Boot 3.3</code></pre><p>Yaxshi:</p><pre><code class="language-text">Spring Boot 2.3 → 2.7.x → 3.x</code></pre><p>Sabab:</p><pre><code class="language-text">2.7.x migration warninglarni beradi
deprecated API’larni ko‘rish osonlashadi
dependencylar Boot 3 ga yaqinlashadi
Security 5.8 orqali Security 6 ga tayyorlanadi</code></pre><hr><h3>7. Java EE → Jakarta EE migration</h3><p>Spring Boot 3 bilan katta o‘zgarish:</p><pre><code class="language-text">javax.* → jakarta.*</code></pre><p>Masalan:</p><pre><code class="language-java">import javax.persistence.Entity;
import javax.validation.Valid;
import javax.servlet.Filter;</code></pre><p>bo‘ladi:</p><pre><code class="language-java">import jakarta.persistence.Entity;
import jakarta.validation.Valid;
import jakarta.servlet.Filter;</code></pre><p>Bu oddiy import almashtirishga o‘xshaydi, lekin real project’da katta ta’sir qiladi.</p><hr><h4>Ta’sir qiladigan joylar</h4><pre><code class="language-text">JPA entities
Bean Validation
Servlet API
JAX-RS
JMS
JSON-B
XML binding
third-party libraries
generated code
OpenAPI generated models</code></pre><p>Misol:</p><pre><code class="language-java">import javax.persistence.Entity;
import javax.persistence.Id;</code></pre><p>yangisi:</p><pre><code class="language-java">import jakarta.persistence.Entity;
import jakarta.persistence.Id;</code></pre><hr><h3>8. Eng ko‘p chiqadigan muammo: dependency conflict</h3><p>Boot 3 <code>jakarta.*</code> ishlatadi. Lekin ba’zi eski librarylar hali ham <code>javax.*</code> ishlatadi.</p><p>Muammo:</p><pre><code class="language-text">Application Boot 3&#039;da
lekin eski dependency javax.servlet.Filter kutyapti
runtime’da ClassNotFoundException chiqadi</code></pre><p>Yechim:</p><pre><code class="language-text">librarylarni Boot 3 compatible versiyaga ko‘tarish
eski libraryni almashtirish
generated clientlarni qayta generate qilish
javax/jakarta aralashmasini tozalash</code></pre><hr><h3>9. OpenRewrite bilan migration</h3><p>Katta codebase’da qo‘lda import almashtirish xavfli va sekin.</p><p>OpenRewrite Spring Boot 3 migration recipe’lari build files, deprecated/preferred API, configuration settings va framework migrationlarni avtomatik o‘zgartirishga yordam beradi. (<a href="https://docs.openrewrite.org/recipes/java/spring/boot3/upgradespringboot_3_0?utm_source=chatgpt.com" title="Migrate to Spring Boot 3.0">OpenRewrite Docs</a>)</p><p>Masalan migration yordam berishi mumkin:</p><pre><code class="language-text">javax → jakarta importlar
Spring Boot version update
deprecated property migration
Spring Security DSL update
old API’dan new API’ga o‘tish</code></pre><p>Lekin qoida:</p><blockquote><p>OpenRewrite migrationni tezlashtiradi, lekin arxitektor qarorini almashtirmaydi. Har doim review, test va rollout kerak.</p></blockquote><hr><h3>10. Migration branch strategy</h3><p>Katta upgrade uchun yaxshi branch strategiya:</p><pre><code class="language-text">main
 ├── migration/spring-boot-3
      ├── step-1-java-17
      ├── step-2-boot-2.7-latest
      ├── step-3-jakarta
      ├── step-4-security-6
      └── step-5-boot-3</code></pre><p>Yoki kichik PR’lar:</p><pre><code class="language-text">PR 1: Java 17 support
PR 2: Deprecated API cleanup
PR 3: Dependency alignment
PR 4: javax → jakarta
PR 5: Spring Security migration
PR 6: Hibernate fixes
PR 7: Boot 3 switch</code></pre><p>Yomon amaliyot:</p><pre><code class="language-text">500 ta file o‘zgargan bitta ulkan PR</code></pre><p>Bunday PR review qilish qiyin, rollback qilish qiyin.</p><hr><h3>11. Spring Security migration</h3><p>Spring Security 6&#039;da eski config yondashuvlar ko‘p o‘zgargan.</p><p>Eski uslub:</p><pre><code class="language-java">@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}</code></pre><p>Yangi uslub:</p><pre><code class="language-java">@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(auth -&gt; auth
                        .requestMatchers(&quot;/actuator/health&quot;).permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth -&gt; oauth.jwt())
                .build();
    }
}</code></pre><p>Migration’da quyidagilarni tekshirish kerak:</p><pre><code class="language-text">endpoint authorization
CSRF policy
CORS
JWT claim mapping
role prefix
method security
password encoder
actuator security</code></pre><p>Security migration’da eng xavfli narsa:</p><blockquote><p>Application compile bo‘lishi mumkin, lekin security behavior o‘zgarib ketadi.</p></blockquote><hr><h3>12. Hibernate 5 → 6 migration</h3><p>Spring Boot 3 odatda Hibernate 6 bilan keladi.</p><p>Tekshiriladigan joylar:</p><pre><code class="language-text">custom queries
native queries
JPQL syntax
dialect settings
custom UserType
pagination behavior
naming strategy
lazy loading
criteria API</code></pre><p>Agar projectda ko‘p custom query bo‘lsa, migration risk yuqori.</p><p>Testlar:</p><pre><code class="language-text">repository integration tests
Testcontainers bilan real database
migration scripts
slow query monitoring
N+1 checks</code></pre><hr><h3>13. Configuration property migration</h3><p>Spring Boot versiyalarda property nomlari o‘zgarishi mumkin.</p><p>Masalan:</p><pre><code class="language-yaml">management.metrics.export.prometheus.enabled: true</code></pre><p>ba’zi versiyalarda boshqacha konfiguratsiyaga o‘tgan bo‘lishi mumkin.</p><p>Migration strategy:</p><pre><code class="language-text">old propertylarni scan qilish
metadata warninglarni ko‘rish
startup loglarni tekshirish
deprecated config report olish
configuration tests yozish</code></pre><p>Spring Boot release notes va migration guide har release uchun &quot;breaking change&quot; va property o‘zgarishlarini tekshirishda asosiy manba bo‘ladi. (<a href="https://docs.spring.io/spring-boot/upgrading.html?utm_source=chatgpt.com" title="Upgrading Spring Boot">Home</a>)</p><hr><h3>14. Dependency convergence</h3><p><strong>Dependency convergence</strong> - project ichida bir xil library’ning turli versiyalari conflict qilmasligi.</p><p>Masalan:</p><pre><code class="language-text">service A:
  jackson-databind 2.15

dependency X:
  jackson-databind 2.13

dependency Y:
  jackson-databind 2.16</code></pre><p>Natija:</p><pre><code class="language-text">NoSuchMethodError
ClassCastException
runtime conflict
security vulnerability</code></pre><p>Arxitektor darajada yechim:</p><pre><code class="language-text">BOM ishlatish
dependencyManagement markazlashtirish
Maven Enforcer / Gradle constraints
dependency tree audit
CI’da vulnerability scan</code></pre><hr><h3>15. BOM orqali boshqarish</h3><p>Platform standardization mavzusida aytganimizdek, company BOM dependency versionlarni markazlashtiradi.</p><pre><code class="language-xml">&lt;dependencyManagement&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.company.platform&lt;/groupId&gt;
            &lt;artifactId&gt;company-bom&lt;/artifactId&gt;
            &lt;version&gt;2.0.0&lt;/version&gt;
            &lt;type&gt;pom&lt;/type&gt;
            &lt;scope&gt;import&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/dependencyManagement&gt;</code></pre><p>Foyda:</p><pre><code class="language-text">hamma service bir xil dependency baseline’da
CVE patch markazdan qilinadi
upgrade PR’lar standart bo‘ladi
conflict kamayadi</code></pre><hr><h3>16. CVE management</h3><p>CVE - dependency yoki platformadagi xavfsizlik zaifligi.</p><p>Masalan:</p><pre><code class="language-text">Jackson CVE
Netty CVE
Tomcat CVE
Spring Security CVE
Logback CVE
PostgreSQL driver CVE</code></pre><p>CVE strategy:</p><pre><code class="language-text">1. SBOM generate qilish
2. Dependency scan qilish
3. Severity bo‘yicha triage
4. Exploitability tekshirish
5. Patched versionga upgrade
6. Regression test
7. Rollout
8. Audit record</code></pre><p>Spring OSS support policy’da major release’lar 3 yilgacha, minor release’lar esa kamida 12 oy qo‘llab-quvvatlanishi aytilgan. (<a href="https://spring.io/support-policy?utm_source=chatgpt.com" title="Support Policy">Home</a>) Shuning uchun arxitektor faqat &quot;hozir ishlayapti&quot; deb emas, <strong>support lifecycle</strong> bo‘yicha ham reja qiladi.</p><hr><h3>17. Release cadence &amp; EOL planning</h3><p>EOL - End of Life. Ya’ni versiya endi free public security/bug fix olmaydi.</p><p>Arxitektor har service uchun matrix yuritadi:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Service</p></th><th rowspan="1" colspan="1"><p>Java</p></th><th rowspan="1" colspan="1"><p>Spring Boot</p></th><th rowspan="1" colspan="1"><p>Spring Cloud</p></th><th rowspan="1" colspan="1"><p>Status</p></th></tr><tr><td rowspan="1" colspan="1"><p>order-service</p></td><td rowspan="1" colspan="1"><p>17</p></td><td rowspan="1" colspan="1"><p>3.3.x</p></td><td rowspan="1" colspan="1"><p>2023.x</p></td><td rowspan="1" colspan="1"><p>upgrade kerak</p></td></tr><tr><td rowspan="1" colspan="1"><p>payment-service</p></td><td rowspan="1" colspan="1"><p>21</p></td><td rowspan="1" colspan="1"><p>3.5.x</p></td><td rowspan="1" colspan="1"><p>2025.x</p></td><td rowspan="1" colspan="1"><p>ok</p></td></tr><tr><td rowspan="1" colspan="1"><p>legacy-report</p></td><td rowspan="1" colspan="1"><p>11</p></td><td rowspan="1" colspan="1"><p>2.7.x</p></td><td rowspan="1" colspan="1"><p>old</p></td><td rowspan="1" colspan="1"><p>high risk</p></td></tr></tbody></table><p>Yaxshi platform governance:</p><pre><code class="language-text">har quarter dependency review
EOL’dan 6 oy oldin migration plan
critical CVE bo‘lsa emergency patch
major upgrade uchun pilot service</code></pre><hr><h3>18. Strangler Fig Pattern</h3><p><strong>Strangler Fig pattern</strong> - legacy systemni birdan rewrite qilmasdan, sekin-sekin yangi system bilan almashtirish.</p><p>G‘oya:</p><pre><code class="language-text">Old monolith saqlanadi
yangi featurelar yangi service/module’da yoziladi
traffic asta-sekin yangi tomonga yo‘naltiriladi
legacy qismi qisqarib boradi
oxiri eski qism o‘chiriladi</code></pre><hr><h4>Oddiy ko‘rinish</h4><pre><code class="language-text">Client
  ↓
API Gateway
  ├── /old/orders  → legacy monolith
  ├── /new/orders  → new order service
  └── /payments    → new payment service</code></pre><p>Yoki:</p><pre><code class="language-text">/orders eski read → legacy
/orders yangi write → new service</code></pre><hr><h3>19. Strangler Fig qachon kerak?</h3><p>Kerak bo‘ladi:</p><pre><code class="language-text">katta monolith bor
rewrite xavfli
business to‘xtamasligi kerak
domainlarni asta ajratish kerak
teamlar parallel ishlashi kerak
legacy code test qilinmaydi</code></pre><p>Kerak bo‘lmasligi mumkin:</p><pre><code class="language-text">kichik project
legacy juda oddiy
full rewrite tez va xavfsiz
business critical emas</code></pre><hr><h3>20. Strangler migration bosqichlari</h3><pre><code class="language-text">1. Legacy system boundary mapping
2. Domain/module tanlash
3. API gateway/proxy qo‘yish
4. Yangi module/service yaratish
5. Data sync strategiya tanlash
6. Traffic routing
7. Dual-write yoki event sync
8. Canary rollout
9. Legacy endpointni deprecated qilish
10. Eski kodni olib tashlash</code></pre><hr><h3>21. Eng xavfli qism: data migration</h3><p>Legacy’dan yangi systemga o‘tishda data masalasi eng qiyin.</p><p>Variantlar:</p><pre><code class="language-text">Big bang migration
Dual-write
CDC - Change Data Capture
Event-driven sync
Read from legacy, write to new
Read from new, fallback to legacy</code></pre><h4>Big bang</h4><pre><code class="language-text">Bir kechada hamma data ko‘chiriladi
old system o‘chadi
new system yoqiladi</code></pre><p>Risk yuqori.</p><h4>Dual-write</h4><pre><code class="language-text">Application eski DB va yangi DB’ga yozadi</code></pre><p>Risk:</p><pre><code class="language-text">biri yozildi, biri yozilmadi
consistency muammosi
rollback qiyin</code></pre><h4>CDC</h4><pre><code class="language-text">Legacy database change log → Debezium → Kafka → new system</code></pre><p>Ko‘p enterprise migrationlarda CDC xavfsizroq bo‘lishi mumkin.</p><hr><h3>22. API backward compatibility</h3><p>Upgrade vaqtida eng muhim qoidalardan biri:</p><blockquote><p>Existing clientlar buzilmasligi kerak.</p></blockquote><p>Breaking change misollar:</p><pre><code class="language-text">field nomi o‘zgardi
enum value o‘zgardi
required field qo‘shildi
response structure o‘zgardi
HTTP status o‘zgardi
pagination format o‘zgardi
error response o‘zgardi
auth scope o‘zgardi</code></pre><p>Yomon:</p><pre><code class="language-json">{
  &quot;userName&quot;: &quot;Ali&quot;
}</code></pre><p>yangisi:</p><pre><code class="language-json">{
  &quot;fullName&quot;: &quot;Ali&quot;
}</code></pre><p>Bu clientni buzadi.</p><p>Yaxshi:</p><pre><code class="language-json">{
  &quot;userName&quot;: &quot;Ali&quot;,
  &quot;fullName&quot;: &quot;Ali&quot;
}</code></pre><p>keyin deprecation period.</p><hr><h3>23. API versioning strategy</h3><p>Variantlar:</p><h4>URI versioning</h4><pre><code class="language-text">/api/v1/orders
/api/v2/orders</code></pre><p>Sodda va tushunarli.</p><h4>Header versioning</h4><pre><code class="language-http">Accept: application/vnd.company.orders-v2+json</code></pre><p>Toza, lekin clientlar uchun murakkabroq.</p><h4>Compatible evolution</h4><p>Bitta endpoint, faqat backward compatible o‘zgarishlar.</p><pre><code class="language-text">field qo‘shish mumkin
field o‘chirish mumkin emas
required qilish mumkin emas
enumni ehtiyot bilan kengaytirish kerak</code></pre><hr><h3>24. Backward compatible o‘zgarishlar</h3><p>Odatda xavfsiz:</p><pre><code class="language-text">response’ga optional field qo‘shish
yangi endpoint qo‘shish
yangi optional request field qo‘shish
pagination metadata qo‘shish
new enum value - ehtiyot bilan</code></pre><p>Xavfli:</p><pre><code class="language-text">field rename
field remove
type change
required field qo‘shish
error code o‘zgartirish
HTTP status o‘zgartirish
auth requirement kuchaytirish</code></pre><hr><h3>25. Deprecation policy</h3><p>Har API uchun deprecation qoidasi bo‘lishi kerak:</p><pre><code class="language-text">1. Old field/endpoint deprecated deb belgilanadi
2. Documentation’da yoziladi
3. Clientlarga xabar beriladi
4. Metrics bilan usage o‘lchanadi
5. Migration deadline beriladi
6. Deadline’dan keyin olib tashlanadi</code></pre><p>Masalan:</p><pre><code class="language-http">Deprecation: true
Sunset: Wed, 31 Dec 2026 23:59:59 GMT</code></pre><hr><h3>26. Contract testing</h3><p>Migration’da contract test juda foydali.</p><p>Maqsad:</p><pre><code class="language-text">Provider o‘zgarsa ham consumer buzilmasin</code></pre><p>Masalan:</p><pre><code class="language-text">order-service API response o‘zgardi
mobile app yoki payment-service buzilmasligi kerak</code></pre><p>Contract test tools:</p><pre><code class="language-text">Spring Cloud Contract
Pact
OpenAPI diff tools
consumer-driven contract tests</code></pre><hr><h3>27. Database migration strategy</h3><p>Database migration ham upgrade strategy’ning bir qismi.</p><p>Qoida:</p><blockquote><p>Database migration backward compatible bo‘lishi kerak.</p></blockquote><p>Yomon:</p><pre><code class="language-sql">ALTER TABLE users DROP COLUMN username;</code></pre><p>Bu eski applicationni buzadi.</p><p>Yaxshi expand-contract pattern:</p><pre><code class="language-text">1. Expand: yangi column qo‘shish
2. App eski va yangi column bilan ishlaydi
3. Backfill data
4. Client/app yangi columnga o‘tadi
5. Contract: eski columnni keyin olib tashlash</code></pre><hr><h4>Expand-contract misol</h4><ol start="1"><li><p>Yangi column:</p></li></ol><pre><code class="language-sql">ALTER TABLE users ADD COLUMN full_name VARCHAR(255);</code></pre><ol start="2"><li><p>App ikkala fieldni yozadi:</p></li></ol><pre><code class="language-text">username + full_name</code></pre><ol start="3"><li><p>Backfill:</p></li></ol><pre><code class="language-sql">UPDATE users SET full_name = username WHERE full_name IS NULL;</code></pre><ol start="4"><li><p>App faqat <code>full_name</code> o‘qiydi.</p></li><li><p>Keyingi release’da:</p></li></ol><pre><code class="language-sql">ALTER TABLE users DROP COLUMN username;</code></pre><hr><h3>28. Rollout strategy</h3><p>Upgrade production’da birdan hamma userga berilmasligi kerak.</p><p>Variantlar:</p><pre><code class="language-text">blue-green deployment
canary deployment
rolling deployment
feature flags
dark launch
shadow traffic</code></pre><h4>Canary</h4><pre><code class="language-text">1% traffic → new version
5% traffic → new version
25% traffic → new version
100% traffic → new version</code></pre><p>Bu vaqt davomida kuzatiladi:</p><pre><code class="language-text">error rate
latency
CPU/memory
database query time
business metrics
security failures</code></pre><hr><h3>29. Rollback strategy</h3><p>Migration’dan oldin rollback aniq bo‘lishi kerak.</p><p>Savollar:</p><pre><code class="language-text">old app new database schema bilan ishlaydimi?
migration reversiblemi?
data o‘zgargan bo‘lsa rollback qanday?
message format backward compatiblemi?
clientlar eski API’ga qayta oladimi?</code></pre><p>Eng yomon holat:</p><pre><code class="language-text">new app deploy qilindi
database destructive migration qildi
app yiqildi
old app schema bilan ishlamaydi
rollback imkonsiz</code></pre><p>Shuning uchun database migration expand-contract bo‘lishi kerak.</p><hr><h3>30. Observability during migration</h3><p>Migration monitoring’siz qilinmaydi.</p><p>Kerakli dashboard:</p><pre><code class="language-text">startup errors
HTTP 4xx/5xx
p95/p99 latency
DB connection pool
slow queries
Kafka consumer lag
memory/GC
security denied requests
business KPI</code></pre><p>Loglarda migration flag foydali:</p><pre><code class="language-json">{
  &quot;service&quot;: &quot;order-service&quot;,
  &quot;version&quot;: &quot;3.0.0-migration&quot;,
  &quot;springBoot&quot;: &quot;3.5.x&quot;,
  &quot;traceId&quot;: &quot;abc123&quot;,
  &quot;message&quot;: &quot;Order created&quot;
}</code></pre><hr><h3>31. Spring Boot 2 → 3 migration checklist</h3><pre><code class="language-text">Java 17+ ga o‘tildi
Spring Boot latest 2.7.x ga ko‘tarildi
Deprecated API’lar tozalandi
javax.* importlar jakarta.* ga o‘tdi
Spring Security config yangilandi
Hibernate queries test qilindi
Validation imports yangilandi
Servlet filters yangilandi
OpenAPI/generated code yangilandi
Third-party libraries Boot 3 compatible
Actuator endpoints tekshirildi
Metrics/tracing tekshirildi
Integration tests o‘tdi
Performance baseline solishtirildi
Canary deploy qilindi
Rollback plan bor</code></pre><hr><h3>32. Real migration example</h3><p>Tasavvur qilamiz:</p><pre><code class="language-text">order-service
Java 11
Spring Boot 2.5
Spring Security old config
Hibernate 5
javax.persistence</code></pre><p>To‘g‘ri migration:</p><pre><code class="language-text">Step 1: Java 17 support qo‘shish
Step 2: Spring Boot 2.7.x ga o‘tish
Step 3: Security 5.8 style configga o‘tish
Step 4: Deprecated configlarni tozalash
Step 5: javax → jakarta migration
Step 6: Boot 3 ga o‘tish
Step 7: Hibernate querylarni tuzatish
Step 8: Testcontainers bilan DB test
Step 9: Canary deploy
Step 10: full rollout</code></pre><hr><h3>33. Eng ko‘p xatolar</h3><h4>Xato 1: Big bang upgrade</h4><pre><code class="language-text">Java 11 → 21
Boot 2.4 → 3.5
Security 5 → 6
Hibernate 5 → 6
hammasi bitta PR’da</code></pre><p>Bu xavfli.</p><hr><h4>Xato 2: Compile bo‘lsa yetadi deb o‘ylash</h4><p>Migration’da runtime behavior muhim:</p><pre><code class="language-text">security rules
transaction behavior
serialization
query behavior
cache behavior
metrics/tracing</code></pre><hr><h4>Xato 3: Database rollback o‘ylanmagan</h4><p>Destructive migration rollbackni o‘ldiradi.</p><hr><h4>Xato 4: Third-party dependency tekshirilmagan</h4><p>Eski SDK <code>javax.*</code> ishlatsa, Boot 3&#039;da muammo beradi.</p><hr><h4>Xato 5: API compatibility buzilgan</h4><p>Backend upgrade bo‘ldi, lekin mobile app eski response kutyapti.</p><hr><h3>34. Arxitektor xulosasi</h3><p><strong>Migration &amp; upgrade strategy</strong> - bu production systemni yangilashda xavfni kamaytirish san’ati.</p><p>Asosiy qoida:</p><pre><code class="language-text">Upgrade step-by-step qilinadi.
Breaking change oldindan aniqlanadi.
API backward compatibility saqlanadi.
Database migration expand-contract bo‘ladi.
Dependency/CVE markazdan boshqariladi.
Rollout canary/blue-green bilan qilinadi.
Rollback oldindan tayyor bo‘ladi.</code></pre><p>Spring Boot 2 → 3 uchun qisqa formula:</p><pre><code class="language-text">Latest 2.7.x
+ Java 17+
+ javax → jakarta
+ Security 6 migration
+ Hibernate 6 validation
+ dependency compatibility
+ full regression
+ canary rollout
= xavfsiz Spring Boot 3 migration</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Spring Modulith]]></title>
            <link>https://blog.jtscorp.uz/spring-modulith</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/spring-modulith</guid>
            <pubDate>Thu, 02 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p>Spring Modulith - <strong>bitta Spring Boot application ichida aniq chegaralangan modullar</strong> qurish uchun ishlatiladi.</p><p>Spring Modulith quyidagi mavzularni o‘z ichiga oladi: <strong>application modules &amp; boundaries</strong>, <strong>inter-module event communication</strong>, <strong>module integration tests</strong>, <strong>documenting module structure</strong>, va <strong>modulith’dan microservices’ga evolyutsiya</strong>.</p><p>Spring’ning rasmiy hujjatlarida Spring Modulith alohida modullarni izolyatsiyada yoki boshqa modullar bilan birga integration test qilish imkonini berishi aytiladi. (<a href="https://docs.spring.io/spring-modulith/reference/testing.html?utm_source=chatgpt.com" title="Integration Testing Application Modules">Home</a>) Shuningdek, modullarni bo‘sh bog‘langan saqlash uchun ular orasida Spring application event’lari orqali aloqa qilish tavsiya qilinadi. (<a href="https://docs.spring.io/spring-modulith/reference/events.html?utm_source=chatgpt.com" title="Working with Application Events :: Spring Modulith">Home</a>)</p><hr><h3>1. Spring Modulith nima?</h3><p><strong>Spring Modulith</strong> - bu microservice emas. Bu <strong>modular monolith</strong> qurish usuli.</p><p>Ya’ni:</p><pre><code class="language-text">bitta application
bitta deploy
bitta database bo‘lishi mumkin
lekin ichida aniq ajratilgan biznes modullar bor</code></pre><p>Masalan:</p><pre><code class="language-text">marketplace-app
 ├── order
 ├── payment
 ├── delivery
 ├── catalog
 ├── customer
 └── notification</code></pre><p>Hammasi bitta Spring Boot project ichida. Lekin <code>order</code> module <code>payment</code> module ichki classlarini xohlagancha ishlata olmaydi.</p><hr><h3>2. Nega Spring Modulith kerak?</h3><p>Oddiy monolith boshida juda qulay:</p><pre><code class="language-text">Controller → Service → Repository</code></pre><p>Lekin project kattalashganda shunday bo‘lib ketadi:</p><pre><code class="language-text">OrderService -&gt; PaymentRepository
PaymentService -&gt; DeliveryService
NotificationService -&gt; OrderRepository
CatalogService -&gt; PaymentService</code></pre><p>Ya’ni hamma classlar bir-biriga bog‘lanib ketadi.</p><p>Natija:</p><ul><li><p>bitta joyni o‘zgartirsak boshqa joy buziladi</p></li><li><p>testlar og‘irlashadi</p></li><li><p>yangi developer projectni tushunishga qiynaladi</p></li><li><p>microservice’ga ajratish deyarli imkonsiz bo‘ladi</p></li><li><p>dependency tartibsiz bo‘ladi</p></li></ul><p>Spring Modulith shuni oldini oladi:</p><blockquote><p>Monolith ichida microservice’ga o‘xshash intizom beradi, lekin deploy murakkabligini oshirmaydi.</p></blockquote><hr><h3>3. Modular Monolith vs Microservices</h3><table><tbody><tr><th rowspan="1" colspan="1"><p>Mezon</p></th><th rowspan="1" colspan="1"><p>Modular Monolith</p></th><th rowspan="1" colspan="1"><p>Microservices</p></th></tr><tr><td rowspan="1" colspan="1"><p>Deploy</p></td><td rowspan="1" colspan="1"><p>Bitta application</p></td><td rowspan="1" colspan="1"><p>Har service alohida</p></td></tr><tr><td rowspan="1" colspan="1"><p>Network call</p></td><td rowspan="1" colspan="1"><p>Yo‘q yoki kam</p></td><td rowspan="1" colspan="1"><p>Ko‘p</p></td></tr><tr><td rowspan="1" colspan="1"><p>Transaction</p></td><td rowspan="1" colspan="1"><p>Osonroq</p></td><td rowspan="1" colspan="1"><p>Murakkab</p></td></tr><tr><td rowspan="1" colspan="1"><p>Monitoring</p></td><td rowspan="1" colspan="1"><p>Osonroq</p></td><td rowspan="1" colspan="1"><p>Murakkabroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Team mustaqilligi</p></td><td rowspan="1" colspan="1"><p>O‘rtacha</p></td><td rowspan="1" colspan="1"><p>Yuqori</p></td></tr><tr><td rowspan="1" colspan="1"><p>Boshlash narxi</p></td><td rowspan="1" colspan="1"><p>Pastroq</p></td><td rowspan="1" colspan="1"><p>Yuqoriroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Architecture intizomi</p></td><td rowspan="1" colspan="1"><p>Kerak</p></td><td rowspan="1" colspan="1"><p>Juda kerak</p></td></tr></tbody></table><p>Arxitektor uchun muhim qoida:</p><blockquote><p>Avval modular monolith qilib to‘g‘ri chegaralarni topish, keyin kerak bo‘lsa microservice’ga ajratish - ko‘p holatda xavfsizroq yo‘l.</p></blockquote><hr><h3>4. Application Modules &amp; Boundaries</h3><p>Spring Modulith’da module odatda <strong>package</strong> orqali aniqlanadi.</p><p>Masalan:</p><pre><code class="language-text">com.company.shop
 ├── order
 ├── payment
 ├── delivery
 └── notification</code></pre><p>Har bir yuqori darajadagi package alohida <strong>application module</strong> sifatida qaraladi.</p><hr><h4>Yaxshi structure</h4><pre><code class="language-text">com.company.shop.order
 ├── OrderManagement.java
 ├── OrderCreatedEvent.java
 ├── internal
 │    ├── OrderService.java
 │    ├── OrderRepository.java
 │    └── OrderJpaEntity.java
 └── web
      └── OrderController.java</code></pre><p>Bu yerda:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Qism</p></th><th rowspan="1" colspan="1"><p>Vazifa</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>order</code> package root</p></td><td rowspan="1" colspan="1"><p>boshqa modullar ko‘rishi mumkin bo‘lgan public API</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>internal</code></p></td><td rowspan="1" colspan="1"><p>faqat <code>order</code> module ichida ishlatiladi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>web</code></p></td><td rowspan="1" colspan="1"><p>REST controller yoki input adapter</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>OrderCreatedEvent</code></p></td><td rowspan="1" colspan="1"><p>boshqa modullarga chiqariladigan event</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>OrderManagement</code></p></td><td rowspan="1" colspan="1"><p>module facade/public service</p></td></tr></tbody></table><hr><h4>Yomon structure</h4><pre><code class="language-text">com.company.shop
 ├── controller
 ├── service
 ├── repository
 ├── entity
 └── dto</code></pre><p>Bu texnik qatlam bo‘yicha bo‘lingan. Katta project’da biznes chegaralar yo‘qoladi.</p><p>Yaxshiroq:</p><pre><code class="language-text">com.company.shop
 ├── order
 ├── payment
 ├── delivery
 └── customer</code></pre><p>Bu <strong>package-by-domain</strong>.</p><hr><h3>5. Public API va internal classlar</h3><p>Spring Modulith’da eng muhim tushuncha:</p><blockquote><p>Boshqa modul faqat public API’dan foydalansin, internal classlarga kirmasin.</p></blockquote><p>Masalan <code>payment</code> module:</p><pre><code class="language-text">payment
 ├── PaymentManagement.java
 ├── PaymentCompletedEvent.java
 └── internal
      ├── PaymentService.java
      ├── PaymentRepository.java
      └── PaymentProcessor.java</code></pre><p><code>order</code> module quyidagini ishlatishi mumkin:</p><pre><code class="language-java">payment.PaymentManagement
payment.PaymentCompletedEvent</code></pre><p>Lekin buni ishlatmasligi kerak:</p><pre><code class="language-java">payment.internal.PaymentService
payment.internal.PaymentRepository</code></pre><p>Chunki <code>internal</code> - modulning ichki implementatsiyasi.</p><hr><h3>6. Module facade</h3><p>Module facade - boshqa modullar bilan gaplashish uchun public contract.</p><p>Masalan:</p><pre><code class="language-java">package com.company.shop.payment;

public interface PaymentManagement {

    PaymentResult pay(PaymentRequest request);

}</code></pre><p>Implementation esa <code>internal</code> ichida turadi:</p><pre><code class="language-java">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);
    }
}</code></pre><p>Bu yondashuv yaxshi, chunki boshqa module faqat <code>PaymentManagement</code> bilan ishlaydi.</p><hr><h3>7. Inter-module communication</h3><p>Modullar ikki xil yo‘l bilan gaplashadi:</p><h4>1. Synchronous call</h4><p>Bir module boshqa module’ning public API’sini chaqiradi.</p><pre><code class="language-java">@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()));
    }
}</code></pre><p>Bu oddiy va tushunarli. Lekin coupling kuchliroq bo‘ladi.</p><hr><h3>2. Event-based communication</h3><p>Bir module event chiqaradi, boshqa module uni eshitadi.</p><pre><code class="language-java">public record OrderCreatedEvent(
        Long orderId,
        Long customerId,
        BigDecimal totalAmount
) {}</code></pre><p>Order module event publish qiladi:</p><pre><code class="language-java">@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()
        ));
    }
}</code></pre><p>Payment module event’ni eshitadi:</p><pre><code class="language-java">@Component
class PaymentOnOrderCreated {

    @EventListener
    void on(OrderCreatedEvent event) {
        // payment boshlash
    }
}</code></pre><p>Bu yondashuvda <code>order</code> module <code>payment</code> module borligini bilmaydi.</p><hr><h3>8. <code>@ApplicationModuleListener</code></h3><p>Spring Modulith’da event listener uchun maxsus annotation ishlatiladi:</p><pre><code class="language-java">@ApplicationModuleListener
void on(OrderCreatedEvent event) {
    // boshqa module eventiga reaksiya
}</code></pre><p>Bu oddiy <code>@EventListener</code>dan kuchliroq. Odatda quyidagi maqsadlarda ishlatiladi:</p><ul><li><p>module’lar orasidagi event handling</p></li><li><p>transaction tugagandan keyin eventni qayta ishlash</p></li><li><p>async processing</p></li><li><p>module boundary’ni toza saqlash</p></li></ul><p>Arxitektor nuqtayi nazaridan:</p><blockquote><p>Agar bir module boshqa module’ning ichki service’ini chaqirmasdan event orqali ishlasa, coupling kamayadi.</p></blockquote><hr><h3>9. Real flow: Order → Payment → Delivery</h3><p>Online shop misoli:</p><pre><code class="language-text">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</code></pre><p>Structure:</p><pre><code class="language-text">shop
 ├── order
 │    ├── OrderManagement.java
 │    ├── OrderCreatedEvent.java
 │    └── internal
 │         └── OrderService.java
 │
 ├── payment
 │    ├── PaymentCompletedEvent.java
 │    └── internal
 │         └── PaymentService.java
 │
 └── delivery
      └── internal
           └── DeliveryService.java</code></pre><p>Flow:</p><pre><code class="language-text">OrderCreatedEvent
        ↓
Payment module
        ↓
PaymentCompletedEvent
        ↓
Delivery module</code></pre><p>Bu hali microservice emas. Lekin architecture microservice’ga o‘xshab toza.</p><hr><h3>10. Module verification</h3><p>Spring Modulith’ning eng katta foydasi - <strong>module boundary test</strong>.</p><p>Masalan:</p><pre><code class="language-java">class ModularityTests {

    ApplicationModules modules =
            ApplicationModules.of(ShopApplication.class);

    @Test
    void verifiesModularStructure() {
        modules.verify();
    }
}</code></pre><p>Bu test quyidagilarni tekshiradi:</p><ul><li><p>module’lar orasida cyclic dependency bormi</p></li><li><p>internal package tashqaridan ishlatilganmi</p></li><li><p>module ruxsat berilmagan module’ga bog‘langanmi</p></li><li><p>package structure buzilganmi</p></li></ul><p>Masalan, <code>order</code> module buni qilsa:</p><pre><code class="language-java">import com.company.shop.payment.internal.PaymentService;</code></pre><p>test yiqiladi.</p><p>Bu juda kuchli narsa:</p><blockquote><p>Architecture faqat diagrammada emas, test bilan majburlanadi.</p></blockquote><hr><h3>11. Module integration tests</h3><p>Oddiy Spring Boot test ko‘pincha butun application context’ni ko‘taradi:</p><pre><code class="language-java">@SpringBootTest
class OrderTest {
}</code></pre><p>Katta project’da bu sekin.</p><p>Spring Modulith bilan faqat bitta modulni test qilish mumkin:</p><pre><code class="language-java">@ApplicationModuleTest
class OrderModuleTest {

    @Test
    void createsOrder() {
        // faqat order module konteksti
    }
}</code></pre><p>Rasmiy hujjatlarda Spring Modulith application module’larni izolyatsiyada yoki boshqa modullar bilan kombinatsiyada integration test qilish imkonini berishi ko‘rsatilgan. (<a href="https://docs.spring.io/spring-modulith/reference/testing.html?utm_source=chatgpt.com" title="Integration Testing Application Modules">Home</a>)</p><p>Foydasi:</p><ul><li><p>test tezroq</p></li><li><p>module mustaqilligi tekshiriladi</p></li><li><p>hidden dependency topiladi</p></li><li><p>architecture real ishlashi isbotlanadi</p></li></ul><hr><h3>12. Published events testing</h3><p>Event chiqdimi yoki yo‘qmi - test qilish mumkin.</p><p>Masalan:</p><pre><code class="language-java">@ApplicationModuleTest
class OrderModuleTest {

    @Test
    void publishesOrderCreatedEvent(
            PublishedEvents events,
            OrderManagement orders
    ) {
        orders.createOrder(new CreateOrderCommand(...));

        assertThat(events)
                .contains(OrderCreatedEvent.class);
    }
}</code></pre><p>Bu nima beradi?</p><blockquote><p>Order yaratish use case’i payment’ni bevosita chaqirmaydi, faqat event chiqaradi. Test shuni isbotlaydi.</p></blockquote><p>Spring Modulith <code>PublishedEvents</code> orqali test davomida publish qilingan eventlarni tekshirishga imkon beradi. (<a href="https://docs.spring.io/spring-modulith/reference/events.html?utm_source=chatgpt.com" title="Working with Application Events :: Spring Modulith">Home</a>)</p><hr><h3>13. Documenting module structure</h3><p>Spring Modulith module structure’ni dokumentatsiya qilishga ham yordam beradi.</p><p>Masalan:</p><pre><code class="language-java">@Test
void writeDocumentation() {
    ApplicationModules modules =
            ApplicationModules.of(ShopApplication.class);

    new Documenter(modules)
            .writeModulesAsPlantUml()
            .writeIndividualModulesAsPlantUml();
}</code></pre><p>Natijada module dependency diagrammalar olish mumkin.</p><p>Bu arxitektor uchun foydali:</p><ul><li><p>yangi developer projectni tez tushunadi</p></li><li><p>real dependency ko‘rinadi</p></li><li><p>diagramma koddan generate bo‘ladi</p></li><li><p>eski qo‘lda chizilgan diagrammadan ko‘ra ishonchliroq</p></li></ul><hr><h3>14. Spring Modulith va DDD</h3><p>Spring Modulith DDD bilan juda yaxshi mos keladi.</p><p>DDD’da <strong>bounded context</strong> bor:</p><pre><code class="language-text">Order Context
Payment Context
Delivery Context
Customer Context</code></pre><p>Spring Modulith’da ular package/module sifatida ajratiladi:</p><pre><code class="language-text">com.company.shop.order
com.company.shop.payment
com.company.shop.delivery
com.company.shop.customer</code></pre><p>Ya’ni:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>DDD tushuncha</p></th><th rowspan="1" colspan="1"><p>Spring Modulith’da</p></th></tr><tr><td rowspan="1" colspan="1"><p>Bounded Context</p></td><td rowspan="1" colspan="1"><p>Application Module</p></td></tr><tr><td rowspan="1" colspan="1"><p>Domain Event</p></td><td rowspan="1" colspan="1"><p>Spring Application Event</p></td></tr><tr><td rowspan="1" colspan="1"><p>Context API</p></td><td rowspan="1" colspan="1"><p>Module public API</p></td></tr><tr><td rowspan="1" colspan="1"><p>Internal model</p></td><td rowspan="1" colspan="1"><p><code>internal</code> package</p></td></tr><tr><td rowspan="1" colspan="1"><p>Context boundary</p></td><td rowspan="1" colspan="1"><p>Module verification</p></td></tr></tbody></table><hr><h3>15. Modulith’dan microservice’ga o‘tish</h3><p>Spring Modulith’ning kuchli tomoni:</p><blockquote><p>Avval module chegaralarini bitta application ichida to‘g‘ri topasiz, keyin kerak bo‘lsa alohida service qilib ajratasiz.</p></blockquote><p>Masalan boshida:</p><pre><code class="language-text">shop-app
 ├── order
 ├── payment
 └── delivery</code></pre><p>Keyin traffic oshsa yoki team alohida ishlashi kerak bo‘lsa:</p><pre><code class="language-text">order-service
payment-service
delivery-service</code></pre><p>Agar oldindan module boundary yaxshi bo‘lsa, ajratish osonroq bo‘ladi.</p><p>Agar boshidan hamma joy aralash bo‘lsa:</p><pre><code class="language-text">OrderService -&gt; PaymentRepository
PaymentService -&gt; OrderRepository
DeliveryService -&gt; PaymentService</code></pre><p>microservice’ga ajratish juda og‘riqli bo‘ladi.</p><hr><h3>16. Qachon Spring Modulith ishlatish kerak?</h3><h4>Ishlatish yaxshi</h4><ul><li><p>project o‘rta yoki katta bo‘lsa</p></li><li><p>biznes domain aniq modullarga bo‘linsa</p></li><li><p>microservice qilishga shoshilmasak</p></li><li><p>monolith ichida tartib kerak bo‘lsa</p></li><li><p>jamoa bitta codebase’da ishlasa</p></li><li><p>module boundary’ni test bilan nazorat qilmoqchi bo‘lsangiz</p></li></ul><h4>Shart emas</h4><ul><li><p>juda kichik CRUD project</p></li><li><p>MVP juda tez yozilishi kerak bo‘lsa</p></li><li><p>domain hali aniq bo‘lmasa</p></li><li><p>jamoa package-by-domain’ni ham tushunmasa</p></li><li><p>architecture intizomi talab qilinmasa</p></li></ul><hr><h3>17. Eng ko‘p xatolar</h3><h4>Xato 1: Module nomini texnik qilish</h4><p>Yomon:</p><pre><code class="language-text">controller
service
repository</code></pre><p>Yaxshi:</p><pre><code class="language-text">order
payment
delivery
customer</code></pre><hr><h4>Xato 2: Internal classlarni tashqaridan ishlatish</h4><p>Yomon:</p><pre><code class="language-java">import com.company.shop.payment.internal.PaymentService;</code></pre><p>Yaxshi:</p><pre><code class="language-java">import com.company.shop.payment.PaymentManagement;</code></pre><p>yoki event:</p><pre><code class="language-java">OrderCreatedEvent</code></pre><hr><h4>Xato 3: Event’ni hamma narsaga ishlatish</h4><p>Event yaxshi, lekin hamma narsa event bo‘lishi shart emas.</p><p>Masalan, real vaqt javob kerak bo‘lsa:</p><pre><code class="language-text">Order -&gt; Payment public API call</code></pre><p>Agar loose coupling kerak bo‘lsa:</p><pre><code class="language-text">OrderCreatedEvent -&gt; Payment listener</code></pre><hr><h4>Xato 4: Bitta module ichida hamma narsani saqlash</h4><p>Yomon:</p><pre><code class="language-text">core
common
shared</code></pre><p>Agar hamma narsa <code>common</code>ga ketaversa, module boundary buziladi.</p><hr><h4>Xato 5: Modulith’ni microservice deb o‘ylash</h4><p>Spring Modulith:</p><pre><code class="language-text">bitta process
bitta deploy
module boundary ichki darajada</code></pre><p>Microservice:</p><pre><code class="language-text">alohida process
alohida deploy
network communication
alohida observability</code></pre><hr><h3>18. Amaliy structure - tavsiya</h3><pre><code class="language-text">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</code></pre><hr><h3>19. Minimal dependency</h3><p>Maven misol:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.modulith&lt;/groupId&gt;
    &lt;artifactId&gt;spring-modulith-starter-core&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.modulith&lt;/groupId&gt;
    &lt;artifactId&gt;spring-modulith-starter-test&lt;/artifactId&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;</code></pre><p>Test starter rasmiy hujjatlarda module integration testing uchun ishlatilishi ko‘rsatilgan. (<a href="https://docs.spring.io/spring-modulith/reference/testing.html?utm_source=chatgpt.com" title="Integration Testing Application Modules">Home</a>)</p><hr><h3>20. Arxitektor xulosasi</h3><p>Spring Modulith’ning asosiy qiymati:</p><pre><code class="language-text">Monolith qulayligini saqlaydi.
Microservice intizomini beradi.
Module boundary’ni test bilan tekshiradi.
Event orqali coupling’ni kamaytiradi.
Keyinchalik microservice’ga o‘tishni osonlashtiradi.</code></pre><p>Eng muhim qoida:</p><blockquote><p>Spring Modulith ishlatishdan maqsad annotation qo‘shish emas. Maqsad - biznes modullar orasidagi chegarani kodda ham, testda ham, dokumentatsiyada ham aniq qilish.</p></blockquote>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Taqsimlangan ma’lumotlar patternlari (Distributed Data Patterns)]]></title>
            <link>https://blog.jtscorp.uz/distributed-data-patterns</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/distributed-data-patterns</guid>
            <pubDate>Thu, 02 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Distributed data patterns</strong> - microservice yoki modullarga bo‘lingan system’da <strong>ma’lumotlar bir nechta joyda saqlanganda consistency, transaction, retry, event va failure</strong> muammolarini boshqarish usullari hisoblanadi.</p><p>Bu blok quyidagilarni o‘z ichiga oladi: <strong>SAGA orchestration vs choreography</strong>, <strong>Transactional Outbox + Debezium CDC</strong>, <strong>Idempotent Consumer</strong>, <strong>Distributed Locking</strong>, va <strong>Two-Phase Commit’dan qachon qochishni hal qiladi</strong>.</p><hr><h3>1. Muammo nimada?</h3><p>Monolith’da bitta database bo‘lsa, transaction oddiy:</p><pre><code class="language-text">Order yaratildi
Payment yozildi
Stock kamaytirildi
Delivery yaratildi
        ↓
bitta database transaction</code></pre><p>Kodda:</p><pre><code class="language-java">@Transactional
public void checkout() {
    orderRepository.save(order);
    paymentRepository.save(payment);
    stockRepository.decrease(productId);
    deliveryRepository.create(orderId);
}</code></pre><p>Agar xato bo‘lsa:</p><pre><code class="language-text">rollback</code></pre><p>Hammasi bitta database ichida qaytadi.</p><hr><h3>2. Distributed system’da muammo</h3><p>Microservice architecture’da har service odatda o‘z database’iga ega:</p><pre><code class="language-text">order-service       → order_db
payment-service     → payment_db
inventory-service   → inventory_db
delivery-service    → delivery_db</code></pre><p>Endi bitta <code>@Transactional</code> bilan hammasini boshqarib bo‘lmaydi.</p><p>Masalan checkout flow:</p><pre><code class="language-text">1. Order yaratish
2. Payment olish
3. Stock kamaytirish
4. Delivery yaratish</code></pre><p>Agar 3-qadamda stock kamaytirish yiqilsa:</p><pre><code class="language-text">Order yaratildi
Payment olindi
Stock kamaymadi
Delivery yaratilmadi</code></pre><p>System &quot;yarim bajarilgan&quot; holatda qoladi.</p><p>Distributed data patterns shuni boshqarish uchun kerak.</p><hr><h3>3. Muhim tushuncha: local transaction</h3><p>Har service faqat o‘z database’ida transaction qila oladi.</p><pre><code class="language-text">order-service:
@Transactional
order_db ichida transaction

payment-service:
@Transactional
payment_db ichida transaction

inventory-service:
@Transactional
inventory_db ichida transaction</code></pre><p>Lekin bularni bitta global transaction qilish murakkab va ko‘pincha yomon tanlov.</p><p>Shuning uchun distributed system’da ko‘proq quyidagi model ishlatiladi:</p><pre><code class="language-text">Local transaction
+ Event
+ Retry
+ Compensation
+ Idempotency
+ Observability</code></pre><hr><h3>4. SAGA Pattern</h3><p><strong>SAGA</strong> - uzun biznes transaction’ni bir nechta kichik local transactionlarga bo‘lish patterni.</p><p>Har qadam:</p><pre><code class="language-text">local transaction bajaradi
keyingi qadamni chaqiradi yoki event chiqaradi
xato bo‘lsa compensation ishlaydi</code></pre><p>Masalan order checkout:</p><pre><code class="language-text">Create Order
   ↓
Reserve Stock
   ↓
Take Payment
   ↓
Create Delivery
   ↓
Complete Order</code></pre><p>Agar payment fail bo‘lsa:</p><pre><code class="language-text">Payment failed
   ↓
Release Stock
   ↓
Cancel Order</code></pre><p>Bu yerda rollback database rollback emas. Bu <strong>compensating action </strong>deyiladi.</p><hr><h3>5. Compensation nima?</h3><p>Compensation - oldin bajarilgan qadamni biznes jihatdan bekor qilish.</p><p>Misollar:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Amal</p></th><th rowspan="1" colspan="1"><p>Compensation</p></th></tr><tr><td rowspan="1" colspan="1"><p>Order yaratildi</p></td><td rowspan="1" colspan="1"><p>Order cancel qilinadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Stock reserve qilindi</p></td><td rowspan="1" colspan="1"><p>Stock release qilinadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Payment olindi</p></td><td rowspan="1" colspan="1"><p>Refund qilinadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Delivery yaratildi</p></td><td rowspan="1" colspan="1"><p>Delivery cancel qilinadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Bonus berildi</p></td><td rowspan="1" colspan="1"><p>Bonus qaytarib olinadi</p></td></tr></tbody></table><p>Muhim:</p><blockquote><p>Compensation har doim real rollback emas. Masalan payment refund qilinsa, pul darhol qaytmasligi mumkin. Lekin biznes holat tuzatiladi.</p></blockquote><hr><h3>6. SAGA ikki xil bo‘ladi</h3><pre><code class="language-text">1. Choreography
2. Orchestration</code></pre><hr><h3>7. SAGA Choreography</h3><p>Choreography’da markaziy boshqaruvchi yo‘q. Har service event eshitadi va o‘z ishini qiladi.</p><p>Flow:</p><pre><code class="language-text">OrderCreatedEvent
        ↓
Inventory service stock reserve qiladi
        ↓
StockReservedEvent
        ↓
Payment service pul oladi
        ↓
PaymentCompletedEvent
        ↓
Delivery service delivery yaratadi
        ↓
DeliveryCreatedEvent
        ↓
Order service orderni CONFIRMED qiladi</code></pre><hr><h4>Choreography example</h4><pre><code class="language-text">order-service:
OrderCreatedEvent publish qiladi

inventory-service:
OrderCreatedEvent eshitadi
StockReservedEvent publish qiladi

payment-service:
StockReservedEvent eshitadi
PaymentCompletedEvent publish qiladi

delivery-service:
PaymentCompletedEvent eshitadi
DeliveryCreatedEvent publish qiladi</code></pre><p>Kodda ko‘rinishi:</p><pre><code class="language-java">@Component
class InventoryOnOrderCreated {

    private final InventoryService inventoryService;
    private final ApplicationEventPublisher events;

    @EventListener
    public void on(OrderCreatedEvent event) {
        inventoryService.reserve(event.orderId(), event.items());

        events.publishEvent(new StockReservedEvent(
                event.orderId(),
                event.items()
        ));
    }
}</code></pre><hr><h4>Choreography afzalliklari</h4><pre><code class="language-text">loose coupling
markaziy coordinator yo‘q
service’lar mustaqilroq
event-driven architecture uchun tabiiy
kichik flowlarda yaxshi</code></pre><hr><h4>Choreography kamchiliklari</h4><pre><code class="language-text">flow qayerda tugashini tushunish qiyin
debug qilish murakkab
event chain ko‘payib ketadi
failure handling tarqalib ketadi
business process diagrammasi kodda aniq ko‘rinmaydi</code></pre><p>Katta flow’da shunday bo‘lib ketadi:</p><pre><code class="language-text">OrderCreated → StockReserved → PaymentStarted → PaymentCompleted
→ DeliveryCreated → InvoiceGenerated → NotificationSent</code></pre><p>Xato qayerda bo‘lganini topish qiyinlashadi.</p><hr><h3>8. SAGA Orchestration</h3><p>Orchestration’da bitta markaziy <strong>orchestrator</strong> flow’ni boshqaradi.</p><p>Masalan:</p><pre><code class="language-text">OrderSagaOrchestrator
   ↓
InventoryService.reserve()
   ↓
PaymentService.charge()
   ↓
DeliveryService.create()
   ↓
OrderService.confirm()</code></pre><p>Agar payment fail bo‘lsa:</p><pre><code class="language-text">OrderSagaOrchestrator
   ↓
InventoryService.release()
   ↓
OrderService.cancel()</code></pre><hr><h4>Orchestration misol kod:</h4><pre><code class="language-java">@Service
public class OrderSagaOrchestrator {

    private final InventoryClient inventoryClient;
    private final PaymentClient paymentClient;
    private final DeliveryClient deliveryClient;
    private final OrderService orderService;

    public void process(Long orderId) {
        try {
            inventoryClient.reserve(orderId);
            paymentClient.charge(orderId);
            deliveryClient.create(orderId);
            orderService.confirm(orderId);
        } catch (PaymentFailedException ex) {
            inventoryClient.release(orderId);
            orderService.cancel(orderId);
        }
    }
}</code></pre><p>Bu soddalashtirilgan. Production’da retry, timeout, state machine, event log, idempotency kerak bo‘ladi.</p><hr><h4>Orchestration afzalliklari</h4><pre><code class="language-text">business flow bitta joyda ko‘rinadi
debug qilish osonroq
compensation tartibli
murakkab processlar uchun yaxshi
monitoring qilish oson</code></pre><hr><h4>Orchestration kamchiliklari</h4><pre><code class="language-text">orchestrator ko‘p narsani biladi
coupling kuchayishi mumkin
orchestrator failure nuqtasi bo‘lishi mumkin
service’lar orasida command-style dependency paydo bo‘ladi</code></pre><hr><h3>9. Choreography vs Orchestration</h3><table><tbody><tr><th rowspan="1" colspan="1"><p style="text-align: center;"><strong>Mezon</strong></p></th><th rowspan="1" colspan="1"><p style="text-align: center;"><strong>Choreography</strong></p></th><th rowspan="1" colspan="1"><p style="text-align: center;"><strong>Orchestration</strong></p></th></tr><tr><td rowspan="1" colspan="1"><p>Boshqaruvchi</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td><td rowspan="1" colspan="1"><p>Bor</p></td></tr><tr><td rowspan="1" colspan="1"><p>Coupling</p></td><td rowspan="1" colspan="1"><p>Pastroq</p></td><td rowspan="1" colspan="1"><p>Yuqoriroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Flow visibility</p></td><td rowspan="1" colspan="1"><p>Pastroq</p></td><td rowspan="1" colspan="1"><p>Yuqori</p></td></tr><tr><td rowspan="1" colspan="1"><p>Debug</p></td><td rowspan="1" colspan="1"><p>Qiyinroq</p></td><td rowspan="1" colspan="1"><p>Osonroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Kichik flow</p></td><td rowspan="1" colspan="1"><p>Yaxshi</p></td><td rowspan="1" colspan="1"><p>Ortiqcha bo‘lishi mumkin</p></td></tr><tr><td rowspan="1" colspan="1"><p>Murakkab flow</p></td><td rowspan="1" colspan="1"><p>Chalkashishi mumkin</p></td><td rowspan="1" colspan="1"><p>Yaxshi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Monitoring</p></td><td rowspan="1" colspan="1"><p>Tarqoq</p></td><td rowspan="1" colspan="1"><p>Markazlashgan</p></td></tr></tbody></table><p>Qisqa qoida:</p><pre><code class="language-text">Kichik, oddiy event flow → choreography
Murakkab, ko‘p qadamli biznes process → orchestration</code></pre><hr><h3>10. Transactional Outbox Pattern</h3><p>Distributed system’da eng katta muammolardan biri:</p><pre><code class="language-text">Database’ga yozildi, lekin event publish bo‘lmadi.</code></pre><p>Masalan:</p><pre><code class="language-java">@Transactional
public void createOrder(CreateOrderCommand command) {
    orderRepository.save(order);

    kafkaTemplate.send(&quot;order-created&quot;, event);
}</code></pre><p>Bu xavfli.</p><p>Nima bo‘lishi mumkin?</p><pre><code class="language-text">Order DB’ga yozildi
Kafka publish vaqtida broker down bo‘ldi
Transaction commit bo‘ldi
Lekin event ketmadi
Payment service order borligini bilmadi</code></pre><p>Yoki teskarisi:</p><pre><code class="language-text">Kafka event ketdi
DB transaction rollback bo‘ldi
Payment service mavjud bo‘lmagan order uchun payment boshladi</code></pre><hr><h3>11. Outbox yechimi</h3><p>Transactional Outbox’da biznes data va event bir xil database transaction’da yoziladi.</p><pre><code class="language-text">orders table
outbox_events table</code></pre><p>Transaction:</p><pre><code class="language-text">1. orders table’ga order yoziladi
2. outbox_events table’ga OrderCreated event yoziladi
3. transaction commit</code></pre><p>Keyin alohida process outbox’dan eventni brokerga yuboradi.</p><hr><h4>Outbox table</h4><pre><code class="language-sql">CREATE TABLE outbox_events (
    id UUID PRIMARY KEY,
    aggregate_type VARCHAR(100) NOT NULL,
    aggregate_id VARCHAR(100) NOT NULL,
    event_type VARCHAR(100) NOT NULL,
    payload JSONB NOT NULL,
    status VARCHAR(30) NOT NULL,
    created_at TIMESTAMP NOT NULL,
    published_at TIMESTAMP
);</code></pre><hr><h4>Order create</h4><pre><code class="language-java">@Transactional
public void createOrder(CreateOrderCommand command) {
    Order order = Order.create(command.customerId(), command.items());

    orderRepository.save(order);

    OutboxEvent event = OutboxEvent.of(
            &quot;Order&quot;,
            order.getId().toString(),
            &quot;OrderCreated&quot;,
            toJson(new OrderCreatedEvent(order.getId(), order.getTotal()))
    );

    outboxRepository.save(event);
}</code></pre><p>Muhim:</p><pre><code class="language-text">Order va OutboxEvent bitta transaction’da commit bo‘ladi.</code></pre><hr><h3>12. Outbox publisher</h3><p>Keyin background worker eventlarni publish qiladi:</p><pre><code class="language-java">@Scheduled(fixedDelay = 1000)
public void publishOutboxEvents() {
    List&lt;OutboxEvent&gt; events = outboxRepository.findTop100ByStatus(&quot;NEW&quot;);

    for (OutboxEvent event : events) {
        kafkaTemplate.send(event.getEventType(), event.getPayload());

        event.markPublished();
        outboxRepository.save(event);
    }
}</code></pre><p>Bu oddiy variant. Production’da quyidagilar kerak:</p><pre><code class="language-text">batch processing
locking
retry count
dead-letter status
idempotent publish
ordering
metrics
backpressure</code></pre><hr><h3>13. Outbox + Debezium CDC</h3><p>Outbox polling qilish mumkin, lekin katta system’da ko‘pincha <strong>CDC</strong> ishlatiladi.</p><p>CDC - <strong>Change Data Capture</strong>.</p><p>Flow:</p><pre><code class="language-text">Application
   ↓
orders + outbox_events DB’ga yozadi
   ↓
Debezium database transaction log’ni o‘qiydi
   ↓
Outbox row paydo bo‘lganini ko‘radi
   ↓
Kafka’ga event chiqaradi</code></pre><p>Architecture:</p><pre><code class="language-text">order-service
   ↓ local transaction
PostgreSQL
   ↓ WAL/binlog
Debezium
   ↓
Kafka topic
   ↓
payment-service</code></pre><hr><h4>Nima uchun Debezium yaxshi?</h4><pre><code class="language-text">application polling qilmaydi
DB transaction log’dan ishonchli o‘qiydi
event yo‘qolish xavfi kamayadi
high-throughput event publishing uchun yaxshi
outbox relay alohida infrastructure bo‘ladi</code></pre><p>Lekin complexity oshadi:</p><pre><code class="language-text">Debezium connector setup
Kafka dependency
schema evolution
connector monitoring
offset management
DLQ strategy</code></pre><hr><h3>14. Outbox pattern nimani kafolatlaydi?</h3><p>Transactional Outbox shuni kafolatlaydi:</p><pre><code class="language-text">Agar order DB’da bo‘lsa, outbox event ham DB’da bo‘ladi.</code></pre><p>Lekin shuni kafolatlamaydi:</p><pre><code class="language-text">consumer eventni faqat bir marta oladi
event duplicate bo‘lmaydi
event doim tartibda keladi
consumer xatosiz ishlaydi</code></pre><p>Shuning uchun keyingi pattern kerak:</p><pre><code class="language-text">Idempotent Consumer</code></pre><hr><h3>15. Idempotent Consumer Pattern</h3><p>Event-driven system’da bitta event bir necha marta kelishi mumkin.</p><p>Sabablar:</p><pre><code class="language-text">producer retry
broker retry
consumer ack bermadi
consumer qayta ishga tushdi
network timeout
outbox relay duplicate publish qildi</code></pre><p>Demak consumer shunday yozilishi kerak:</p><blockquote><p>Bitta event 2 marta kelsa ham biznes natija 1 marta bajarilsin.</p></blockquote><p>Bu <strong>idempotency </strong>deb ataladi.</p><hr><h4>Yomon consumerga misol:</h4><pre><code class="language-java">@KafkaListener(topics = &quot;payment-completed&quot;)
public void on(PaymentCompletedEvent event) {
    orderService.markPaid(event.orderId());
    notificationService.sendPaymentSuccess(event.orderId());
}</code></pre><p>Agar event 2 marta kelsa:</p><pre><code class="language-text">order ikki marta update bo‘lishi mumkin
notification ikki marta ketishi mumkin
bonus ikki marta yozilishi mumkin</code></pre><hr><h4>Yaxshi consumer: processed_events table</h4><pre><code class="language-sql">CREATE TABLE processed_events (
    event_id UUID PRIMARY KEY,
    processed_at TIMESTAMP NOT NULL
);</code></pre><p>Consumer:</p><pre><code class="language-java">@Transactional
public void handle(PaymentCompletedEvent event) {
    if (processedEventRepository.existsById(event.eventId())) {
        return;
    }

    orderService.markPaid(event.orderId());

    processedEventRepository.save(new ProcessedEvent(
            event.eventId(),
            Instant.now()
    ));
}</code></pre><p>Bu bilan event duplicate kelsa, ikkinchi marta bajarilmaydi.</p><hr><h3>16. Idempotency key</h3><p>REST API’da ham idempotency kerak bo‘ladi.</p><p>Masalan payment API:</p><pre><code class="language-http">POST /payments
Idempotency-Key: 8f1a-xyz-123</code></pre><p>Agar client timeout sabab requestni qayta yuborsa, payment ikki marta olinmasligi kerak.</p><p>Table:</p><pre><code class="language-sql">CREATE TABLE idempotency_keys (
    key VARCHAR(100) PRIMARY KEY,
    request_hash VARCHAR(255),
    response_body JSONB,
    status VARCHAR(30),
    created_at TIMESTAMP
);</code></pre><p>Flow:</p><pre><code class="language-text">1. Request keldi
2. Idempotency-Key tekshirildi
3. Oldin bajarilgan bo‘lsa, eski response qaytarildi
4. Yangi bo‘lsa, operation bajarildi
5. Natija key bilan saqlandi</code></pre><hr><h3>17. Idempotent Consumer’da muhim qoidalar</h3><pre><code class="language-text">event’da unique eventId bo‘lsin
processed_events table transaction ichida yozilsin
business operation va processed marker bitta transaction’da bo‘lsin
side effectlar ehtiyot bilan qilinsin
notification/email ham duplicate-safe bo‘lsin</code></pre><p>Yomon:</p><pre><code class="language-text">email yuborildi
keyin processed_events yozishda xato bo‘ldi
event qayta keldi
email yana yuborildi</code></pre><p>Yechim:</p><pre><code class="language-text">email ham outbox orqali yuboriladi
yoki notification service idempotent bo‘ladi</code></pre><hr><h3>18. Distributed Locking</h3><p>Distributed lock - bir nechta instance bir xil ishni bir vaqtda bajarmasligi uchun lock.</p><p>Masalan:</p><pre><code class="language-text">order-service 3 ta podda ishlayapti
har pod scheduled job ishga tushiradi
invoice generation faqat bitta podda ishlashi kerak</code></pre><p>Distributed lock kerak bo‘lishi mumkin:</p><pre><code class="language-text">scheduled job faqat bitta instance’da
bir resource’ni bir vaqtda bitta process update qilsin
inventory reservation
leader election
rate-limited external API call</code></pre><hr><h3>19. Redis/Redisson lock</h3><p>Spring projectlarda ko‘p ishlatiladigan variant:</p><pre><code class="language-java">RLock lock = redissonClient.getLock(&quot;invoice-generation&quot;);

boolean acquired = lock.tryLock(5, 30, TimeUnit.SECONDS);

if (!acquired) {
    return;
}

try {
    invoiceService.generateInvoices();
} finally {
    lock.unlock();
}</code></pre><p>Bu nimani anglatadi?</p><pre><code class="language-text">5 sekund ichida lock olishga urinadi
lock olinsa 30 sekund lease time
ish tugasa unlock qiladi</code></pre><hr><h3>20. Distributed lock xavflari</h3><p>Distributed lock oson ko‘rinadi, lekin xavfli.</p><p>Muammolar:</p><pre><code class="language-text">lock olindi, process o‘ldi
lease time noto‘g‘ri tanlandi
network partition
clock drift
long-running job lease’dan uzoq davom etdi
unlock noto‘g‘ri instance’dan qilindi
Redis unavailable bo‘ldi</code></pre><p>Qoida:</p><blockquote><p>Distributed lock’ni transaction o‘rniga ishlatmang. U faqat coordination uchun.</p></blockquote><hr><h3>21. Database lock alternativasi</h3><p>Ba’zan DB lock yetarli bo‘ladi.</p><p>Masalan PostgreSQL advisory lock:</p><pre><code class="language-sql">SELECT pg_try_advisory_lock(12345);</code></pre><p>Yoki row-level lock:</p><pre><code class="language-sql">SELECT *
FROM jobs
WHERE status = &#039;NEW&#039;
FOR UPDATE SKIP LOCKED
LIMIT 100;</code></pre><p><code>SKIP LOCKED</code> job processing uchun juda foydali:</p><pre><code class="language-text">bir nechta worker bor
har biri lock bo‘lmagan joblarni oladi
bir job ikki workerga tushmaydi</code></pre><hr><h3>22. Distributed lock qachon kerak emas?</h3><p>Ko‘p holatda lock o‘rniga yaxshiroq pattern bor:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Muammo</p></th><th rowspan="1" colspan="1"><p>Lock o‘rniga</p></th></tr><tr><td rowspan="1" colspan="1"><p>Duplicate event</p></td><td rowspan="1" colspan="1"><p>Idempotent consumer</p></td></tr><tr><td rowspan="1" colspan="1"><p>Event publish reliability</p></td><td rowspan="1" colspan="1"><p>Outbox</p></td></tr><tr><td rowspan="1" colspan="1"><p>Concurrent update</p></td><td rowspan="1" colspan="1"><p>Optimistic locking</p></td></tr><tr><td rowspan="1" colspan="1"><p>Job processing</p></td><td rowspan="1" colspan="1"><p>Queue + SKIP LOCKED</p></td></tr><tr><td rowspan="1" colspan="1"><p>Global order</p></td><td rowspan="1" colspan="1"><p>Kafka partition key</p></td></tr><tr><td rowspan="1" colspan="1"><p>Payment duplicate</p></td><td rowspan="1" colspan="1"><p>Idempotency key</p></td></tr></tbody></table><p>Arxitektor qoida:</p><blockquote><p>Lock - oxirgi variantlardan biri. Avval idempotency, queue, database constraint, optimistic locking o‘ylansin.</p></blockquote><hr><h3>23. Two-Phase Commit - 2PC</h3><p><strong>Two-Phase Commit</strong> - bir nechta resource orasida distributed transaction qilish usuli.</p><p>Masalan:</p><pre><code class="language-text">order_db
payment_db
inventory_db</code></pre><p>hammasi bitta global transaction’da commit/rollback bo‘lishi kerak.</p><p>2PC ikki bosqich:</p><pre><code class="language-text">1. Prepare phase
2. Commit phase</code></pre><p>Coordinator resource’lardan so‘raydi:</p><pre><code class="language-text">&quot;Commit qilishga tayyormisiz?&quot;</code></pre><p>Hammasi &quot;ha&quot; desa:</p><pre><code class="language-text">commit</code></pre><p>Biri &quot;yo‘q&quot; desa:</p><pre><code class="language-text">rollback</code></pre><hr><h3>24. 2PC nega yomon?</h3><p>Nazariy jihatdan chiroyli. Amalda distributed microservice’da ko‘pincha yomon.</p><p>Kamchiliklar:</p><pre><code class="language-text">coordinator kerak
resource’lar lock ushlab turadi
latency oshadi
availability pasayadi
network failure’da murakkab holat
service autonomy buziladi
cloud-native architecture’ga mos emas
scaling qiyin</code></pre><p>Microservice’da bitta service yiqilsa, butun transaction osilib qolishi mumkin.</p><hr><h3>25. 2PC qachon ishlatilishi mumkin?</h3><p>Ba’zi enterprise systemlarda hali ham ishlatiladi:</p><pre><code class="language-text">legacy enterprise app
XA transaction support bor
kam traffic
strong consistency juda muhim
bitta organization ichidagi controlled environment</code></pre><p>Lekin zamonaviy cloud-native Spring microservice architecture’da ko‘pincha:</p><pre><code class="language-text">SAGA + Outbox + Idempotency</code></pre><p>yaxshiroq.</p><hr><h3>26. 2PC o‘rniga nima?</h3><table><tbody><tr><th rowspan="1" colspan="1"><p>Kerak bo‘lgan narsa</p></th><th rowspan="1" colspan="1"><p>Tavsiya</p></th></tr><tr><td rowspan="1" colspan="1"><p>Cross-service business transaction</p></td><td rowspan="1" colspan="1"><p>SAGA</p></td></tr><tr><td rowspan="1" colspan="1"><p>Reliable event publish</p></td><td rowspan="1" colspan="1"><p>Transactional Outbox</p></td></tr><tr><td rowspan="1" colspan="1"><p>DB change event</p></td><td rowspan="1" colspan="1"><p>Debezium CDC</p></td></tr><tr><td rowspan="1" colspan="1"><p>Duplicate eventdan himoya</p></td><td rowspan="1" colspan="1"><p>Idempotent Consumer</p></td></tr><tr><td rowspan="1" colspan="1"><p>Concurrent update</p></td><td rowspan="1" colspan="1"><p>Optimistic Locking</p></td></tr><tr><td rowspan="1" colspan="1"><p>One-at-a-time job</p></td><td rowspan="1" colspan="1"><p>Queue / DB lock / Redis lock</p></td></tr><tr><td rowspan="1" colspan="1"><p>Strong audit</p></td><td rowspan="1" colspan="1"><p>Event log / outbox / audit table</p></td></tr></tbody></table><hr><h3>27. Consistency turlari</h3><p>Distributed system’da ko‘pincha <strong>eventual consistency</strong> qabul qilinadi.</p><h4>Strong consistency</h4><pre><code class="language-text">hamma joy darhol bir xil</code></pre><p>Monolith + bitta DB’da osonroq.</p><h4>Eventual consistency</h4><pre><code class="language-text">hozircha order CREATED
bir necha sekunddan keyin PAID
keyin CONFIRMED</code></pre><p>Ya’ni system vaqt o‘tib consistent bo‘ladi.</p><p>Misol:</p><pre><code class="language-text">Order created
Payment processing
Payment completed
Order confirmed</code></pre><p>Foydalanuvchiga status ko‘rsatiladi:</p><pre><code class="language-json">{
  &quot;orderId&quot;: 123,
  &quot;status&quot;: &quot;PAYMENT_PROCESSING&quot;
}</code></pre><hr><h3>28. Eventual consistency UX masalasi</h3><p>Backend eventual consistent bo‘lsa, frontend ham shuni tushunishi kerak.</p><p>Yomon UX:</p><pre><code class="language-text">Buyurtma yaratildi, lekin payment hali tugamagan.
Frontend &quot;xato&quot; deb ko‘rsatadi.</code></pre><p>Yaxshi UX:</p><pre><code class="language-text">Buyurtma qabul qilindi.
To‘lov tekshirilmoqda.
Statusni yangilab turamiz.</code></pre><p>Statuslar:</p><pre><code class="language-text">CREATED
STOCK_RESERVED
PAYMENT_PROCESSING
PAID
CONFIRMED
FAILED
CANCELLED</code></pre><hr><h3>29. Ordering muammosi</h3><p>Eventlar noto‘g‘ri tartibda kelishi mumkin:</p><pre><code class="language-text">PaymentCompletedEvent oldin keldi
OrderCreatedEvent keyin keldi</code></pre><p>Yoki bir aggregate uchun eventlar tartibi buziladi.</p><p>Yechimlar:</p><pre><code class="language-text">Kafka partition key = aggregateId
event version
sequence number
consumer buffering
idempotent state transition</code></pre><p>Masalan:</p><pre><code class="language-java">public record OrderEvent(
        UUID eventId,
        Long orderId,
        long version,
        String type,
        Instant occurredAt
) {}</code></pre><p>Consumer eski versionni ko‘rsa ignore qiladi.</p><hr><h3>30. Optimistic locking</h3><p>Concurrent update uchun distributed lock shart emas. Ko‘p holatda optimistic locking yetarli.</p><p>Entity:</p><pre><code class="language-java">@Entity
public class OrderEntity {

    @Id
    private Long id;

    @Version
    private Long version;

    private OrderStatus status;
}</code></pre><p>Agar ikki transaction bir orderni update qilsa:</p><pre><code class="language-text">birinchisi yutadi
ikkinchisi OptimisticLockException oladi</code></pre><p>Keyin retry yoki business error.</p><p>Bu inventory, order status, account update kabi joylarda foydali.</p><hr><h3>31. Transactional Inbox Pattern</h3><p>Outbox producer tomonida reliable event publish uchun.</p><p>Inbox consumer tomonida reliable event processing uchun.</p><pre><code class="language-text">incoming_events table</code></pre><p>Consumer eventni avval inbox’ga yozadi, keyin process qiladi.</p><p>Flow:</p><pre><code class="language-text">Kafka event keldi
   ↓
incoming_events table’ga yozildi
   ↓
business processing
   ↓
processed status</code></pre><p>Bu ayniqsa complex consumer processing uchun foydali bo&#039;ladi.</p><hr><h3>32. Dead Letter Queue - DLQ</h3><p>Agar event qayta-qayta process bo‘lmasa, cheksiz retry qilish yomon.</p><p>Yechim:</p><pre><code class="language-text">retry 3 marta
keyin DLQ topic
alert
manual investigation
replay tool</code></pre><p>Flow:</p><pre><code class="language-text">payment-events
   ↓
consumer fail
   ↓ retry
consumer fail
   ↓ retry
consumer fail
   ↓
payment-events-dlq</code></pre><p>DLQ production’da shart:</p><pre><code class="language-text">xato event yo‘qolmasin
consumer to‘xtab qolmasin
manual fix/replay bo‘lsin</code></pre><hr><h3>33. Real flow: Order checkout</h3><p>Production-ready distributed flow:</p><pre><code class="language-text">1. order-service order yaratadi
2. order_db orders + outbox_events yoziladi
3. Debezium outbox eventni Kafka’ga chiqaradi
4. inventory-service OrderCreatedEvent eshitadi
5. inventory stock reserve qiladi
6. inventory outbox StockReservedEvent chiqaradi
7. payment-service StockReservedEvent eshitadi
8. payment idempotency key bilan charge qiladi
9. PaymentCompletedEvent chiqadi
10. order-service eventni idempotent consume qiladi
11. order status CONFIRMED bo‘ladi</code></pre><p>Failure case:</p><pre><code class="language-text">payment fail
   ↓
PaymentFailedEvent
   ↓
inventory stock release
   ↓
order status CANCELLED</code></pre><hr><h3>34. Spring Boot structure</h3><pre><code class="language-text">com.company.order
 ├── domain
 │    ├── Order.java
 │    └── OrderStatus.java
 │
 ├── application
 │    ├── CreateOrderHandler.java
 │    ├── ConfirmOrderHandler.java
 │    └── CancelOrderHandler.java
 │
 ├── outbox
 │    ├── OutboxEvent.java
 │    ├── OutboxRepository.java
 │    └── OutboxService.java
 │
 ├── inbox
 │    ├── ProcessedEvent.java
 │    └── ProcessedEventRepository.java
 │
 ├── messaging
 │    ├── OrderEventPublisher.java
 │    └── PaymentCompletedListener.java
 │
 └── web
      └── OrderController.java</code></pre><hr><h3>35. Eng ko‘p xatolar</h3><h4>Xato 1: <code>@Transactional</code> bilan Kafka publish qilish</h4><pre><code class="language-java">@Transactional
public void createOrder() {
    orderRepository.save(order);
    kafkaTemplate.send(&quot;order-created&quot;, event);
}</code></pre><p>Bu atomic emas.</p><p>Yaxshiroq:</p><pre><code class="language-text">DB + Outbox bitta transaction
Kafka publish alohida relay/CDC orqali</code></pre><hr><h4>Xato 2: Consumer idempotent emas</h4><p>Duplicate event kelganda biznes ikki marta bajariladi.</p><hr><h4>Xato 3: SAGA’da compensation yo‘q</h4><p>Flow fail bo‘lsa system yarim holatda qoladi.</p><hr><h4>Xato 4: Distributed lock bilan hamma narsani yechish</h4><p>Lock consistency muammolarini to‘liq hal qilmaydi. Ko‘p holatda idempotency va outbox kerak.</p><hr><h4>Xato 5: 2PC’ni microservice’da default tanlash</h4><p>Strong consistency xohishi availability va scalability’ni yomonlashtirishi mumkin.</p><hr><h4>Xato 6: Event schema versioning yo‘q</h4><p>Event o‘zgarsa consumerlar buziladi.</p><p>Yaxshi event:</p><pre><code class="language-json">{
  &quot;eventId&quot;: &quot;uuid&quot;,
  &quot;eventType&quot;: &quot;OrderCreated&quot;,
  &quot;eventVersion&quot;: 1,
  &quot;occurredAt&quot;: &quot;2026-05-21T10:00:00Z&quot;,
  &quot;payload&quot;: {
    &quot;orderId&quot;: 123
  }
}</code></pre><hr><h3>36. Architect checklist</h3><p>Distributed data design qilishdan oldin savollar javob berish kerak:</p><pre><code class="language-text">Har service o‘z database’iga egami?
Cross-service transaction bormi?
Failure bo‘lsa compensation qanday?
Event publish reliable bo‘ladimi?
Consumer duplicate eventga tayyormi?
Event ordering kerakmi?
DLQ strategy bormi?
Retry policy qanday?
Business statuslar eventual consistency’ni ko‘rsatadimi?
Observability event flow’ni ko‘rsatadimi?
2PC haqiqatan kerakmi yoki SAGA yetadimi?</code></pre><hr><h3>37. Qisqa tavsiya</h3><p>Katta Spring microservice system uchun odatiy stack:</p><pre><code class="language-text">SAGA
+ Transactional Outbox
+ Debezium CDC
+ Kafka
+ Idempotent Consumer
+ DLQ
+ Optimistic Locking
+ Observability</code></pre><p>Lock kerak bo‘lsa:</p><pre><code class="language-text">avval database constraint / optimistic lock / queue o‘yla
keyin Redis/Redisson distributed lock</code></pre><p>2PC kerak bo‘lsa:</p><pre><code class="language-text">juda ehtiyot bo‘l
cloud-native microservice’da default tanlama</code></pre><hr><h3>38. Arxitektor xulosasi</h3><p><strong>Distributed data patterns</strong> - distributed system’da &quot;hammasi bitta transaction bo‘lsin&quot; degan fikrdan voz kechib, real failure’larni boshqarish yondashuvi hisoblanadi.</p><p>Asosiy formula:</p><pre><code class="language-text">Local transaction
+ Reliable event publishing
+ Idempotent consuming
+ Compensation
+ Retry/DLQ
+ Observability
= production-ready distributed data architecture</code></pre><p>Eng muhim qoida:</p><blockquote><p>Distributed system’da failure normal holat. Architecture failure bo‘lmasligiga emas, failure bo‘lganda system to‘g‘ri tiklanishiga quriladi.</p></blockquote>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[GraalVM Native & AOT]]></title>
            <link>https://blog.jtscorp.uz/graalvm-native-aot</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/graalvm-native-aot</guid>
            <pubDate>Thu, 02 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>GraalVM Native &amp; AOT</strong> - Spring Boot application’ni odatdagi JVM’da ishga tushirishdan tashqari, <strong>oldindan kompilyatsiya qilingan native executable</strong> ko‘rinishida yuritish mavzusi.</p><p>Bu blok quyidagilarni o‘z ichiga oladi: <strong>Spring Native / GraalVM native image</strong>, <strong>Spring 6 AOT compilation</strong>, <strong>reflection hints &amp; proxy hints</strong>, <strong>native image trade-offlari</strong>, va <strong>qachon native, qachon JVM tanlash</strong>.</p><hr><h3>1. Muammo nimada?</h3><p>Oddiy Spring Boot application odatda JVM’da ishlaydi:</p><pre><code class="language-text">Java code
   ↓
Bytecode
   ↓
JVM
   ↓
Runtime classpath scanning
   ↓
Spring context startup
   ↓
Application ready</code></pre><p>Spring Boot application start vaqtida ko‘p ish qiladi:</p><pre><code class="language-text">classpath scan
annotation scan
bean definition yaratish
auto-configuration tanlash
reflection ishlatish
proxy yaratish
configuration binding
condition tekshirish</code></pre><p>Katta service’da bu startup vaqtini oshiradi.</p><p>Masalan:</p><pre><code class="language-text">order-service JVM startup: 5-20 sekund
serverless function cold start: juda sekin
Kubernetes scale-up: pod tez ready bo‘lmaydi</code></pre><p>Shu yerda <strong>AOT</strong> va <strong>Native Image</strong> yordam beradi.</p><hr><h3>2. AOT nima?</h3><p><strong>AOT - Ahead-of-Time processing</strong>.</p><p>Ya’ni Spring runtime’da qiladigan ayrim ishlarni <strong>build vaqtida oldindan bajaradi</strong>.</p><p>Spring Framework hujjatlarida AOT optimizations <code>ApplicationContext</code>ni build vaqtida tekshirib, odatda runtime’da qilinadigan discovery va decision logic’ni oldindan bajarishi aytilgan. Bu startup arrangement’ni soddaroq va aniqroq qiladi. (<a href="https://docs.spring.io/spring-framework/reference/core/aot.html?utm_source=chatgpt.com" title="Ahead of Time Optimizations">Home</a>)</p><p>Oddiy JVM mode:</p><pre><code class="language-text">Application start bo‘ldi
   ↓
Spring classpath scan qildi
   ↓
Beanlarni topdi
   ↓
Proxy yaratdi
   ↓
Reflection metadata ishlatdi</code></pre><p>AOT mode:</p><pre><code class="language-text">Build vaqtida Spring analiz qiladi
   ↓
Bean definitions oldindan tayyorlanadi
   ↓
Reflection/proxy/resource hints generatsiya qilinadi
   ↓
Startup paytida kamroq ish qoladi</code></pre><hr><h3>3. GraalVM Native Image nima?</h3><p><strong>GraalVM Native Image</strong> Java application’ni JVM bytecode sifatida emas, balki <strong>native executable</strong> qilib build qiladi.</p><p>Ya’ni:</p><pre><code class="language-text">Spring Boot app
   ↓
AOT processing
   ↓
GraalVM native-image
   ↓
Linux/macOS/Windows executable</code></pre><p>Natija:</p><pre><code class="language-text">./order-service</code></pre><p>Uni ishga tushirish uchun alohida JVM o‘rnatish shart emas.</p><hr><h3>JVM vs Native Image</h3><table><tbody><tr><th rowspan="1" colspan="1"><p>Mezon</p></th><th rowspan="1" colspan="1"><p>JVM mode</p></th><th rowspan="1" colspan="1"><p>Native Image</p></th></tr><tr><td rowspan="1" colspan="1"><p>Startup</p></td><td rowspan="1" colspan="1"><p>Sekinroq</p></td><td rowspan="1" colspan="1"><p>Juda tez</p></td></tr><tr><td rowspan="1" colspan="1"><p>Memory footprint</p></td><td rowspan="1" colspan="1"><p>Ko‘proq bo‘lishi mumkin</p></td><td rowspan="1" colspan="1"><p>Ko‘pincha kamroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Peak throughput</p></td><td rowspan="1" colspan="1"><p>Ko‘pincha kuchliroq</p></td><td rowspan="1" colspan="1"><p>Ba’zida pastroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Build time</p></td><td rowspan="1" colspan="1"><p>Tezroq</p></td><td rowspan="1" colspan="1"><p>Ancha sekinroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Debug qilish</p></td><td rowspan="1" colspan="1"><p>Osonroq</p></td><td rowspan="1" colspan="1"><p>Qiyinroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Reflection</p></td><td rowspan="1" colspan="1"><p>Runtime’da erkinroq</p></td><td rowspan="1" colspan="1"><p>Oldindan hint kerak</p></td></tr><tr><td rowspan="1" colspan="1"><p>Dynamic class loading</p></td><td rowspan="1" colspan="1"><p>Moslashuvchan</p></td><td rowspan="1" colspan="1"><p>Cheklangan</p></td></tr><tr><td rowspan="1" colspan="1"><p>Serverless</p></td><td rowspan="1" colspan="1"><p>Har doim ham ideal emas</p></td><td rowspan="1" colspan="1"><p>Juda mos</p></td></tr><tr><td rowspan="1" colspan="1"><p>Long-running service</p></td><td rowspan="1" colspan="1"><p>Juda yaxshi</p></td><td rowspan="1" colspan="1"><p>Vaziyatga qarab</p></td></tr></tbody></table><hr><h3>4. Spring 6 AOT qanday ishlaydi?</h3><p>Spring 6 va Spring Boot 3 native image’ni qo‘llash uchun AOT engine bilan keladi.</p><p>Build vaqtida Spring quyidagilarni qiladi:</p><pre><code class="language-text">@Configuration classlarni analiz qiladi
@Bean metodlarni ko‘radi
@Component scan natijalarini oldindan hisoblaydi
conditional beanlarni tekshiradi
proxy kerak joylarni aniqlaydi
reflection kerak classlarni ro‘yxatga oladi
resource kerak bo‘lsa qo‘shadi
generated source/code yaratadi</code></pre><p>Bu native image uchun muhim, chunki GraalVM build vaqtida iloji boricha hamma narsani bilishi kerak.</p><hr><h3>5. Native Image nima uchun reflection’dan qo‘rqadi?</h3><p>Java’da reflection odatda runtime’da ishlaydi:</p><pre><code class="language-java">Class&lt;?&gt; clazz = Class.forName(&quot;com.company.Order&quot;);
Object obj = clazz.getDeclaredConstructor().newInstance();</code></pre><p>JVM buni runtime’da qila oladi.</p><p>Lekin native image build qilinayotganda GraalVM shunday savol beradi:</p><pre><code class="language-text">Bu class runtime’da reflection orqali ishlatiladimi?
Constructor kerakmi?
Field kerakmi?
Method kerakmi?
Annotation kerakmi?</code></pre><p>Agar oldindan aytilmasa, runtime’da xato chiqishi mumkin.</p><hr><h3>6. Runtime Hints</h3><p>Spring’da native image uchun reflection, resource, proxy kabi narsalarni oldindan ko‘rsatish kerak bo‘lsa, <strong>RuntimeHints</strong> ishlatiladi.</p><p>Masalan:</p><pre><code class="language-java">public class OrderRuntimeHints implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        hints.reflection().registerType(
                Order.class,
                MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
                MemberCategory.INVOKE_PUBLIC_METHODS,
                MemberCategory.DECLARED_FIELDS
        );
    }
}</code></pre><p>Keyin config’da ulaymiz:</p><pre><code class="language-java">@ImportRuntimeHints(OrderRuntimeHints.class)
@Configuration
public class OrderNativeConfiguration {
}</code></pre><p>Spring Framework hujjatlarida AOT restrictions borligi ko‘rsatiladi: build vaqtidagi optimizatsiya application’ni classpath va environment asosida oldindan belgilangan holatga keltiradi. Shuning uchun runtime’da juda dinamik behavior ishlatish native image’da muammo bo‘lishi mumkin. (<a href="https://docs.spring.io/spring-framework/reference/core/aot.html?utm_source=chatgpt.com" title="Ahead of Time Optimizations">Home</a>)</p><hr><h3>7. Proxy Hints</h3><p>Spring ko‘p joyda proxy ishlatadi:</p><pre><code class="language-text">@Transactional
@Async
@Cacheable
@Validated
Spring Security method security
AOP</code></pre><p>JVM’da proxy runtime’da yaratiladi.</p><p>Native image’da esa proxy kerakligini oldindan bilish kerak bo‘ladi.</p><p>Masalan interface proxy:</p><pre><code class="language-java">hints.proxies().registerJdkProxy(
        OrderService.class,
        SpringProxy.class,
        Advised.class
);</code></pre><p>Ko‘p oddiy Spring holatlarida Spring Boot AOT bularni avtomatik aniqlaydi. Lekin custom AOP, custom reflection yoki third-party library bo‘lsa, qo‘shimcha hint kerak bo‘lishi mumkin.</p><hr><h3>8. Resource Hints</h3><p>Agar application runtime’da fayl o‘qisa:</p><pre><code class="language-text">schema.sql
templates/email.html
certificates/public.pem
mapper-config.json</code></pre><p>native image ichiga bu resource’larni qo‘shish kerak.</p><p>Misol:</p><pre><code class="language-java">public class ResourceHints implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        hints.resources().registerPattern(&quot;templates/*.html&quot;);
        hints.resources().registerPattern(&quot;certificates/*.pem&quot;);
    }
}</code></pre><p>Aks holda JVM’da ishlagan narsa native’da topilmasligi mumkin.</p><hr><h3>9. Native Image build qilish</h3><h4>Maven</h4><p>Odatda Spring Boot native image uchun <code>native</code> profile ishlatiladi:</p><pre><code class="language-bash">./mvnw -Pnative native:compile</code></pre><p>Yoki container image:</p><pre><code class="language-bash">./mvnw -Pnative spring-boot:build-image</code></pre><h4>Gradle</h4><pre><code class="language-bash">./gradlew nativeCompile</code></pre><p>Natijada executable chiqadi:</p><pre><code class="language-text">target/order-service</code></pre><p>yoki:</p><pre><code class="language-text">build/native/nativeCompile/order-service</code></pre><hr><h3>10. Native application startup</h3><p>JVM:</p><pre><code class="language-bash">java -jar order-service.jar</code></pre><p>Native:</p><pre><code class="language-bash">./order-service</code></pre><p>Native’da startup odatda juda tez bo‘ladi:</p><pre><code class="language-text">JVM:    sekundlar
Native: millisekundlar yoki juda kam sekund</code></pre><p>Bu ayniqsa foydali:</p><pre><code class="language-text">serverless functions
CLI tools
short-lived jobs
Kubernetes rapid scale-up
cold start sensitive workloads
edge deployment</code></pre><hr><h3>11. Native Image trade-offlari</h3><p>Native image faqat &quot;tezroq&quot; degani emas. Bu <strong>trade-off</strong>.</p><p>GraalVM Native Image optimizatsiya va performance hujjatlarida native executable’ni performance, file size, build time va debuggability kabi metrikalar bo‘yicha sozlash mexanizmlari borligi aytiladi. (<a href="https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/?utm_source=chatgpt.com" title="Optimizations and Performance">GraalVM</a>)</p><h4>Afzalliklar</h4><pre><code class="language-text">startup juda tez
memory footprint ko‘pincha pastroq
JVM kerak emas
container image kichikroq bo‘lishi mumkin
serverless cold start yaxshi
Kubernetes scale-up tezroq</code></pre><h4>Kamchiliklar</h4><pre><code class="language-text">build vaqti uzoq
native build ko‘proq RAM talab qiladi
reflection/proxy/resource muammolari chiqishi mumkin
debug qilish qiyinroq
ba’zi librarylar native’da to‘liq mos emas
peak throughput JVM’dan past bo‘lishi mumkin
dynamic class loading cheklangan</code></pre><hr><h3>12. Startup vs Throughput</h3><p>Native image ko‘pincha <strong>startup</strong> bo‘yicha yutadi.</p><p>Lekin <strong>uzoq ishlaydigan, juda yuqori throughput talab qiladigan service</strong> uchun JVM ba’zida yaxshiroq bo‘lishi mumkin.</p><p>Sabab:</p><pre><code class="language-text">JVM JIT compiler runtime’da kodni optimallashtiradi
hot pathlarni aniqlaydi
uzoq ishlagan sari performance yaxshilanishi mumkin</code></pre><p>Native image esa oldindan kompilyatsiya qilingan. Startup tez, lekin JVM JIT’ning uzoq muddatli optimizatsiyalari yo‘q.</p><p>Arxitektor xulosa:</p><blockquote><p>Native image - har doim JVM’dan tezroq degani emas. U asosan startup va memory bo‘yicha kuchli.</p></blockquote><hr><h3>13. Memory masalasi</h3><p>GraalVM Native Image memory management hujjatlarida native image uchun turli GC implementatsiyalar mavjudligi, Serial GC default ekanligi va u kichik heap hamda past memory footprint uchun optimallashtirilgani ko‘rsatiladi. (<a href="https://docs.oracle.com/en/graalvm/jdk/22/docs/reference-manual/native-image/optimizations-and-performance/MemoryManagement/?embed=1&amp;utm_source=chatgpt.com" title="Memory Management">Oracle Docs</a>)</p><p>Bu serverless yoki kichik containerlarda foydali.</p><p>Lekin katta long-running API uchun faqat &quot;native kam RAM yeydi&quot; deb qaror qilish noto‘g‘ri. Har doim real workload bilan benchmark qilish kerak.</p><hr><h3>14. Native image’da muammo beradigan joylar</h3><p>Quyidagi narsalar native’da ehtiyot talab qiladi:</p><pre><code class="language-text">Reflection
Dynamic proxy
CGLIB proxy
Dynamic class loading
Serialization/deserialization
Jackson polymorphic mapping
Hibernate lazy proxy
JPA metamodel
Custom annotation scanning
JNI
File/resource loading
Third-party SDK
Scripting engines
Runtime-generated bytecode</code></pre><p>Masalan, Jackson DTO’larni reflection orqali ishlatsa, Spring Boot ko‘p holatda hintlarni avtomatik chiqaradi. Lekin custom serializer/deserializer, polymorphic type yoki external SDK bo‘lsa, qo‘lda hint kerak bo‘lishi mumkin.</p><hr><h3>15. Spring Boot + JPA + Native</h3><p>JPA native image’da ishlaydi, lekin oddiy REST API’dan murakkabroq.</p><p>Sabab:</p><pre><code class="language-text">Hibernate reflection ishlatadi
Entity proxy bor
Lazy loading bor
Annotation metadata kerak</code></pre><p>Spring Boot AOT ko‘p narsani hal qiladi. Lekin arxitektor sifatida buni bilish kerak:</p><blockquote><p>Native image’da eng oson workload - oddiy REST, HTTP client, JSON, small service. Eng murakkab workload - reflection-heavy ORM, dynamic library, eski SDK’lar.</p></blockquote><hr><h3>16. Qachon GraalVM Native tanlash kerak?</h3><h3>Juda yaxshi mos keladi</h3><pre><code class="language-text">Serverless function
CLI application
Short-lived batch job
Small REST microservice
Kubernetes’da tez scale-up kerak bo‘lgan service
Memory limit qattiq bo‘lgan container
Edge service
Internal tool</code></pre><p>Masalan:</p><pre><code class="language-text">notification-worker
image-resize-function
pdf-generator-cli
webhook-handler
small-auth-adapter</code></pre><hr><h4>Ehtiyotkorlik bilan tanlash kerak</h4><pre><code class="language-text">katta monolith
og‘ir JPA/Hibernate service
ko‘p reflection ishlatadigan legacy app
ko‘p dynamic plugin ishlatadigan system
yuqori throughput talab qiladigan long-running service
native support’i noaniq third-party SDK</code></pre><p>Bu holatda JVM ko‘pincha xavfsizroq.</p><hr><h3>17. JVM qachon yaxshiroq?</h3><p>JVM tanlash yaxshi bo‘ladi:</p><pre><code class="language-text">service doim ishlab tursa
startup critical bo‘lmasa
throughput muhim bo‘lsa
JIT optimizatsiya foydali bo‘lsa
debug/profiling muhim bo‘lsa
library ecosystem murakkab bo‘lsa
team native image tajribasiz bo‘lsa</code></pre><p>Masalan:</p><pre><code class="language-text">high-traffic payment API
large business monolith
analytics service
complex report generator
legacy integration service</code></pre><hr><h3>18. Native vs JVM qaror jadvali</h3><table><tbody><tr><th rowspan="1" colspan="1"><p>Savol</p></th><th rowspan="1" colspan="1"><p>Native tarafga</p></th><th rowspan="1" colspan="1"><p>JVM tarafga</p></th></tr><tr><td rowspan="1" colspan="1"><p>Startup juda muhimmi?</p></td><td rowspan="1" colspan="1"><p>Ha</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td></tr><tr><td rowspan="1" colspan="1"><p>Memory limit qattiqmi?</p></td><td rowspan="1" colspan="1"><p>Ha</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td></tr><tr><td rowspan="1" colspan="1"><p>Service qisqa ishlaydimi?</p></td><td rowspan="1" colspan="1"><p>Ha</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td></tr><tr><td rowspan="1" colspan="1"><p>Workload uzoq ishlaydimi?</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td><td rowspan="1" colspan="1"><p>Ha</p></td></tr><tr><td rowspan="1" colspan="1"><p>Reflection ko‘pmi?</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td><td rowspan="1" colspan="1"><p>Ha</p></td></tr><tr><td rowspan="1" colspan="1"><p>Build pipeline kuchlimi?</p></td><td rowspan="1" colspan="1"><p>Ha</p></td><td rowspan="1" colspan="1"><p>Shart emas</p></td></tr><tr><td rowspan="1" colspan="1"><p>Debug/profiling ko‘p kerakmi?</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td><td rowspan="1" colspan="1"><p>Ha</p></td></tr><tr><td rowspan="1" colspan="1"><p>Serverless bormi?</p></td><td rowspan="1" colspan="1"><p>Ha</p></td><td rowspan="1" colspan="1"><p>Kamroq</p></td></tr></tbody></table><hr><h3>19. Arxitektor qaror formulasi</h3><pre><code class="language-text">Agar startup + memory muhim bo‘lsa
va application nisbatan statik bo‘lsa
va librarylar native image’ni qo‘llasa
→ GraalVM Native ko‘rib chiqiladi.

Agar service long-running bo‘lsa
va throughput muhim bo‘lsa
va reflection/dynamic behavior ko‘p bo‘lsa
→ JVM yaxshiroq.</code></pre><hr><h3>20. Spring Native eski nomi haqida</h3><p>Oldin Spring Native degan alohida project bor edi. Spring Boot 3 va Spring Framework 6 bilan native support asosiy ekotizimga kirib keldi.</p><p>Bugungi yondashuv:</p><pre><code class="language-text">Spring Boot 3+
Spring Framework 6+
GraalVM Native Image
AOT processing
Runtime hints</code></pre><p>Ya’ni yangi projectlarda alohida eski <code>spring-native</code> projectiga tayanish kerak emas.</p><hr><h3>21. Production’da tekshirish checklist</h3><p>Native image’ni production’ga chiqarishdan oldin quyidagilar tekshiriladi:</p><pre><code class="language-text">Application native build bo‘ladimi?
Barcha endpointlar ishlaydimi?
JSON serialization/deserialization to‘g‘rimi?
Validation ishlaydimi?
Security filter chain ishlaydimi?
JPA queries ishlaydimi?
Migration tool ishlaydimi?
Kafka/Rabbit listener ishlaydimi?
Actuator health/metrics ishlaydimi?
SSL/certificate/resource fayllar topiladimi?
Container image size va startup o‘lchandimi?
Memory usage real load’da tekshirildimi?
Throughput JVM bilan solishtirildimi?</code></pre><p>Eng muhim qism:</p><blockquote><p>Native image qarori benchmark bilan qilinadi, taxmin bilan emas.</p></blockquote><hr><h3>22. Oddiy native-ready coding qoidalari</h3><h4>1. Runtime’da class nomidan object yaratishni kamaytirish</h4><p>Yomon:</p><pre><code class="language-java">Class&lt;?&gt; clazz = Class.forName(className);
Object instance = clazz.getDeclaredConstructor().newInstance();</code></pre><p>Yaxshi:</p><pre><code class="language-java">Map&lt;String, PaymentHandler&gt; handlers;</code></pre><hr><h4>2. Reflection ishlatsangiz hint bering</h4><pre><code class="language-java">@ImportRuntimeHints(PaymentRuntimeHints.class)
@Configuration
class PaymentNativeConfig {
}</code></pre><hr><h4>3. Resource fayllarni aniq ko‘rsating</h4><pre><code class="language-java">hints.resources().registerPattern(&quot;templates/*.html&quot;);</code></pre><hr><h4>4. Third-party library native support’ini tekshiring</h4><p>Agar SDK eski bo‘lsa, native image’da muammo ko‘p bo‘lishi mumkin.</p><hr><h4>5. CI’da native build qo‘shing</h4><p>Faqat local’da emas, CI’da ham native build ishlashi kerak:</p><pre><code class="language-text">pull request
   ↓
unit tests
   ↓
integration tests
   ↓
nativeCompile
   ↓
native smoke test</code></pre><hr><h3>23. Real misol: webhook service</h3><p>Webhook service:</p><pre><code class="language-text">request qabul qiladi
signature tekshiradi
eventni Kafka’ga yozadi
tez javob qaytaradi</code></pre><p>Bu native uchun yaxshi nomzod:</p><pre><code class="language-text">kichik service
startup muhim
memory kam
logic oddiy
reflection kam</code></pre><p>Architecture:</p><pre><code class="language-text">webhook-service native image
   ↓
Kubernetes’da tez start
   ↓
autoscaling tezroq
   ↓
cold start kamroq</code></pre><hr><h3>24. Real misol: katta ERP monolith</h3><p>ERP monolith:</p><pre><code class="language-text">500+ entity
Hibernate complex mapping
dynamic report engine
reflection-heavy library
ko‘p scheduled job
legacy SDK</code></pre><p>Bu native uchun xavfli nomzod.</p><p>Yaxshiroq qaror:</p><pre><code class="language-text">JVM’da qoldirish
startup optimizatsiya qilish
lazy initialization tekshirish
container memory tuning
JVM/CDS ko‘rib chiqish</code></pre><hr><h3>25. Eng ko‘p xatolar</h3><h4>Xato 1: Native image’ni silver bullet deb o‘ylash</h4><p>Native image hamma muammoni hal qilmaydi.</p><p>U asosan:</p><pre><code class="language-text">startup
memory
cold start</code></pre><p>muammolarini hal qiladi.</p><hr><h4>Xato 2: Benchmark qilmasdan qaror qilish</h4><p>To‘g‘ri test:</p><pre><code class="language-text">startup time
RSS memory
heap usage
CPU
p95/p99 latency
throughput
container image size
build time</code></pre><p>JVM va native ikkalasi bir xil load’da solishtiriladi.</p><hr><h4>Xato 3: Library compatibility tekshirmaslik</h4><p>Agar critical SDK native image’da ishlamasa, production risk katta.</p><hr><h4>Xato 4: Native build’ni CI’ga qo‘shmaslik</h4><p>Local’da ishlagan native build keyin dependency update’dan buzilishi mumkin.</p><hr><h4>Xato 5: Reflection hintlarni tartibsiz yozish</h4><p>Keraksiz ko‘p hint native image size va attack surface’ni oshirishi mumkin.</p><hr><h3>26. Arxitektor xulosasi</h3><p><strong>GraalVM Native &amp; AOT</strong> - Spring Boot application’ni tezroq start qiladigan, ko‘pincha kamroq memory ishlatadigan native executable ko‘rinishida chiqarish imkonini beradi.</p><p>Lekin arxitektor qarori bunday bo‘ladi:</p><pre><code class="language-text">Native image kerakmi?
Startup haqiqatan muhimmi?
Memory haqiqatan muhimmi?
Build pipeline bunga tayyormi?
Librarylar mosmi?
Team support qila oladimi?
JVM bilan benchmark qildikmi?</code></pre><p>Eng muhim qoida:</p><blockquote><p>GraalVM Native - performance strategiya emas, deployment va startup strategiya. Uni to‘g‘ri workload uchun ishlatish kerak.</p></blockquote><p>Qisqa tavsiya:</p><pre><code class="language-text">Serverless, CLI, short-lived job, small microservice → Native yaxshi.
Katta long-running, throughput-heavy, reflection-heavy service → JVM ko‘pincha yaxshi.</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Texnologik strategiya va boshqaruv (Tech Strategy & Governance)]]></title>
            <link>https://blog.jtscorp.uz/tech-strategy-governance</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/tech-strategy-governance</guid>
            <pubDate>Thu, 02 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Tech strategy &amp; governance</strong> - bu Arxitektor darajasidagi eng &quot;rahbarlikka yaqin&quot; mavzu hisoblanadi.  Bu yerda savol faqat:</p><pre><code class="language-text">Qaysi framework yaxshi?</code></pre><p>emas.</p><p>Asosiy savol:</p><pre><code class="language-text">Kompaniya 2-5 yil davomida Java/Spring ekotizimini qanday boshqaradi?
Qaysi texnologiya standart bo‘ladi?
Qaysi versiyalar support qilinadi?
Architecture qoidalarini kim va qanday nazorat qiladi?
API contractlar qanday boshqariladi?
Developerlar uchun platforma qanchalik qulay?</code></pre><p>Bu mavzu quyidagilarni o‘z ichiga oladi: <strong>Spring Boot vs Quarkus vs Micronaut tanlash</strong>, <strong>Spring release cadence &amp; EOL planning</strong>, <strong>ArchUnit - architecture-as-tests</strong>, <strong>OpenAPI-first contract governance</strong>, <strong>Developer Experience</strong>, va <strong>Inner-source starter contribution model</strong>.</p><hr><h3>1. Tech Strategy nima?</h3><p><strong>Tech strategy</strong> - kompaniya yoki katta team qaysi texnologiyalarni tanlashi, qaysilarini cheklashi, qaysilariga investitsiya qilishi haqida qarorlar to‘plami.</p><p>Masalan:</p><pre><code class="language-text">Backend framework: Spring Boot
Java version: 21 LTS
Build tool: Gradle yoki Maven
API contract: OpenAPI-first
Messaging: Kafka
Database: PostgreSQL
Cache: Redis
Observability: Micrometer + Prometheus + Grafana
Security: OAuth2/OIDC
Container: Kubernetes</code></pre><p>Bu qarorlar yozib qo‘yilmasa, har team o‘zicha tanlaydi:</p><pre><code class="language-text">Team A → Spring Boot
Team B → Quarkus
Team C → Micronaut
Team D → Node.js
Team E → eski Spring MVC</code></pre><p>Bunday erkinlik ba’zida foydali, lekin katta kompaniyada maintenance xarajatini oshiradi.</p><hr><h3>2. Governance nima?</h3><p><strong>Governance</strong> - tanlangan texnik standartlar haqiqatan ham bajarilishini ta’minlash.</p><p>Faqat hujjat yozish yetmaydi.</p><p>Yomon governance:</p><pre><code class="language-text">README’da &quot;Controller repository chaqirmasin&quot; deb yozilgan.
Lekin CI’da tekshiruv yo‘q.</code></pre><p>Yaxshi governance:</p><pre><code class="language-text">Architecture rule ArchUnit test bilan tekshiriladi.
OpenAPI spec CI’da lint qilinadi.
Dependency version BOM orqali boshqariladi.
Security baseline starter orqali majburiy ulanadi.</code></pre><p>Ya’ni governance:</p><pre><code class="language-text">standard + automation + review + feedback + measurement</code></pre><hr><h3>3. Spring Boot vs Quarkus vs Micronaut</h3><p>Arxitektor framework tanlaganda faqat benchmark bilan qaror qilmaydi. Quyidagilarni solishtiradi:</p><pre><code class="language-text">team tajribasi
ecosystem
documentation
library compatibility
cloud-native support
native image support
startup/memory
enterprise support
hiring market
long-term maintenance
integrationlar</code></pre><hr><h4>Spring Boot</h4><p>Spring Boot - Java enterprise ekotizimida eng keng ishlatiladigan variantlardan biri.</p><p>Kuchli tomonlari:</p><pre><code class="language-text">juda katta ecosystem
Spring Security, Data, Cloud, Batch, Integration
ko‘p developer biladi
documentation kuchli
enterprise support kuchli
legacy va modern systemlarga mos
production patternlar ko‘p</code></pre><p>Kamchiliklari:</p><pre><code class="language-text">startup/memory Quarkus/Micronaut’dan og‘irroq bo‘lishi mumkin
magic/auto-configuration ko‘p
noto‘g‘ri ishlatilsa monolith tez chalkashadi
native image uchun ba’zi dynamic librarylar muammo berishi mumkin</code></pre><p>Spring Boot uchun rasmiy support policy’da major versiyalar kamida 3 yil, minor versiyalar kamida 12 oy support qilinishi ko‘rsatilgan; bu EOL planning uchun muhim. (<a href="https://github.com/spring-projects/spring-boot/wiki/Supported-Versions?utm_source=chatgpt.com" title="Supported Versions &middot; spring-projects/spring-boot Wiki">GitHub</a>)</p><hr><h4>Quarkus</h4><p>Quarkus cloud-native va Kubernetes-native Java uchun optimallashtirilgan framework sifatida ko‘riladi.</p><p>Kuchli tomonlari:</p><pre><code class="language-text">fast startup
low memory footprint
GraalVM native image bilan yaxshi integratsiya
Kubernetes/cloud-native use case’lar uchun qulay
developer live reload tajribasi yaxshi</code></pre><p>Kamchiliklari:</p><pre><code class="language-text">Spring ekotizimidan chiqish xarajati bor
team learning curve kerak
hamma Spring library patternlari to‘g‘ridan-to‘g‘ri mos emas
enterprise’da Spring kabi universal adoption bo‘lmasligi mumkin</code></pre><hr><h4>Micronaut</h4><p>Micronaut ham cloud-native va reflection’ni kamaytirishga qaratilgan framework.</p><p>Kuchli tomonlari:</p><pre><code class="language-text">compile-time dependency injection
tez startup
kamroq reflection
serverless va microservice uchun mos
native image use case’lari yaxshi</code></pre><p>Kamchiliklari:</p><pre><code class="language-text">Spring’dan migration xarajati bor
ecosystem kichikroq
team tajribasi kerak
ba’zi enterprise integratsiyalar Spring’dagi kabi keng bo‘lmasligi mumkin</code></pre><hr><h3>4. Framework tanlash qaror jadvali</h3><table><tbody><tr><th rowspan="1" colspan="1"><p>Holat</p></th><th rowspan="1" colspan="1"><p>Tavsiya</p></th></tr><tr><td rowspan="1" colspan="1"><p>Katta enterprise, ko‘p Spring tajribasi bor</p></td><td rowspan="1" colspan="1"><p>Spring Boot</p></td></tr><tr><td rowspan="1" colspan="1"><p>Legacy Spring ecosystem mavjud</p></td><td rowspan="1" colspan="1"><p>Spring Boot</p></td></tr><tr><td rowspan="1" colspan="1"><p>Security/Data/Batch/Cloud integratsiya ko‘p</p></td><td rowspan="1" colspan="1"><p>Spring Boot</p></td></tr><tr><td rowspan="1" colspan="1"><p>Serverless/cold start juda muhim</p></td><td rowspan="1" colspan="1"><p>Quarkus yoki Micronaut ko‘rib chiqiladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Native image birinchi darajali talab</p></td><td rowspan="1" colspan="1"><p>Quarkus/Micronaut yoki Spring Native benchmark</p></td></tr><tr><td rowspan="1" colspan="1"><p>Team Spring’da kuchli, delivery tezligi muhim</p></td><td rowspan="1" colspan="1"><p>Spring Boot</p></td></tr><tr><td rowspan="1" colspan="1"><p>Yangi greenfield cloud-native platform</p></td><td rowspan="1" colspan="1"><p>Benchmark asosida 2-3 framework solishtiriladi</p></td></tr></tbody></table><p>Arxitektor qoidasi:</p><blockquote><p>Framework tanlash &quot;qaysi biri tezroq?&quot; emas, &quot;qaysi biri bizning team, product, infra va 3 yillik maintenance modelimizga mos?&quot; degan savol bilan qilinadi.</p></blockquote><hr><h3>5. Spring Release Cadence &amp; EOL Planning</h3><p>Spring ekotizimida release va support muddatlarini boshqarish juda muhim.</p><p>Agar kompaniyada 30 ta service bo‘lsa, ularning har biri turli Spring Boot versiyada qolib ketsa:</p><pre><code class="language-text">service-a → Spring Boot 2.7
service-b → Spring Boot 3.1
service-c → Spring Boot 3.3
service-d → Spring Boot 3.5</code></pre><p>Upgrade, CVE patch, compatibility va support muammoga aylanadi.</p><p>Spring Boot support policy’ga ko‘ra major versiyalar kamida 3 yil, minor versiyalar kamida 12 oy support qilinadi; demak Arxitektor har service qaysi supported line’da ekanini kuzatishi kerak. (<a href="https://github.com/spring-projects/spring-boot/wiki/Supported-Versions?utm_source=chatgpt.com" title="Supported Versions &middot; spring-projects/spring-boot Wiki">GitHub</a>)</p><hr><h3>EOL planning nima?</h3><p><strong>EOL - End of Life</strong>. Versiya public OSS supportdan chiqsa, security fix va bug fix olish qiyinlashadi.</p><p>Arxitektor quyidagi matrix yuritadi:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Service</p></th><th rowspan="1" colspan="1"><p>Java</p></th><th rowspan="1" colspan="1"><p>Spring Boot</p></th><th rowspan="1" colspan="1"><p>Spring Cloud</p></th><th rowspan="1" colspan="1"><p>Status</p></th></tr><tr><td rowspan="1" colspan="1"><p>order-service</p></td><td rowspan="1" colspan="1"><p>21</p></td><td rowspan="1" colspan="1"><p>3.5.x</p></td><td rowspan="1" colspan="1"><p>2025.x</p></td><td rowspan="1" colspan="1"><p>OK</p></td></tr><tr><td rowspan="1" colspan="1"><p>payment-service</p></td><td rowspan="1" colspan="1"><p>17</p></td><td rowspan="1" colspan="1"><p>3.3.x</p></td><td rowspan="1" colspan="1"><p>2023.x</p></td><td rowspan="1" colspan="1"><p>Upgrade kerak</p></td></tr><tr><td rowspan="1" colspan="1"><p>report-service</p></td><td rowspan="1" colspan="1"><p>11</p></td><td rowspan="1" colspan="1"><p>2.7.x</p></td><td rowspan="1" colspan="1"><p>eski</p></td><td rowspan="1" colspan="1"><p>High risk</p></td></tr><tr><td rowspan="1" colspan="1"><p>auth-service</p></td><td rowspan="1" colspan="1"><p>21</p></td><td rowspan="1" colspan="1"><p>3.5.x</p></td><td rowspan="1" colspan="1"><p>2025.x</p></td><td rowspan="1" colspan="1"><p>OK</p></td></tr></tbody></table><hr><h3>EOL governance qoidasi</h3><pre><code class="language-text">1. Har service inventory qilinadi.
2. Java/Spring/Cloud versiyalar yoziladi.
3. EOL sanalar kuzatiladi.
4. EOL’dan 6 oy oldin migration plan boshlanadi.
5. Critical CVE bo‘lsa emergency upgrade process ishlaydi.
6. Company BOM orqali versiyalar markazlashtiriladi.</code></pre><p>Bu platform standardization bilan bog‘liq.</p><hr><h3>6. Architecture-as-Tests</h3><p>Architecture faqat diagrammada qolmasligi kerak.</p><p>Yomon holat:</p><pre><code class="language-text">Diagrammada:
Controller → Service → Repository

Kodda:
Controller → Repository
Service → Controller
Order module → payment.internal.PaymentService</code></pre><p>Yaxshi holat:</p><pre><code class="language-text">Architecture rule CI’da test qilinadi.
Qoidani buzgan PR merge bo‘lmaydi.</code></pre><p>ArchUnit Java bytecode’ni analiz qilib package, class, layer, cyclic dependency kabi architecture qoidalarini test qilishga imkon beradi. (<a href="https://www.archunit.org/userguide/html/000_Index.html?utm_source=chatgpt.com" title="ArchUnit User Guide">ArchUnit</a>)</p><hr><h3>ArchUnit misollar</h3><h4>Controller repository chaqirmasin</h4><pre><code class="language-java">@AnalyzeClasses(packages = &quot;com.company&quot;)
class ArchitectureTest {

    @ArchTest
    static final ArchRule controllers_should_not_access_repositories =
            noClasses()
                    .that().resideInAPackage(&quot;..controller..&quot;)
                    .should().accessClassesThat()
                    .resideInAPackage(&quot;..repository..&quot;);
}</code></pre><hr><h4>Domain layer Spring’ga qaram bo‘lmasin</h4><pre><code class="language-java">@ArchTest
static final ArchRule domain_should_not_depend_on_spring =
        noClasses()
                .that().resideInAPackage(&quot;..domain..&quot;)
                .should().dependOnClassesThat()
                .resideInAnyPackage(&quot;org.springframework..&quot;);</code></pre><p>Bu Hexagonal/DDD uchun juda foydali.</p><hr><h4>Internal package tashqaridan ishlatilmasin</h4><pre><code class="language-java">@ArchTest
static final ArchRule internal_packages_should_not_be_accessed =
        noClasses()
                .that().resideOutsideOfPackage(&quot;..payment..&quot;)
                .should().accessClassesThat()
                .resideInAPackage(&quot;..payment.internal..&quot;);</code></pre><hr><h4>Cyclic dependency bo‘lmasin</h4><pre><code class="language-java">@ArchTest
static final ArchRule modules_should_be_free_of_cycles =
        slices()
                .matching(&quot;com.company.(*)..&quot;)
                .should()
                .beFreeOfCycles();</code></pre><hr><h3>7. Architecture governance qoidalari</h3><p>Arxitektor barcha qoidani birdan majburlamaydi. Bosqichma-bosqich qiladi:</p><pre><code class="language-text">Level 1: Package cycle yo‘q
Level 2: Controller → Service → Repository qoidasi
Level 3: Domain Spring’dan mustaqil
Level 4: Module internal boundary
Level 5: Hexagonal port/adapter dependency direction</code></pre><p>Yomon governance:</p><pre><code class="language-text">100 ta ArchUnit rule yozildi.
Team nima uchunligini tushunmadi.
Hamma testni disable qildi.</code></pre><p>Yaxshi governance:</p><pre><code class="language-text">Kam, lekin muhim rule.
Har rule sababi yozilgan.
Violation message tushunarli.
Exception process bor.</code></pre><hr><h3>8. OpenAPI-first Contract Governance</h3><p><strong>OpenAPI-first</strong> - avval API contract yoziladi, keyin implementation yoziladi.</p><p>OpenAPI Specification HTTP API’larni standart formatda tasvirlaydi, shunda insonlar va kompyuterlar API imkoniyatlarini source code yoki qo‘shimcha dokumentatsiyasiz tushunishi mumkin. (<a href="https://swagger.io/specification/?utm_source=chatgpt.com" title="OpenAPI Specification - Version 3.1.0">Swagger</a>) OpenAPI Initiative ham OpenAPI specification API’larni formal standartda tasvirlashini va client code, testlar hamda design standards uchun ishlatilishini ko‘rsatadi. (<a href="https://www.openapis.org/?utm_source=chatgpt.com" title="OpenAPI Initiative &ndash; The OpenAPI Initiative provides an open ...">OpenAPI Initiative</a>)</p><hr><h4>Code-first vs API-first</h4><h4>Code-first</h4><pre><code class="language-text">Controller yoziladi
Swagger avtomatik generate bo‘ladi
Keyin frontend ko‘radi</code></pre><p>Afzallik:</p><pre><code class="language-text">tez
kichik project uchun qulay</code></pre><p>Kamchilik:</p><pre><code class="language-text">API design tasodifiy chiqadi
breaking change sezilmaydi
consumerlar kech xabar topadi</code></pre><hr><h4>API-first</h4><pre><code class="language-text">OpenAPI spec yoziladi
Review qilinadi
Lint qilinadi
Mock server chiqadi
Client code generate bo‘ladi
Backend implementation specga mos yoziladi</code></pre><p>Afzallik:</p><pre><code class="language-text">contract oldindan kelishiladi
frontend/backend parallel ishlaydi
breaking change CI’da topiladi
API governance kuchli bo‘ladi</code></pre><hr><h3>9. OpenAPI governance da nima tekshiriladi?</h3><p>CI pipeline’da quyidagilar tekshiriladi:</p><pre><code class="language-text">endpoint naming convention
HTTP method to‘g‘ri ishlatilganmi
error response standartmi
pagination standartmi
auth scopes bor-mi
operationId unique-mi
request/response schema aniqmi
breaking change bormi
deprecated endpoint policy bormi</code></pre><p>Masalan naming convention:</p><pre><code class="language-text">GET /orders
POST /orders
GET /orders/{orderId}
PATCH /orders/{orderId}
DELETE /orders/{orderId}</code></pre><p>Yomon:</p><pre><code class="language-text">GET /getOrders
POST /createOrder
POST /deleteOrder</code></pre><hr><h4>Standard error response</h4><p>OpenAPI’da hamma service bir xil error format ishlatadi:</p><pre><code class="language-yaml">components:
  schemas:
    ApiError:
      type: object
      required:
        - type
        - message
        - traceId
      properties:
        type:
          type: string
        message:
          type: string
        traceId:
          type: string</code></pre><p>Bu frontend va integratsiyalar uchun juda muhim.</p><hr><h3>10. API compatibility governance</h3><p>Breaking change oldindan topilishi kerak.</p><p>Breaking change misollar:</p><pre><code class="language-text">field remove
field rename
required field qo‘shish
response type o‘zgartirish
HTTP status o‘zgartirish
enum value o‘chirish
endpoint o‘chirish
auth scope o‘zgartirish</code></pre><p>CI’da OpenAPI diff ishlatiladi:</p><pre><code class="language-text">old openapi.yml
        ↓ compare
new openapi.yml
        ↓
breaking change bormi?</code></pre><p>Agar breaking change bo‘lsa:</p><pre><code class="language-text">major version kerak
deprecation policy kerak
consumer approval kerak</code></pre><hr><h3>11. Developer Experience - DX</h3><p><strong>DX - Developer Experience</strong>. Platform va governance developerga yordam berishi kerak, to‘sqinlik emas.</p><p>Yomon DX:</p><pre><code class="language-text">starter magic qiladi, lekin dokumentatsiya yo‘q
CI xatosi tushunarsiz
project yaratish 3 kun
local environment og‘ir
dependency upgrade qo‘lda
API standard ko‘p, lekin sample yo‘q</code></pre><p>Yaxshi DX:</p><pre><code class="language-text">project template tayyor
starterlar dokumentatsiyali
local docker-compose bor
test utilities bor
sample service bor
clear error message bor
migration guide bor
CLI yoki portal bor</code></pre><hr><h3>Platform team product sifatida ishlashi kerak</h3><p>Platform team developerlarni &quot;foydalanuvchi&quot; deb ko‘rishi kerak.</p><p>Ya’ni platform:</p><pre><code class="language-text">developerga tezroq service yaratishga yordam beradi
security va observabilityni default qiladi
boilerplate kamaytiradi
upgrade’ni osonlashtiradi</code></pre><p>Agar governance faqat taqiq bo‘lsa, teamlar workaround qiladi.</p><hr><h3>12. Developer portal</h3><p>Katta kompaniyada quyidagi developer portali bo&#039;lishi foydali:</p><pre><code class="language-text">service catalog
owner information
runbook
API documentation
dependency version
SLO/SLA
deployment status
logs/metrics links
on-call info</code></pre><p>Masalan service catalog:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Service</p></th><th rowspan="1" colspan="1"><p>Owner</p></th><th rowspan="1" colspan="1"><p>Spring Boot</p></th><th rowspan="1" colspan="1"><p>API</p></th><th rowspan="1" colspan="1"><p>Dashboard</p></th></tr><tr><td rowspan="1" colspan="1"><p>order-service</p></td><td rowspan="1" colspan="1"><p>Order Team</p></td><td rowspan="1" colspan="1"><p>3.5.x</p></td><td rowspan="1" colspan="1"><p>OpenAPI</p></td><td rowspan="1" colspan="1"><p>Grafana</p></td></tr><tr><td rowspan="1" colspan="1"><p>payment-service</p></td><td rowspan="1" colspan="1"><p>Payment Team</p></td><td rowspan="1" colspan="1"><p>3.5.x</p></td><td rowspan="1" colspan="1"><p>OpenAPI</p></td><td rowspan="1" colspan="1"><p>Grafana</p></td></tr><tr><td rowspan="1" colspan="1"><p>catalog-service</p></td><td rowspan="1" colspan="1"><p>Catalog Team</p></td><td rowspan="1" colspan="1"><p>3.4.x</p></td><td rowspan="1" colspan="1"><p>OpenAPI</p></td><td rowspan="1" colspan="1"><p>Grafana</p></td></tr></tbody></table><p>Bu incident va governance uchun juda foydali hisoblanadi.</p><hr><h3>13. Inner-source starter contribution model</h3><p>Oldingi mavzuda <strong>company starterlar</strong> haqida gapirdik:</p><pre><code class="language-text">company-security-starter
company-observability-starter
company-web-starter
company-error-starter
company-bom</code></pre><p><strong>Inner-source</strong> degani - kompaniya ichidagi platform library’lar open-source kabi yuritiladi:</p><pre><code class="language-text">public internal repository
README
issues
pull requests
code owners
release notes
versioning
contribution guide</code></pre><hr><h3>Nega kerak?</h3><p>Agar starterlarni faqat platform team o‘zgartira olsa:</p><pre><code class="language-text">product teamlar kutib qoladi
platform bottleneck bo‘ladi
real ehtiyojlar sekin qo‘shiladi</code></pre><p>Inner-source modelda:</p><pre><code class="language-text">product team PR ochadi
platform team review qiladi
standartga mos bo‘lsa merge qiladi
release qilinadi
hamma team foydalanadi</code></pre><hr><h3>Inner-source repository structure</h3><pre><code class="language-text">company-platform
 ├── company-bom
 ├── company-web-starter
 ├── company-security-starter
 ├── company-observability-starter
 ├── company-test-starter
 ├── docs
 │    ├── getting-started.md
 │    ├── migration-guide.md
 │    └── contribution-guide.md
 └── examples
      ├── simple-rest-service
      ├── kafka-consumer-service
      └── modulith-service</code></pre><hr><h3>14. Starter contribution qoidalari</h3><p>Har PR uchun tekshiruv:</p><pre><code class="language-text">Backward compatible-mi?
Default behavior buzilmaydimi?
Override qilish mumkinmi?
Documentation bor-mi?
Test bor-mi?
Migration note bor-mi?
Security impact bormi?
Observability impact bormi?</code></pre><p>Yomon starter change:</p><pre><code class="language-text">company-security-starter yangi versiyada hamma /actuator endpointlarni yopdi
teamlar production’da health check fail oldi</code></pre><p>Yaxshi process:</p><pre><code class="language-text">breaking change bo‘lsa major version
release notes
migration guide
sample update
pilot service’da test</code></pre><hr><h3>15. Technology Radar</h3><p>Arxitektor organization uchun <strong>Technology Radar</strong> yuritishi mumkin.</p><p>Kategoriyalar:</p><pre><code class="language-text">Adopt
Trial
Assess
Hold</code></pre><p>Misol:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Technology</p></th><th rowspan="1" colspan="1"><p>Status</p></th><th rowspan="1" colspan="1"><p>Izoh</p></th></tr><tr><td rowspan="1" colspan="1"><p>Spring Boot 3.5</p></td><td rowspan="1" colspan="1"><p>Adopt</p></td><td rowspan="1" colspan="1"><p>Default backend framework</p></td></tr><tr><td rowspan="1" colspan="1"><p>Java 21</p></td><td rowspan="1" colspan="1"><p>Adopt</p></td><td rowspan="1" colspan="1"><p>New services uchun standard</p></td></tr><tr><td rowspan="1" colspan="1"><p>Spring Modulith</p></td><td rowspan="1" colspan="1"><p>Trial</p></td><td rowspan="1" colspan="1"><p>Katta modular monolithlar uchun</p></td></tr><tr><td rowspan="1" colspan="1"><p>Quarkus</p></td><td rowspan="1" colspan="1"><p>Assess</p></td><td rowspan="1" colspan="1"><p>Serverless/cold-start use case</p></td></tr><tr><td rowspan="1" colspan="1"><p>GraphQL</p></td><td rowspan="1" colspan="1"><p>Assess</p></td><td rowspan="1" colspan="1"><p>Maxsus use case’lar uchun</p></td></tr><tr><td rowspan="1" colspan="1"><p>Java 11</p></td><td rowspan="1" colspan="1"><p>Hold</p></td><td rowspan="1" colspan="1"><p>Yangi service’da ishlatilmaydi</p></td></tr></tbody></table><p>Bu teamlarga aniq signal beradi.</p><hr><h3>16. ADR - Architecture Decision Records</h3><p>Tech governance’da har muhim qaror yozib boriladi.</p><p>ADR structure:</p><pre><code class="language-text">Title
Status
Context
Decision
Consequences
Alternatives
Date
Owner</code></pre><p>Misol uchun:</p><pre><code class="language-text">ADR-004: Use Spring Boot as default backend framework

Status: Accepted

Context:
Company’da Java team kuchli, mavjud service’lar Spring’da,
security/data/cloud integratsiyalar ko‘p.

Decision:
New backend services default Spring Boot 3.5 + Java 21 bilan yaratiladi.

Consequences:
Spring ecosystem bo‘yicha platform starterlar yuritiladi.
Quarkus/Micronaut faqat maxsus cold-start use case’da approval bilan.</code></pre><p>ADR foydasi:</p><pre><code class="language-text">qaror sababi yo‘qolmaydi
yangi developerlar tushunadi
keyin qarorni qayta ko‘rib chiqish oson</code></pre><hr><h3>17. Governance review board</h3><p>Katta organization’da architecture review board bo‘lishi mumkin, lekin ehtiyot bo‘lish kerak.</p><p>Yomon review board:</p><pre><code class="language-text">hamma qarorni sekinlashtiradi
approval by bureaucracy
teamlar kutib qoladi</code></pre><p>Yaxshi review board:</p><pre><code class="language-text">high-risk qarorlarni ko‘radi
standartlarni yangilaydi
exceptionlarni boshqaradi
teamlarga maslahat beradi
platformni yaxshilaydi</code></pre><p>Review kerak bo‘ladigan holatlar:</p><pre><code class="language-text">yangi framework tanlash
yangi database tanlash
public API breaking change
security baseline’dan chetga chiqish
multi-region architecture
critical data migration</code></pre><hr><h3>18. Exception process</h3><p>Har doim ham standard 100% mos kelmaydi.</p><p>Masalan:</p><pre><code class="language-text">default Spring Boot, lekin serverless function uchun Quarkus kerak
default PostgreSQL, lekin time-series data uchun TimescaleDB kerak
default REST, lekin streaming uchun gRPC kerak</code></pre><p>Exception process:</p><pre><code class="language-text">1. Team sabab yozadi
2. Risk va alternativalar ko‘rsatiladi
3. Arxitektor review qiladi
4. Muddat yoki scope belgilanadi
5. ADR yoziladi
6. Ownership aniq bo‘ladi</code></pre><p>Bu innovation’ni o‘ldirmaydi, lekin tartibsizlikni kamaytiradi.</p><hr><h3>19. Metrics for governance</h3><p>Governance o‘lchanishi kerak.</p><p>Quyidagi metrikalar foydali:</p><pre><code class="language-text">nechta service supported Spring Boot versiyada
nechta service EOL riskda
critical CVE count
mean time to patch
starter adoption rate
OpenAPI lint pass rate
architecture test failure trend
new service creation time
deployment frequency
rollback frequency
incident count by category</code></pre><p>Masalan:</p><pre><code class="language-text">EOL riskdagi service’lar: 12 → 3
Yangi service yaratish vaqti: 2 kun → 30 daqiqa
Critical CVE patch time: 14 kun → 2 kun</code></pre><p>Bu platform strategy foyda berayotganini ko‘rsatadi.</p><hr><h3>20. Tech debt governance</h3><p>Arxitektor tech debt’ni ham boshqaradi.</p><p>Tech debt turlari:</p><pre><code class="language-text">old Spring Boot version
unsupported Java version
manual security config
missing tests
no OpenAPI spec
no observability
shared database coupling
cyclic package dependencies
legacy authentication</code></pre><p>Tech debt backlog:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Debt</p></th><th rowspan="1" colspan="1"><p>Risk</p></th><th rowspan="1" colspan="1"><p>Owner</p></th><th rowspan="1" colspan="1"><p>Deadline</p></th></tr><tr><td rowspan="1" colspan="1"><p>payment-service Boot 2.7</p></td><td rowspan="1" colspan="1"><p>High</p></td><td rowspan="1" colspan="1"><p>Payment Team</p></td><td rowspan="1" colspan="1"><p>Q3</p></td></tr><tr><td rowspan="1" colspan="1"><p>no OpenAPI for billing</p></td><td rowspan="1" colspan="1"><p>Medium</p></td><td rowspan="1" colspan="1"><p>Billing Team</p></td><td rowspan="1" colspan="1"><p>Q2</p></td></tr><tr><td rowspan="1" colspan="1"><p>shared common-lib too large</p></td><td rowspan="1" colspan="1"><p>High</p></td><td rowspan="1" colspan="1"><p>Platform Team</p></td><td rowspan="1" colspan="1"><p>Q4</p></td></tr></tbody></table><hr><h3>21. Security governance</h3><p>Tech strategy’da security baseline bo‘lishi shart:</p><pre><code class="language-text">OAuth2/OIDC standard
JWT validation rules
mTLS kerak joylar
secret management
dependency scanning
container scanning
SAST/DAST
least privilege
audit logging
security headers</code></pre><p>Security governance starter orqali ham qo‘llanadi:</p><pre><code class="language-text">company-security-starter
default SecurityFilterChain
standard CORS
standard headers
actuator protection
scope mapping</code></pre><hr><h3>22. Data governance</h3><p>Backend architecture’da data governance ham muhim:</p><pre><code class="language-text">PII qayerda saqlanadi?
audit log qayerda?
retention policy qanday?
backup/restore SLA qanday?
data residency bormi?
schema ownership kimda?
shared database mumkinmi?</code></pre><p>Microservice’da ayniqsa:</p><pre><code class="language-text">service owns its database
boshqa service DB’ga to‘g‘ridan-to‘g‘ri kirmaydi
data sharing event/API orqali bo‘ladi</code></pre><hr><h3>23. Platform maturity model</h3><p>Platform va governance birdan mukammal bo‘lmaydi.</p><h4>Level 1 - Ad hoc</h4><pre><code class="language-text">har team o‘zicha
standard yo‘q
dependencylar chalkash</code></pre><h4>Level 2 - Documented</h4><pre><code class="language-text">standartlar bor
lekin avtomatlashtirilmagan</code></pre><h4>Level 3 - Automated</h4><pre><code class="language-text">starterlar
BOM
CI checks
ArchUnit
OpenAPI lint</code></pre><h4>Level 4 - Self-service</h4><pre><code class="language-text">developer portal
service template
automatic provisioning
dashboardlar</code></pre><h4>Level 5 - Optimized</h4><pre><code class="language-text">metrics-driven governance
automatic dependency PR
policy-as-code
platform product management</code></pre><p>Arxitektor maqsadi - organization’ni bosqichma-bosqich yuqoriga olib chiqishdir.</p><hr><h3>24. Real organization strategy misoli</h3><p>Aytaylik kompaniyada 25 ta Spring service bor.</p><h4>Muammo</h4><pre><code class="language-text">7 ta service Spring Boot 2.7
10 ta service Spring Boot 3.2
8 ta service Spring Boot 3.5
security config har xil
OpenAPI spec yo‘q
log format har xil
dependency CVE ko‘p</code></pre><h4>Strategy</h4><pre><code class="language-text">1. Service inventory qilinadi
2. Java 21 + Spring Boot 3.5 target belgilanadi
3. Company BOM yaratiladi
4. Security/observability starter majburiy bo‘ladi
5. OpenAPI-first yangi API’lar uchun standard bo‘ladi
6. ArchUnit baseline testlar qo‘shiladi
7. EOL riskdagi service’lar migration plan oladi
8. Developer portalda service catalog yuritiladi</code></pre><h4>Natija</h4><pre><code class="language-text">upgrade boshqariladi
CVE patch tezlashadi
service yaratish osonlashadi
API design bir xil bo‘ladi
architecture erosion kamayadi</code></pre><hr><h3>25. Eng ko‘p xatolar</h3><h4>Xato 1: Governance’ni faqat taqiq deb tushunish</h4><p>Yomon:</p><pre><code class="language-text">Buni ishlatma.
Buni tanlama.
Bu mumkin emas.</code></pre><p>Yaxshi:</p><pre><code class="language-text">Mana standard.
Mana starter.
Mana template.
Mana exception process.
Mana sample.</code></pre><hr><h4>Xato 2: Framework tanlashni benchmark bilan tugatish</h4><p>Benchmark muhim, lekin yetarli emas.</p><p>Kerakli savollar:</p><pre><code class="language-text">Team buni biladimi?
Hiring osonmi?
Security integration bormi?
Monitoring bormi?
3 yildan keyin kim support qiladi?</code></pre><hr><h4>Xato 3: EOL planning yo‘q</h4><p>Service ishlayapti, lekin unsupported versiyada qolib ketadi.</p><hr><h4>Xato 4: Architecture rule faqat diagrammada</h4><p>Diagramma chiroyli, kod esa boshqa. ArchUnit yoki shunga o‘xshash testlar kerak.</p><hr><h4>Xato 5: API contract governance yo‘q</h4><p>Har service har xil response, error, pagination, auth style ishlatadi.</p><hr><h4>Xato 6: Platform team bottleneck bo‘lib qoladi</h4><p>Inner-source model bo‘lmasa, product teamlar platformni aylanib o‘tadi.</p><hr><h3>26. Architect checklist</h3><p>Tech strategy &amp; governance uchun savollar:</p><pre><code class="language-text">Default backend framework nima?
Exception process bormi?
Java/Spring version matrix bormi?
EOL sanalar kuzatiladimi?
Company BOM bormi?
Security starter bormi?
Observability starter bormi?
Architecture tests bormi?
OpenAPI-first qayerda majburiy?
Breaking change qanday aniqlanadi?
Developer portal yoki service catalog bormi?
Platform starterlarga product team PR bera oladimi?
ADR yoziladimi?
Tech debt kim kuzatadi?</code></pre><hr><h3>27. Qisqa qoida</h3><pre><code class="language-text">Tech strategy - nima tanlashni belgilaydi.
Governance - tanlangan narsalar bajarilishini ta’minlaydi.
Platform - developerga buni oson qiladi.
Automation - qoidani real qiladi.
DX - teamlar buni qabul qilishini ta’minlaydi.</code></pre><hr><h3>28. Arxitektor xulosasi</h3><p><strong>Tech strategy &amp; governance</strong> - Arxitektor’ning eng muhim mas’uliyatlaridan biri. Bu mavzu kod yozishdan ham kengroqdur:</p><pre><code class="language-text">framework tanlash
release/EOL rejalash
dependency boshqarish
security baseline
API contract standard
architecture-as-tests
developer experience
inner-source platform model</code></pre><p>Eng muhim qoida:</p><blockquote><p>Governance developerlarni sekinlashtirish uchun emas, katta systemni xavfsiz, izchil va tez rivojlantirish uchun kerak.</p></blockquote><p>Yaxshi tech governance natijasi quyidagicha:</p><pre><code class="language-text">yangi service tez yaratiladi
dependencylar boshqariladi
security default bo‘ladi
API’lar bir xil chiqadi
architecture buzilishi CI’da ushlanadi
upgrade va CVE patchlar rejalangan bo‘ladi
developerlar platformdan qochmaydi, foydalanadi</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Spring internals]]></title>
            <link>https://blog.jtscorp.uz/spring-internals</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/spring-internals</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p>Spring internals quyidagilarni qamrab oladi: <strong>Bean lifecycle</strong>, <strong>BeanPostProcessor</strong>, <strong>BeanFactoryPostProcessor</strong>, <strong>@Conditional</strong>, <strong>auto-configuration</strong>, <strong>@Import</strong>, va <strong>bean scope</strong>lar.</p><h3>1. Spring Internals nima?</h3><p><strong>Spring Internals</strong> - bu Spring ichkarida qanday ishlashini tushunish.</p><p>Boshlang&#039;ich darajada biz odatda shuni bilamiz:</p><pre><code class="language-java">@Service
public class UserService {
}</code></pre><p>Spring buni avtomatik topadi, obyekt yaratadi va kerakli joyga inject qiladi.</p><p>O&#039;rta darajada savol boshqacha bo‘ladi:</p><blockquote><p>Spring bu classni qayerdan topdi?<br>Qachon obyekt yaratdi?<br>Qachon dependency inject qildi?<br>Qachon proxy yaratdi?<br>Qachon <code>@Transactional</code>, <code>@Async</code>, <code>@Cacheable</code> ishlay boshladi?</p></blockquote><p>Mana shu ichki mexanizmlar <strong>Spring Internals</strong> deyiladi.</p><hr><h3>2. Eng asosiy tushuncha: IoC Container</h3><p>Springning yuragi - <strong>IoC Container</strong>.</p><p>IoC - <strong>Inversion of Control</strong>.</p><p>Oddiy Java’da obyektni o‘zingiz yaratamiz:</p><pre><code class="language-java">UserRepository repository = new UserRepository();
UserService service = new UserService(repository);</code></pre><p>Spring’da esa obyektlarni Spring yaratadi:</p><pre><code class="language-java">@Service
public class UserService {

    private final UserRepository repository;

    public UserService(UserRepository repository) {
        this.repository = repository;
    }
}</code></pre><p>Bu yerda biz <code>new UserRepository()</code> demadingiz. Spring o‘zi yaratdi va constructor orqali berdi.</p><p>Spring containerning vazifalari:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Vazifa</p></th><th rowspan="1" colspan="1"><p>Ma’nosi</p></th></tr><tr><td rowspan="1" colspan="1"><p>Classlarni topish</p></td><td rowspan="1" colspan="1"><p><code>@Component</code>, <code>@Service</code>, <code>@Repository</code>larni scan qiladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Bean yaratish</p></td><td rowspan="1" colspan="1"><p>Obyektlarni o‘zi yaratadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Dependency injection</p></td><td rowspan="1" colspan="1"><p>Kerakli dependencylarni ulaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Lifecycle boshqarish</p></td><td rowspan="1" colspan="1"><p>Init/destroy metodlarini ishlatadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Proxy yaratish</p></td><td rowspan="1" colspan="1"><p><code>@Transactional</code>, AOP, Security kabi mexanizmlar uchun</p></td></tr></tbody></table><hr><h3>3. Bean nima?</h3><p><strong>Bean</strong> - Spring container boshqarayotgan obyekt.</p><p>Masalan:</p><pre><code class="language-java">@Service
public class PaymentService {
}</code></pre><p>Bu oddiy Java class emas. Spring uni ko‘rib:</p><blockquote><p>&quot;Bu classni men boshqaraman&quot;</p></blockquote><p>deydi va undan bean yaratadi.</p><p>Yana bir misol:</p><pre><code class="language-java">@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}</code></pre><p>Bu yerda <code>RestTemplate</code> ham Spring bean bo‘ladi.</p><hr><h3>4. Bean lifecycle nima?</h3><p><strong>Bean lifecycle</strong> - Spring bean yaratilganidan to yo‘q qilinishigacha bo‘lgan jarayon.</p><p>Soddalashtirilgan tartib:</p><pre><code class="language-text">1. Class topiladi
2. BeanDefinition yaratiladi
3. Bean obyekti yaratiladi
4. Dependency injection bo‘ladi
5. Aware interface’lar chaqiriladi
6. BeanPostProcessor oldingi bosqichi ishlaydi
7. Init method ishlaydi
8. BeanPostProcessor keyingi bosqichi ishlaydi
9. Bean tayyor bo‘ladi
10. App yopilganda destroy method ishlaydi</code></pre><p>Keling, buni tushunarliroq qilamiz.</p><hr><h3>5. BeanDefinition nima?</h3><p>Spring avval beanni darrov yaratmaydi. Avval uning <strong>rejasini</strong> tuzadi.</p><p>Bu reja <strong>BeanDefinition</strong> deyiladi.</p><p>BeanDefinition ichida shunday ma’lumotlar bo‘ladi:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Ma’lumot</p></th><th rowspan="1" colspan="1"><p>Misol</p></th></tr><tr><td rowspan="1" colspan="1"><p>Class nomi</p></td><td rowspan="1" colspan="1"><p><code>UserService.class</code></p></td></tr><tr><td rowspan="1" colspan="1"><p>Scope</p></td><td rowspan="1" colspan="1"><p><code>singleton</code></p></td></tr><tr><td rowspan="1" colspan="1"><p>Lazy yoki eager</p></td><td rowspan="1" colspan="1"><p>darrov yaratiladimi yoki keyinmi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Constructor argumentlar</p></td><td rowspan="1" colspan="1"><p>qaysi dependencylar kerak</p></td></tr><tr><td rowspan="1" colspan="1"><p>Init method</p></td><td rowspan="1" colspan="1"><p>qaysi metod ishga tushadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Destroy method</p></td><td rowspan="1" colspan="1"><p>qaysi metod yopilishda ishlaydi</p></td></tr></tbody></table><p>Misol:</p><pre><code class="language-java">@Service
public class UserService {
}</code></pre><p>Spring buni scan qiladi va ichkarida taxminan shunday reja tuzadi:</p><pre><code class="language-text">beanName = &quot;userService&quot;
beanClass = UserService
scope = singleton
lazyInit = false</code></pre><hr><h3>6. Bean lifecycle oddiy misol</h3><pre><code class="language-java">@Component
public class ReportService {

    public ReportService() {
        System.out.println(&quot;1. Constructor ishladi&quot;);
    }

    @PostConstruct
    public void init() {
        System.out.println(&quot;2. Init ishladi&quot;);
    }

    @PreDestroy
    public void destroy() {
        System.out.println(&quot;3. Destroy ishladi&quot;);
    }
}</code></pre><p>Natija:</p><pre><code class="language-text">1. Constructor ishladi
2. Init ishladi</code></pre><p>Application yopilganda:</p><pre><code class="language-text">3. Destroy ishladi</code></pre><p>Bu nimani anglatadi?</p><p><code>constructor</code> - obyekt yaratilayotgan payt.</p><p><code>@PostConstruct</code> - dependencylar inject qilingandan keyin.</p><p><code>@PreDestroy</code> - Spring container yopilayotganda.</p><hr><h3>7. Constructor va @PostConstruct farqi</h3><p>Ko‘pchilik shu joyda adashadi.</p><h4>Constructor</h4><pre><code class="language-java">public ReportService(UserRepository repository) {
    this.repository = repository;
}</code></pre><p>Constructor asosan:</p><ul><li><p>majburiy dependencylarni olish uchun;</p></li><li><p>obyektni valid holatga keltirish uchun;</p></li><li><p>oddiy fieldlarni tayyorlash uchun.</p></li></ul><h4>@PostConstruct</h4><pre><code class="language-java">@PostConstruct
public void init() {
    // bean to‘liq tayyor bo‘lgandan keyingi ish
}</code></pre><p><code>@PostConstruct</code>:</p><ul><li><p>dependency injection tugagandan keyin ishlaydi;</p></li><li><p>Spring beanni yaratib bo‘lgandan keyin chaqiriladi;</p></li><li><p>og‘ir ishlar uchun emas.</p></li></ul><p>Yomon amaliyot:</p><pre><code class="language-java">@PostConstruct
public void init() {
    // DB dan 1 million data o‘qish
    // tashqi API ga request yuborish
    // katta cache warmup qilish
}</code></pre><p>Bu startupni sekinlashtiradi.</p><p>Yaxshi amaliyot:</p><pre><code class="language-java">@PostConstruct
public void validateConfig() {
    if (apiKey == null || apiKey.isBlank()) {
        throw new IllegalStateException(&quot;API key required&quot;);
    }
}</code></pre><hr><h3>8. BeanPostProcessor nima?</h3><p><strong>BeanPostProcessor</strong> - bean yaratilgandan keyin unga aralashish imkonini beradigan mexanizm.</p><p>Spring ichida juda ko‘p narsalar aynan shu orqali ishlaydi.</p><p>Masalan:</p><ul><li><p><code>@Autowired</code></p></li><li><p><code>@PostConstruct</code></p></li><li><p>AOP proxy</p></li><li><p><code>@Async</code></p></li><li><p><code>@Transactional</code></p></li><li><p><code>@Cacheable</code></p></li></ul><p>Soddalashtirilgan ko‘rinish:</p><pre><code class="language-java">@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(
            Object bean,
            String beanName
    ) {
        System.out.println(&quot;Before init: &quot; + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(
            Object bean,
            String beanName
    ) {
        System.out.println(&quot;After init: &quot; + beanName);
        return bean;
    }
}</code></pre><p>Bu har bir bean uchun ishlaydi.</p><hr><h3>9. BeanPostProcessor qayerda ishlatiladi?</h3><p>Masalan, bizda shunday service bor:</p><pre><code class="language-java">@Service
public class OrderService {

    @Transactional
    public void createOrder() {
        // order yaratish
    }
}</code></pre><p>Spring <code>OrderService</code>ni oddiy obyekt sifatida ishlatib qo‘ymaydi. Unga proxy o‘raydi.</p><p>Taxminan:</p><pre><code class="language-text">OrderService original bean
        ↓
Transactional proxy
        ↓
Application ishlatadigan bean</code></pre><p>Shuning uchun <code>@Transactional</code> ishlaydi.</p><p>Ya’ni biz chaqiramiz:</p><pre><code class="language-java">orderService.createOrder();</code></pre><p>Lekin real chaqiruv:</p><pre><code class="language-text">Proxy → transaction start → method → transaction commit/rollback</code></pre><p>Mana shu proxy yaratish jarayonlarida <code>BeanPostProcessor</code> muhim rol o‘ynaydi.</p><hr><h3>10. BeanFactoryPostProcessor nima?</h3><p><strong>BeanPostProcessor</strong> bean obyektiga ta’sir qiladi.</p><p><strong>BeanFactoryPostProcessor</strong> esa bean yaratilishidan oldin, <code>BeanDefinition</code>larga ta’sir qiladi.</p><p>Farqi:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Mexanizm</p></th><th rowspan="1" colspan="1"><p>Qachon ishlaydi?</p></th><th rowspan="1" colspan="1"><p>Nimaga ta’sir qiladi?</p></th></tr><tr><td rowspan="1" colspan="1"><p>BeanFactoryPostProcessor</p></td><td rowspan="1" colspan="1"><p>Bean yaratilishidan oldin</p></td><td rowspan="1" colspan="1"><p>BeanDefinition</p></td></tr><tr><td rowspan="1" colspan="1"><p>BeanPostProcessor</p></td><td rowspan="1" colspan="1"><p>Bean yaratilgandan keyin</p></td><td rowspan="1" colspan="1"><p>Bean obyekti</p></td></tr></tbody></table><p>Misol:</p><pre><code class="language-java">@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(
            ConfigurableListableBeanFactory beanFactory
    ) {
        System.out.println(&quot;Bean definitions count: &quot;
                + beanFactory.getBeanDefinitionCount());
    }
}</code></pre><p>Bu kod beanlar yaratilishidan oldin ishlaydi.</p><hr><h3>11. @Conditional nima?</h3><p><code>@Conditional</code> - ma’lum shart bajarilsagina bean yaratish mexanizmi.</p><p>Masalan:</p><pre><code class="language-java">@Bean
@ConditionalOnProperty(
        name = &quot;payment.provider&quot;,
        havingValue = &quot;stripe&quot;
)
public PaymentService stripePaymentService() {
    return new StripePaymentService();
}</code></pre><p>Agar configda shunday bo‘lsa:</p><pre><code class="language-yaml">payment:
  provider: stripe</code></pre><p>Spring <code>StripePaymentService</code> beanini yaratadi.</p><p>Agar:</p><pre><code class="language-yaml">payment:
  provider: click</code></pre><p>bo‘lsa, bu bean yaratilmaydi.</p><hr><h3>12. Spring Boot auto-configuration nima?</h3><p>Spring Bootning eng kuchli joyi - <strong>auto-configuration</strong>.</p><p>Biz dependency qo‘shamiz:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>Spring Boot shuni ko‘radi va o‘zi taxmin qiladi:</p><blockquote><p>&quot;Demak bu project JPA ishlatyapti. Men EntityManager, DataSource, TransactionManager kerak bo‘lishini bilaman.&quot;</p></blockquote><p>Keyin kerakli beanlarni avtomatik yaratadi.</p><p>Lekin bu sehr emas. Ichkarida shartlar bor:</p><pre><code class="language-java">@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)</code></pre><p>Ma’nosi:</p><pre><code class="language-text">Agar classpathda DataSource bo‘lsa
va user o‘zi DataSource bean yaratmagan bo‘lsa,
Spring Boot default DataSource yaratadi.</code></pre><hr><h3>13. @ConditionalOnMissingBean juda muhim</h3><p>Bu Spring Boot auto-configurationning asosiy g‘oyasi.</p><p>Masalan Spring Boot default <code>ObjectMapper</code> yaratadi.</p><p>Lekin biz o‘zinmiz bersak:</p><pre><code class="language-java">@Bean
public ObjectMapper objectMapper() {
    return new ObjectMapper()
            .findAndRegisterModules();
}</code></pre><p>Spring Boot aytadi:</p><blockquote><p>&quot;User o‘zi ObjectMapper berdi. Men defaultini yaratmayman.&quot;</p></blockquote><p>Shu sabab Spring Boot moslashuvchan.</p><hr><h3>14. Auto-configuration eski va yangi mexanizmi</h3><p>Oldin Spring Boot auto-config classlar odatda bu faylda ko‘rsatilardi:</p><pre><code class="language-text">META-INF/spring.factories</code></pre><p>Yangi Spring Boot versiyalarida esa:</p><pre><code class="language-text">META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports</code></pre><p>O&#039;rta darajada buni bilish muhim, chunki custom starter yozganda kerak bo‘ladi.</p><hr><h3>15. @Import nima?</h3><p><code>@Import</code> - boshqa configuration classni Spring contextga qo‘shish.</p><p>Misol:</p><pre><code class="language-java">@Configuration
public class SecurityConfig {
}</code></pre><p>Buni boshqa joyda import qilish:</p><pre><code class="language-java">@Configuration
@Import(SecurityConfig.class)
public class AppConfig {
}</code></pre><p>Endi <code>SecurityConfig</code> ichidagi beanlar ham contextga qo‘shiladi.</p><p><code>@Import</code> ko‘pincha:</p><ul><li><p>custom configuration ulashda;</p></li><li><p>starter yozishda;</p></li><li><p>framework ichki mexanizmlarida;</p></li><li><p>modular configlarda ishlatiladi.</p></li></ul><hr><h3>16. Bean scope nima?</h3><p><strong>Scope</strong> - bean qancha vaqt yashashini belgilaydi.</p><p>Eng ko‘p ishlatiladiganlari:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Scope</p></th><th rowspan="1" colspan="1"><p>Ma’nosi</p></th></tr><tr><td rowspan="1" colspan="1"><p>singleton</p></td><td rowspan="1" colspan="1"><p>bitta bean, butun app bo‘yicha umumiy</p></td></tr><tr><td rowspan="1" colspan="1"><p>prototype</p></td><td rowspan="1" colspan="1"><p>har safar yangi obyekt</p></td></tr><tr><td rowspan="1" colspan="1"><p>request</p></td><td rowspan="1" colspan="1"><p>har HTTP request uchun alohida</p></td></tr><tr><td rowspan="1" colspan="1"><p>session</p></td><td rowspan="1" colspan="1"><p>har user session uchun alohida</p></td></tr><tr><td rowspan="1" colspan="1"><p>application</p></td><td rowspan="1" colspan="1"><p>servlet context bo‘yicha bitta</p></td></tr></tbody></table><p>Default scope - <code>singleton</code>.</p><hr><h3>17. Singleton scope</h3><pre><code class="language-java">@Service
public class UserService {
}</code></pre><p>Bu default holatda singleton.</p><p>Ya’ni Spring bitta <code>UserService</code> obyekt yaratadi va hamma joyda shuni ishlatadi.</p><pre><code class="language-text">Controller A ─┐
Controller B ─┼──&gt; same UserService object
Controller C ─┘</code></pre><p>Shuning uchun singleton beanda mutable state saqlash xavfli.</p><p>Yomon:</p><pre><code class="language-java">@Service
public class OrderService {

    private Long currentUserId;

    public void createOrder(Long userId) {
        this.currentUserId = userId;
    }
}</code></pre><p>Bu productionda bug beradi, chunki bir vaqtning o‘zida ko‘p request keladi.</p><p>Yaxshi:</p><pre><code class="language-java">@Service
public class OrderService {

    public void createOrder(Long userId) {
        // userId local variable sifatida ishlatiladi
    }
}</code></pre><hr><h3>18. Prototype scope</h3><pre><code class="language-java">@Component
@Scope(&quot;prototype&quot;)
public class ReportBuilder {
}</code></pre><p>Har safar so‘ralganda yangi obyekt yaratiladi.</p><pre><code class="language-java">ReportBuilder builder1 = context.getBean(ReportBuilder.class);
ReportBuilder builder2 = context.getBean(ReportBuilder.class);</code></pre><p>Bu yerda:</p><pre><code class="language-text">builder1 != builder2</code></pre><p>Lekin muhim joy bor.</p><p>Agar prototype bean singleton ichiga inject qilinsa:</p><pre><code class="language-java">@Service
public class ReportService {

    private final ReportBuilder reportBuilder;

    public ReportService(ReportBuilder reportBuilder) {
        this.reportBuilder = reportBuilder;
    }
}</code></pre><p><code>ReportService</code> singleton bo‘lgani uchun <code>ReportBuilder</code> ham faqat bir marta inject bo‘ladi.</p><p>Har safar yangi prototype olish uchun <code>ObjectProvider</code> ishlatiladi:</p><pre><code class="language-java">@Service
public class ReportService {

    private final ObjectProvider&lt;ReportBuilder&gt; builderProvider;

    public ReportService(ObjectProvider&lt;ReportBuilder&gt; builderProvider) {
        this.builderProvider = builderProvider;
    }

    public void generate() {
        ReportBuilder builder = builderProvider.getObject();
    }
}</code></pre><hr><h3>19. Request scope</h3><p>Har HTTP request uchun alohida bean.</p><pre><code class="language-java">@Component
@RequestScope
public class RequestContext {

    private String requestId;

    public String getRequestId() {
        return requestId;
    }

    public void setRequestId(String requestId) {
        this.requestId = requestId;
    }
}</code></pre><p>Bu web applicationlarda foydali:</p><ul><li><p>request id;</p></li><li><p>user context;</p></li><li><p>tenant id;</p></li><li><p>request-specific data.</p></li></ul><p>Lekin ehtiyot bo‘lish kerak. Hamma narsani request scope qilish noto‘g‘ri. Ko‘pincha <code>ThreadLocal</code>, filter, interceptor yoki security context yaxshiroq bo‘ladi.</p><hr><h3>20. Spring application start bo‘lish jarayoni</h3><p>Soddalashtirilgan ketma-ketlik:</p><pre><code class="language-java">SpringApplication.run(App.class, args);</code></pre><p>Ichkarida taxminan:</p><pre><code class="language-text">1. SpringApplication yaratiladi
2. Environment tayyorlanadi
3. ApplicationContext yaratiladi
4. BeanDefinitionlar yuklanadi
5. BeanFactoryPostProcessorlar ishlaydi
6. Singleton beanlar yaratiladi
7. BeanPostProcessorlar ishlaydi
8. Embedded server start bo‘ladi
9. ApplicationReadyEvent chiqadi</code></pre><p>Shuning uchun <code>@PostConstruct</code> paytida application hali to‘liq tayyor bo‘lmasligi mumkin.</p><p>Agar app to‘liq ishga tushgandan keyin nimadir qilish kerak bo‘lsa, ko‘pincha bu yaxshiroq:</p><pre><code class="language-java">@Component
public class StartupRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        System.out.println(&quot;Application started&quot;);
    }
}</code></pre><p>Yoki:</p><pre><code class="language-java">@Component
public class ReadyListener {

    @EventListener(ApplicationReadyEvent.class)
    public void onReady() {
        System.out.println(&quot;Application is fully ready&quot;);
    }
}</code></pre><hr><h3>21. Spring Internals real hayotda nimaga kerak?</h3><h4>1. <code>@Transactional</code> nega ishlamayotganini tushunish uchun</h4><p>Masalan:</p><pre><code class="language-java">@Service
public class UserService {

    public void create() {
        saveUser();
    }

    @Transactional
    public void saveUser() {
        // DB save
    }
}</code></pre><p>Bu yerda <code>@Transactional</code> ishlamasligi mumkin.</p><p>Sabab: <strong>self-invocation</strong>.</p><p><code>create()</code> ichidan <code>saveUser()</code> chaqirilganda proxy chetlab o‘tiladi.</p><p>To‘g‘ri yechimlardan biri:</p><pre><code class="language-java">@Service
public class UserFacade {

    private final UserService userService;

    public UserFacade(UserService userService) {
        this.userService = userService;
    }

    public void create() {
        userService.saveUser();
    }
}</code></pre><pre><code class="language-java">@Service
public class UserService {

    @Transactional
    public void saveUser() {
        // DB save
    }
}</code></pre><hr><h4>2. Bean circular dependency muammosini tushunish uchun</h4><p>Yomon:</p><pre><code class="language-java">@Service
public class AService {
    public AService(BService bService) {}
}

@Service
public class BService {
    public BService(AService aService) {}
}</code></pre><p>Bu circular dependency.</p><pre><code class="language-text">AService BServicega muhtoj
BService AServicega muhtoj</code></pre><p>Yaxshi yechim - responsibilityni ajratish:</p><pre><code class="language-java">@Service
public class CommonService {
}</code></pre><p>Yoki arxitekturani qayta ko‘rib chiqish.</p><hr><h4>3. Startup sekinligini topish uchun</h4><p>Agar app sekin start bo‘lsa, sabablar:</p><ul><li><p>juda ko‘p eager singleton bean;</p></li><li><p><code>@PostConstruct</code> ichida og‘ir ish;</p></li><li><p>DB/API calllar startupda bajarilishi;</p></li><li><p>noto‘g‘ri auto-configuration;</p></li><li><p>classpathda keraksiz starterlar.</p></li></ul><hr><h3>22. Java developer bilishi kerak bo‘lgan xulosa</h3><p>Java Spring developer faqat annotation ishlatmaydi. U annotation ortida nima bo‘layotganini tushunadi.</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Mavzu</p></th><th rowspan="1" colspan="1"><p>O&#039;rta darajada bilish kerak bo‘lgan narsa</p></th></tr><tr><td rowspan="1" colspan="1"><p>Bean lifecycle</p></td><td rowspan="1" colspan="1"><p>Bean qachon yaratiladi, qachon init/destroy bo‘ladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>BeanPostProcessor</p></td><td rowspan="1" colspan="1"><p>Bean yaratilgandan keyin unga qanday ishlov beriladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>BeanFactoryPostProcessor</p></td><td rowspan="1" colspan="1"><p>BeanDefinitionlarga qanday ta’sir qilinadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>@Conditional</p></td><td rowspan="1" colspan="1"><p>Bean shart asosida qanday yaratiladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Auto-configuration</p></td><td rowspan="1" colspan="1"><p>Spring Boot default beanlarni qanday qo‘shadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>@Import</p></td><td rowspan="1" colspan="1"><p>Configurationlarni qanday ulaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Scope</p></td><td rowspan="1" colspan="1"><p>Singleton/prototype/request farqlari</p></td></tr><tr><td rowspan="1" colspan="1"><p>Proxy</p></td><td rowspan="1" colspan="1"><p><code>@Transactional</code>, AOP, cache qanday ishlaydi</p></td></tr></tbody></table><hr><h3>23. Interview savollar</h3><h4>Savol 1: Bean lifecycle qanday?</h4><p>Javob:</p><blockquote><p>Spring avval classlarni scan qiladi, BeanDefinition yaratadi, keyin bean obyektini yaratadi, dependency injection qiladi, BeanPostProcessorlarni ishlatadi, init methodlarni chaqiradi va bean tayyor bo‘ladi. Application yopilganda destroy callbacklar ishlaydi.</p></blockquote><h4>Savol 2: BeanPostProcessor va BeanFactoryPostProcessor farqi nima?</h4><p>Javob:</p><blockquote><p>BeanFactoryPostProcessor bean yaratilishidan oldin BeanDefinition bilan ishlaydi. BeanPostProcessor esa bean yaratilgandan keyin real obyekt bilan ishlaydi.</p></blockquote><h4>Savol 3: <code>@Transactional</code> nega ba’zida ishlamaydi?</h4><p>Javob:</p><blockquote><p>Chunki Spring <code>@Transactional</code>ni proxy orqali ishlatadi. Agar method shu class ichidan o‘zini o‘zi chaqirsa, proxy chetlab o‘tiladi va transaction ochilmaydi.</p></blockquote><h4>Savol 4: Singleton beanda fieldda request data saqlash mumkinmi?</h4><p>Javob:</p><blockquote><p>Yo‘q. Singleton bean barcha requestlar uchun bitta obyekt bo‘ladi. Mutable fieldlar race condition va noto‘g‘ri data muammosiga olib keladi.</p></blockquote><h4>Savol 5: Auto-configuration qanday ishlaydi?</h4><p>Javob:</p><blockquote><p>Spring Boot classpath, property va mavjud beanlarga qarab shartli configurationlarni ishga tushiradi. Masalan <code>@ConditionalOnClass</code>, <code>@ConditionalOnMissingBean</code>, <code>@ConditionalOnProperty</code> orqali kerakli default beanlarni yaratadi.</p></blockquote><hr><h3>24. Amaliy mini-topshiriq</h3><p>Quyidagi kichik projectda sinab ko‘ring:</p><pre><code class="language-java">@Component
public class DemoBean {

    public DemoBean() {
        System.out.println(&quot;Constructor&quot;);
    }

    @PostConstruct
    public void init() {
        System.out.println(&quot;PostConstruct&quot;);
    }

    @PreDestroy
    public void destroy() {
        System.out.println(&quot;PreDestroy&quot;);
    }
}</code></pre><p>Keyin <code>BeanPostProcessor</code> qo‘shing:</p><pre><code class="language-java">@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (beanName.equals(&quot;demoBean&quot;)) {
            System.out.println(&quot;Before init: &quot; + beanName);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (beanName.equals(&quot;demoBean&quot;)) {
            System.out.println(&quot;After init: &quot; + beanName);
        }
        return bean;
    }
}</code></pre><p>Taxminiy natija:</p><pre><code class="language-text">Constructor
Before init: demoBean
PostConstruct
After init: demoBean</code></pre><p>Bu Spring lifecycle’ni ko‘z bilan ko‘rishga yordam beradi.</p>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Custom Spring extensions]]></title>
            <link>https://blog.jtscorp.uz/custom-spring-extensions</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/custom-spring-extensions</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Custom Spring extensions</strong> - Spring Boot’ni faqat ishlatish emas, balki uni o‘z loyihangiz yoki kompaniya platformangizga moslab <strong>kengaytirish</strong> demakdir.</p><p>Bu maqola quyidagilarni qamrab oladi: <strong>custom auto-configuration, custom starter, custom Actuator endpoint, custom annotation + AOP, ApplicationListener, SmartLifecycle</strong>.</p><hr><h3>1. Custom Spring extensions nima?</h3><p>Oddiy developer Spring Boot’dan tayyor holda foydalanadi:</p><pre><code class="language-text">spring-boot-starter-web
spring-boot-starter-data-jpa
spring-boot-starter-security</code></pre><p>Senior developer esa kerak bo‘lsa o‘zi ham shunday reusable extension yozadi:</p><pre><code class="language-text">company-security-starter
company-observability-starter
company-multitenancy-starter
company-kafka-starter
company-audit-starter</code></pre><p>Maqsad:</p><pre><code class="language-text">Bir xil config va boilerplate kodni har projectda qayta yozmaslik.</code></pre><p>Masalan, kompaniyada 20 ta microservice bor. Har birida quyidagilar kerak:</p><pre><code class="language-text">JWT security config
Tracing config
Audit logging
Tenant filter
Kafka error handler
Actuator custom endpoint
Common exception handler</code></pre><p>Bularni har service’da copy-paste qilish yomon. Yaxshiroq yechim:</p><pre><code class="language-text">company-platform-starter</code></pre><hr><h3>2. Qachon custom extension kerak?</h3><p>Custom extension kerak bo‘ladi:</p><pre><code class="language-text">[+] Bir xil config 3+ projectda takrorlansa
[+] Platform team umumiy standart bermoqchi bo‘lsa
[+] Security/observability/kafka config markazlashtirilsa
[+] Internal library reusable bo‘lishi kerak bo‘lsa
[+] Teamlar bir xil convention bilan ishlashi kerak bo‘lsa</code></pre><p>Kerak emas:</p><pre><code class="language-text">[-] Faqat bitta project uchun bo‘lsa
[-] Oddiy 2-3 ta bean uchun ortiqcha abstraction bo‘lsa
[-] Extension projectni tushunishni qiyinlashtirsa
[-] Team hali Spring internals’ni yaxshi bilmasa</code></pre><p>Senior qoida:</p><pre><code class="language-text">Abstraction foyda bersa yoziladi. Faqat &quot;chiroyli ko‘rinsin&quot; deb starter yozilmaydi.</code></pre><hr><h3>3. Custom auto-configuration</h3><h4>Auto-configuration nima?</h4><p>Spring Boot auto-configuration classpath’dagi dependency va mavjud beanlarga qarab avtomatik config qiladi. Rasmiy hujjatda Spring Boot auto-configuration application’ni qo‘shilgan jar dependency’lar asosida avtomatik sozlashga urinishi aytiladi. (<a href="https://docs.spring.io/spring-boot/reference/using/auto-configuration.html" title="Auto-configuration :: Spring Boot">Home</a>)</p><p>Masalan:</p><pre><code class="language-text">spring-boot-starter-data-jpa bor
↓
DataSource kerak
↓
Hibernate kerak
↓
JpaRepository kerak
↓
Spring Boot ko‘p config’ni o‘zi qiladi</code></pre><p>Custom auto-configuration ham xuddi shunday:</p><pre><code class="language-text">company-audit-starter dependency qo‘shildi
↓
AuditLogger bean avtomatik yaratildi
↓
AuditAspect avtomatik yoqildi</code></pre><hr><h4>Oddiy custom auto-configuration misol</h4><p>Tasavvur qilamiz, bizda audit logging library bor.</p><h4>1. Properties class</h4><pre><code class="language-java">package uz.company.audit;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = &quot;company.audit&quot;)
public class AuditProperties {

    private boolean enabled = true;
    private String applicationName = &quot;unknown-service&quot;;

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public String getApplicationName() {
        return applicationName;
    }

    public void setApplicationName(String applicationName) {
        this.applicationName = applicationName;
    }
}</code></pre><hr><h4>2. Audit service</h4><pre><code class="language-java">package uz.company.audit;

public class AuditLogger {

    private final AuditProperties properties;

    public AuditLogger(AuditProperties properties) {
        this.properties = properties;
    }

    public void log(String action, String userId) {
        System.out.printf(
                &quot;audit app=%s action=%s userId=%s%n&quot;,
                properties.getApplicationName(),
                action,
                userId
        );
    }
}</code></pre><hr><h4>3. Auto-configuration class</h4><pre><code class="language-java">package uz.company.audit.autoconfigure;

import uz.company.audit.AuditLogger;
import uz.company.audit.AuditProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
@EnableConfigurationProperties(AuditProperties.class)
@ConditionalOnProperty(
        prefix = &quot;company.audit&quot;,
        name = &quot;enabled&quot;,
        havingValue = &quot;true&quot;,
        matchIfMissing = true
)
public class AuditAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public AuditLogger auditLogger(AuditProperties properties) {
        return new AuditLogger(properties);
    }
}</code></pre><p>Spring Boot custom auto-configuration yaratish bo‘yicha rasmiy hujjatda auto-configuration starter bilan bog‘lanishi, starter esa auto-configuration code va kerakli library’larni olib kelishi tushuntiriladi. (<a href="https://docs.spring.io/spring-boot/reference/features/developing-auto-configuration.html" title="Creating Your Own Auto-configuration">Home</a>)</p><hr><h3>4. <code>AutoConfiguration.imports</code></h3><p>Spring Boot 3&#039;da auto-configuration class’ni e’lon qilish uchun odatda quyidagi file ishlatiladi:</p><pre><code class="language-text">src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports</code></pre><p>Ichida:</p><pre><code class="language-text">uz.company.audit.autoconfigure.AuditAutoConfiguration</code></pre><p>Shundan keyin boshqa project dependency qo‘shsa, Spring Boot bu auto-configuration’ni ko‘radi.</p><hr><h3>5. Conditional annotations</h3><p>Custom extension yozishda eng muhim narsa - <strong>shartli ishlash</strong>.</p><p>Yomon starter:</p><pre><code class="language-text">Dependency qo‘shilishi bilan hamma narsani majburan yaratadi.</code></pre><p>Yaxshi starter:</p><pre><code class="language-text">Faqat kerakli class/property/bean bo‘lsa ishlaydi.
User o‘z bean’ini bersa, starter chekinadi.</code></pre><hr><h4>Muhim condition annotationlar</h4><table><tbody><tr><th rowspan="1" colspan="1"><p>Annotation</p></th><th rowspan="1" colspan="1"><p>Vazifasi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>@ConditionalOnClass</code></p></td><td rowspan="1" colspan="1"><p>Classpath’da class bo‘lsa ishlaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>@ConditionalOnMissingClass</code></p></td><td rowspan="1" colspan="1"><p>Class yo‘q bo‘lsa ishlaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>@ConditionalOnBean</code></p></td><td rowspan="1" colspan="1"><p>Bean bor bo‘lsa ishlaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>@ConditionalOnMissingBean</code></p></td><td rowspan="1" colspan="1"><p>Bean yo‘q bo‘lsa default bean yaratadi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>@ConditionalOnProperty</code></p></td><td rowspan="1" colspan="1"><p>Property bo‘yicha yoqadi/o‘chiradi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>@ConditionalOnWebApplication</code></p></td><td rowspan="1" colspan="1"><p>Web app bo‘lsa ishlaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>@ConditionalOnNotWebApplication</code></p></td><td rowspan="1" colspan="1"><p>Web app bo‘lmasa ishlaydi</p></td></tr></tbody></table><p>Misol:</p><pre><code class="language-java">@Bean
@ConditionalOnMissingBean
public AuditLogger auditLogger(AuditProperties properties) {
    return new AuditLogger(properties);
}</code></pre><p>Bu nima degani?</p><pre><code class="language-text">Agar user o‘zi AuditLogger bean bermagan bo‘lsa,
starter default AuditLogger yaratadi.</code></pre><p>Bu extension yozishda juda muhim odob:</p><pre><code class="language-text">User override qila olishi kerak.</code></pre><hr><h3>6. Custom starter</h3><h4>Starter nima?</h4><p>Starter - dependencylarni va auto-configuration’ni bir joyga yig‘adigan library.</p><p>Masalan:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;uz.company&lt;/groupId&gt;
    &lt;artifactId&gt;company-audit-spring-boot-starter&lt;/artifactId&gt;
    &lt;version&gt;1.0.0&lt;/version&gt;
&lt;/dependency&gt;</code></pre><p>Shuni qo‘shgan project avtomatik quyidagilarga ega bo‘ladi:</p><pre><code class="language-text">AuditProperties
AuditLogger
AuditAspect
Audit actuator endpoint
Audit metrics</code></pre><hr><h4>Starter project structure</h4><pre><code class="language-text">company-audit-spring-boot-starter/
  src/main/java/
    uz/company/audit/
      AuditLogger.java
      AuditProperties.java
      AuditAspect.java

    uz/company/audit/autoconfigure/
      AuditAutoConfiguration.java

  src/main/resources/
    META-INF/spring/
      org.springframework.boot.autoconfigure.AutoConfiguration.imports

  pom.xml</code></pre><hr><h4>Starter <code>pom.xml</code></h4><pre><code class="language-xml">&lt;project&gt;
    &lt;groupId&gt;uz.company&lt;/groupId&gt;
    &lt;artifactId&gt;company-audit-spring-boot-starter&lt;/artifactId&gt;
    &lt;version&gt;1.0.0&lt;/version&gt;

    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-autoconfigure&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-configuration-processor&lt;/artifactId&gt;
            &lt;optional&gt;true&lt;/optional&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-aop&lt;/artifactId&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/project&gt;</code></pre><p><code>spring-boot-configuration-processor</code> foydasi:</p><pre><code class="language-text">application.yml da autocomplete va metadata beradi.</code></pre><p>Masalan:</p><pre><code class="language-yaml">company:
  audit:
    enabled: true
    application-name: order-service</code></pre><hr><h3>7. Starter ishlatish</h3><p>Boshqa microservice’da faqat dependency qo‘shiladi:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;uz.company&lt;/groupId&gt;
    &lt;artifactId&gt;company-audit-spring-boot-starter&lt;/artifactId&gt;
    &lt;version&gt;1.0.0&lt;/version&gt;
&lt;/dependency&gt;</code></pre><p>Config:</p><pre><code class="language-yaml">company:
  audit:
    enabled: true
    application-name: order-service</code></pre><p>Service’da:</p><pre><code class="language-java">@Service
@RequiredArgsConstructor
public class OrderService {

    private final AuditLogger auditLogger;

    public void cancelOrder(Long orderId, String userId) {
        auditLogger.log(&quot;ORDER_CANCELLED&quot;, userId);
    }
}</code></pre><p>Bitta starter bilan 20 ta service’da bir xil audit standard ishlaydi.</p><hr><h3>8. Custom annotation + AOP</h3><h4>Muammo</h4><p>Audit logni har joyda qo‘lda chaqirish ham boilerplate:</p><pre><code class="language-java">auditLogger.log(&quot;ORDER_CANCELLED&quot;, userId);</code></pre><p>Yaxshiroq:</p><pre><code class="language-java">@Auditable(action = &quot;ORDER_CANCELLED&quot;)
public void cancelOrder(Long orderId) {
    ...
}</code></pre><hr><h4>Custom annotation</h4><pre><code class="language-java">package uz.company.audit;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Auditable {
    String action();
}</code></pre><hr><h4>Aspect</h4><pre><code class="language-java">package uz.company.audit;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AuditAspect {

    private final AuditLogger auditLogger;
    private final CurrentUserProvider currentUserProvider;

    public AuditAspect(
            AuditLogger auditLogger,
            CurrentUserProvider currentUserProvider
    ) {
        this.auditLogger = auditLogger;
        this.currentUserProvider = currentUserProvider;
    }

    @Around(&quot;@annotation(auditable)&quot;)
    public Object audit(
            ProceedingJoinPoint joinPoint,
            Auditable auditable
    ) throws Throwable {
        String userId = currentUserProvider.getCurrentUserId();

        try {
            Object result = joinPoint.proceed();

            auditLogger.log(auditable.action() + &quot;_SUCCESS&quot;, userId);

            return result;
        } catch (Throwable ex) {
            auditLogger.log(auditable.action() + &quot;_FAILED&quot;, userId);
            throw ex;
        }
    }
}</code></pre><p>Auto-config’da aspect qo‘shamiz:</p><pre><code class="language-java">@Bean
@ConditionalOnMissingBean
public AuditAspect auditAspect(
        AuditLogger auditLogger,
        CurrentUserProvider currentUserProvider
) {
    return new AuditAspect(auditLogger, currentUserProvider);
}</code></pre><hr><h4>Ishlatish</h4><pre><code class="language-java">@Service
public class OrderService {

    @Auditable(action = &quot;ORDER_CANCELLED&quot;)
    public void cancelOrder(Long orderId) {
        // business logic
    }
}</code></pre><p>Natija:</p><pre><code class="language-text">Method success bo‘lsa → ORDER_CANCELLED_SUCCESS
Exception bo‘lsa → ORDER_CANCELLED_FAILED</code></pre><hr><h3>9. Custom annotation yozishda ehtiyot bo‘lish</h3><h4>1. Self-invocation muammosi</h4><p>Spring AOP proxy orqali ishlaydi.</p><pre><code class="language-java">@Service
public class OrderService {

    public void methodA() {
        methodB(); // self call
    }

    @Auditable(action = &quot;METHOD_B&quot;)
    public void methodB() {
    }
}</code></pre><p>Bu holatda <code>methodB()</code> annotation’i ishlamasligi mumkin, chunki chaqiriq proxy’dan o‘tmaydi.</p><p>Yechimlar:</p><pre><code class="language-text">- Annotated methodni boshqa bean’ga chiqarish
- AspectJ weaving ishlatish
- Self-invocation’dan qochish</code></pre><hr><h4>2. Private methodlarga AOP ishlamasligi</h4><p>Yomon:</p><pre><code class="language-java">@Auditable(action = &quot;PRIVATE&quot;)
private void doWork() {
}</code></pre><p>Spring AOP odatda public/proxy orqali chaqiriladigan methodlarda ishlaydi.</p><hr><h4>3. Annotation business logic’ni yashirib yubormasin</h4><p>Annotation juda ko‘payib ketsa:</p><pre><code class="language-java">@Auditable
@Retryable
@Transactional
@Cacheable
@PreAuthorize</code></pre><p>methodning real behavior’ini tushunish qiyinlashadi.</p><p>Senior qoida:</p><pre><code class="language-text">Annotation abstraction bo‘lsa ham, behavior aniq va predictable bo‘lsin.</code></pre><hr><h3>10. Custom Actuator endpoint</h3><h4>Nima uchun kerak?</h4><p>Spring Boot Actuator application’ni kuzatish va boshqarish uchun endpointlar beradi; rasmiy hujjatda Actuator ichki endpointlardan tashqari custom endpoint qo‘shish imkonini ham berishi aytiladi. (<a href="https://docs.spring.io/spring-boot/reference/actuator/endpoints.html" title="Endpoints :: Spring Boot">Home</a>)</p><p>Custom endpoint misollar:</p><pre><code class="language-text">/actuator/features
/actuator/tenants
/actuator/build-info
/actuator/kafka-status
/actuator/outbox</code></pre><hr><h4>Custom endpoint misol</h4><pre><code class="language-java">package uz.company.platform.actuator;

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;

import java.util.Map;

@Endpoint(id = &quot;features&quot;)
public class FeatureFlagsEndpoint {

    private final FeatureFlagService featureFlagService;

    public FeatureFlagsEndpoint(FeatureFlagService featureFlagService) {
        this.featureFlagService = featureFlagService;
    }

    @ReadOperation
    public Map&lt;String, Boolean&gt; features() {
        return featureFlagService.getAllFlags();
    }
}</code></pre><p>Auto-config:</p><pre><code class="language-java">@Bean
@ConditionalOnBean(FeatureFlagService.class)
public FeatureFlagsEndpoint featureFlagsEndpoint(
        FeatureFlagService featureFlagService
) {
    return new FeatureFlagsEndpoint(featureFlagService);
}</code></pre><p>Config:</p><pre><code class="language-yaml">management:
  endpoints:
    web:
      exposure:
        include: health,info,features</code></pre><p>Endpoint:</p><pre><code class="language-http">GET /actuator/features</code></pre><p>Response:</p><pre><code class="language-json">{
  &quot;paymentV2&quot;: true,
  &quot;newCheckout&quot;: false
}</code></pre><hr><h4>Custom Actuator endpoint xavfsizligi</h4><p>Actuator endpointlar production’da ehtiyotkorlik bilan ochiladi.</p><p>Yomon:</p><pre><code class="language-yaml">management:
  endpoints:
    web:
      exposure:
        include: &quot;*&quot;</code></pre><p>Yaxshi:</p><pre><code class="language-yaml">management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus,features</code></pre><p>Security config:</p><pre><code class="language-java">@Bean
SecurityFilterChain actuatorSecurity(HttpSecurity http) throws Exception {
    return http
            .securityMatcher(&quot;/actuator/**&quot;)
            .authorizeHttpRequests(auth -&gt; auth
                    .requestMatchers(&quot;/actuator/health&quot;).permitAll()
                    .requestMatchers(&quot;/actuator/prometheus&quot;).hasRole(&quot;MONITORING&quot;)
                    .anyRequest().hasRole(&quot;ADMIN&quot;)
            )
            .build();
}</code></pre><p>Senior qoida:</p><pre><code class="language-text">Actuator endpoint operational data beradi. Uni public ochmang.</code></pre><hr><h3>11. Custom HealthIndicator</h3><p>Agar sizda muhim dependency bo‘lsa, custom health indicator yozish mumkin.</p><p>Masalan, payment provider status:</p><pre><code class="language-java">@Component
public class PaymentProviderHealthIndicator implements HealthIndicator {

    private final PaymentProviderClient client;

    public PaymentProviderHealthIndicator(PaymentProviderClient client) {
        this.client = client;
    }

    @Override
    public Health health() {
        try {
            boolean ok = client.ping();

            if (ok) {
                return Health.up()
                        .withDetail(&quot;provider&quot;, &quot;payment-provider&quot;)
                        .build();
            }

            return Health.down()
                    .withDetail(&quot;provider&quot;, &quot;payment-provider&quot;)
                    .build();
        } catch (Exception ex) {
            return Health.down(ex).build();
        }
    }
}</code></pre><p>Lekin ehtiyot:</p><pre><code class="language-text">Har dependency’ni liveness ichiga qo‘shmang.
Critical dependency readiness’da bo‘lishi mumkin.</code></pre><hr><h3>12. Application events</h3><p>Spring ichida event publish/listen qilish mumkin.</p><h4>Event class</h4><pre><code class="language-java">public record OrderCreatedEvent(
        Long orderId,
        Long userId
) {}</code></pre><h4>Publish qilish</h4><pre><code class="language-java">@Service
@RequiredArgsConstructor
public class OrderService {

    private final ApplicationEventPublisher eventPublisher;

    @Transactional
    public void createOrder(CreateOrderRequest request) {
        Order order = saveOrder(request);

        eventPublisher.publishEvent(
                new OrderCreatedEvent(order.getId(), order.getUserId())
        );
    }
}</code></pre><h4>Listen qilish</h4><pre><code class="language-java">@Component
public class OrderCreatedListener {

    @EventListener
    public void handle(OrderCreatedEvent event) {
        System.out.println(&quot;Order created: &quot; + event.orderId());
    }
}</code></pre><hr><h3>13. Transactional event listener</h3><p>Oddiy <code>@EventListener</code> transaction ichida darhol ishlashi mumkin.</p><p>Agar eventni faqat DB commit bo‘lgandan keyin ishlatmoqchi bo‘lsangiz:</p><pre><code class="language-java">@Component
public class OrderCreatedListener {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handle(OrderCreatedEvent event) {
        // DB commit bo‘lgandan keyin ishlaydi
    }
}</code></pre><p>Bu muhim.</p><p>Yomon flow:</p><pre><code class="language-text">Order save qilindi
Event publish qilindi
Listener notification yubordi
Keyin DB transaction rollback bo‘ldi</code></pre><p>Natija:</p><pre><code class="language-text">User notification oldi, lekin order DB’da yo‘q.</code></pre><p>Yaxshi:</p><pre><code class="language-text">DB commit
↓
AFTER_COMMIT listener
↓
Notification</code></pre><hr><h3>14. <code>@Async</code> event listener</h3><p>Event listener’ni async qilish mumkin:</p><pre><code class="language-java">@Component
public class OrderCreatedListener {

    @Async(&quot;eventExecutor&quot;)
    @EventListener
    public void handle(OrderCreatedEvent event) {
        // background processing
    }
}</code></pre><p>Executor:</p><pre><code class="language-java">@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean
    public Executor eventExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(1000);
        executor.setThreadNamePrefix(&quot;event-&quot;);
        executor.initialize();
        return executor;
    }
}</code></pre><p>Ehtiyot:</p><pre><code class="language-text">Async event transaction boundary va error handlingni murakkablashtiradi.</code></pre><p>Agar event juda muhim bo‘lsa:</p><pre><code class="language-text">Spring event emas, outbox + Kafka ishlating.</code></pre><hr><h3>15. ApplicationListener</h3><p><code>@EventListener</code> o‘rniga class-based listener ham yozish mumkin.</p><pre><code class="language-java">@Component
public class OrderCreatedApplicationListener
        implements ApplicationListener&lt;OrderCreatedEvent&gt; {

    @Override
    public void onApplicationEvent(OrderCreatedEvent event) {
        System.out.println(&quot;Order created: &quot; + event.orderId());
    }
}</code></pre><p>Qachon kerak?</p><pre><code class="language-text">- Framework-level extension yozilganda
- Generic listener kerak bo‘lsa
- Starter ichida explicit contract kerak bo‘lsa</code></pre><hr><h3>16. Spring Boot lifecycle events</h3><p>Spring Boot application start paytida turli eventlar chiqaradi.</p><p>Ko‘p ishlatiladiganlari:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Event</p></th><th rowspan="1" colspan="1"><p>Qachon ishlaydi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>ApplicationStartingEvent</code></p></td><td rowspan="1" colspan="1"><p>Juda erta</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>ApplicationEnvironmentPreparedEvent</code></p></td><td rowspan="1" colspan="1"><p>Environment tayyor</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>ApplicationPreparedEvent</code></p></td><td rowspan="1" colspan="1"><p>Context tayyorlanmoqda</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>ApplicationStartedEvent</code></p></td><td rowspan="1" colspan="1"><p>Context refresh bo‘ldi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>ApplicationReadyEvent</code></p></td><td rowspan="1" colspan="1"><p>App request qabul qilishga tayyor</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>ApplicationFailedEvent</code></p></td><td rowspan="1" colspan="1"><p>Startup fail bo‘ldi</p></td></tr></tbody></table><p>Masalan:</p><pre><code class="language-java">@Component
public class StartupLogger {

    @EventListener(ApplicationReadyEvent.class)
    public void onReady() {
        System.out.println(&quot;Application is ready&quot;);
    }
}</code></pre><p><code>ApplicationReadyEvent</code> - warmup yoki startup tugaganidan keyingi ishlar uchun qulay.</p><p>Lekin og‘ir ishni shu yerda sync bajarish ham app readiness’ga ta’sir qilishi mumkin.</p><hr><h3>17. SmartLifecycle</h3><h4>Nima?</h4><p><code>SmartLifecycle</code> - Spring context start/stop jarayonida komponentni boshqarish uchun ishlatiladi.</p><p>Masalan:</p><pre><code class="language-text">Kafka consumerni custom start qilish
Background worker boshlash
Scheduler-like processor ishga tushirish
External connection ochish
Graceful shutdown’da to‘xtatish</code></pre><p>Spring Framework <code>SmartLifecycle</code> interface’i <code>Lifecycle</code>&#039;ni kengaytiradi va auto-startup, phase kabi imkoniyatlar beradi. (<a href="https://spring.io/" title="Spring | Home">Home</a>)</p><hr><h4>SmartLifecycle misol</h4><pre><code class="language-java">@Component
public class OutboxWorkerLifecycle implements SmartLifecycle {

    private final OutboxWorker worker;
    private volatile boolean running = false;

    public OutboxWorkerLifecycle(OutboxWorker worker) {
        this.worker = worker;
    }

    @Override
    public void start() {
        worker.start();
        running = true;
    }

    @Override
    public void stop() {
        worker.stop();
        running = false;
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    @Override
    public boolean isAutoStartup() {
        return true;
    }

    @Override
    public int getPhase() {
        return 100;
    }
}</code></pre><hr><h4><code>getPhase()</code> nima?</h4><p>Phase start/stop tartibini boshqaradi.</p><pre><code class="language-text">Kichik phase → oldin start
Katta phase → keyin start

Stop paytida aksincha:
Katta phase → oldin stop
Kichik phase → keyin stop</code></pre><p>Masalan:</p><pre><code class="language-text">Database connection manager phase=0
Outbox worker phase=100</code></pre><p>Start:</p><pre><code class="language-text">DB avval
Worker keyin</code></pre><p>Stop:</p><pre><code class="language-text">Worker avval
DB keyin</code></pre><p>Bu graceful shutdown uchun juda foydali.</p><hr><h3>18. Custom BeanPostProcessor</h3><h4>Nima?</h4><p><code>BeanPostProcessor</code> bean yaratilgandan keyin uni o‘zgartirish yoki tekshirish imkonini beradi.</p><p>Bu advanced Spring internals mavzusi.</p><p>Misol: <code>@Sensitive</code> annotationli beanlarni startup’da tekshirish.</p><pre><code class="language-java">@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveComponent {
}</code></pre><p>Processor:</p><pre><code class="language-java">@Component
public class SensitiveBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(
            Object bean,
            String beanName
    ) throws BeansException {

        if (bean.getClass().isAnnotationPresent(SensitiveComponent.class)) {
            System.out.println(&quot;Sensitive bean registered: &quot; + beanName);
        }

        return bean;
    }
}</code></pre><p>Ehtiyot:</p><pre><code class="language-text">BeanPostProcessor noto‘g‘ri yozilsa, butun application context behavior’i buzilishi mumkin.</code></pre><p>Buni faqat haqiqiy zaruratda ishlating.</p><hr><h3>19. Custom annotation for validation</h3><p>Spring extension faqat AOP emas. Bean Validation uchun ham custom annotation yoziladi.</p><h4>Annotation</h4><pre><code class="language-java">@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface ValidUzPhone {

    String message() default &quot;Invalid Uzbekistan phone number&quot;;

    Class&lt;?&gt;[] groups() default {};

    Class&lt;? extends Payload&gt;[] payload() default {};
}</code></pre><h4>Validator</h4><pre><code class="language-java">public class PhoneNumberValidator
        implements ConstraintValidator&lt;ValidUzPhone, String&gt; {

    private static final Pattern PATTERN =
            Pattern.compile(&quot;^\\+998\\d{9}$&quot;);

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null || value.isBlank()) {
            return true;
        }

        return PATTERN.matcher(value).matches();
    }
}</code></pre><h4>Ishlatish</h4><pre><code class="language-java">public record RegisterRequest(
        @NotBlank String name,
        @ValidUzPhone String phone
) {}</code></pre><hr><h3>20. Custom meta-annotation</h3><p>Ba’zida bir nechta annotationni bitta annotationga yig‘ish mumkin.</p><p>Masalan:</p><pre><code class="language-java">@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize(&quot;hasAuthority(&#039;SCOPE_orders.write&#039;)&quot;)
@Transactional
@Auditable(action = &quot;ORDER_WRITE&quot;)
public @interface OrderWriteOperation {
}</code></pre><p>Ishlatish:</p><pre><code class="language-java">@OrderWriteOperation
public void cancelOrder(Long orderId) {
    ...
}</code></pre><p>Ehtiyot:</p><pre><code class="language-text">Meta-annotation behaviorni yashirib yuborishi mumkin.
Team buni yaxshi tushunsa ishlating.</code></pre><hr><h3>21. Custom starter uchun test</h3><p>Starter yozsangiz, uni albatta test qilish kerak.</p><p>Spring Boot’da <code>ApplicationContextRunner</code> custom auto-configuration testlari uchun juda qulay.</p><pre><code class="language-java">class AuditAutoConfigurationTest {

    private final ApplicationContextRunner contextRunner =
            new ApplicationContextRunner()
                    .withConfiguration(
                            AutoConfigurations.of(AuditAutoConfiguration.class)
                    );

    @Test
    void shouldCreateAuditLoggerByDefault() {
        contextRunner.run(context -&gt; {
            assertThat(context).hasSingleBean(AuditLogger.class);
        });
    }

    @Test
    void shouldDisableAuditWhenPropertyFalse() {
        contextRunner
                .withPropertyValues(&quot;company.audit.enabled=false&quot;)
                .run(context -&gt; {
                    assertThat(context).doesNotHaveBean(AuditLogger.class);
                });
    }

    @Test
    void shouldBackOffWhenUserProvidesAuditLogger() {
        contextRunner
                .withBean(AuditLogger.class, () -&gt; new CustomAuditLogger())
                .run(context -&gt; {
                    assertThat(context).hasSingleBean(AuditLogger.class);
                });
    }
}</code></pre><p>Test qilinadigan narsalar:</p><pre><code class="language-text">[ ] Default bean yaratiladimi?
[ ] Property orqali o‘chadimi?
[ ] User custom bean bersa back off qiladimi?
[ ] Kerakli class yo‘q bo‘lsa ishlamayaptimi?
[ ] Web/non-web context farqi to‘g‘rimi?</code></pre><hr><h3>22. Versioning va compatibility</h3><p>Internal starter ham product kabi versionlanadi.</p><p>Yomon:</p><pre><code class="language-text">company-starter:latest</code></pre><p>Yaxshi:</p><pre><code class="language-text">company-starter:1.4.2</code></pre><p>Semantic versioning:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Version</p></th><th rowspan="1" colspan="1"><p>Ma’nosi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>1.4.3</code></p></td><td rowspan="1" colspan="1"><p>Bug fix</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>1.5.0</code></p></td><td rowspan="1" colspan="1"><p>Backward-compatible feature</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>2.0.0</code></p></td><td rowspan="1" colspan="1"><p>Breaking change</p></td></tr></tbody></table><p>Senior qoida:</p><pre><code class="language-text">Starter yangilanishi 20 ta service’ni buzmasligi kerak.</code></pre><hr><h3>23. Real example: company observability starter</h3><p>Kompaniyada har microservice’da bir xil observability kerak:</p><pre><code class="language-text">traceId loglarda
common MDC filter
custom metrics
standard actuator exposure
correlation id
exception metrics</code></pre><p>Starter:</p><pre><code class="language-text">company-observability-starter</code></pre><p>Ichida:</p><pre><code class="language-text">CorrelationIdFilter
MdcLoggingFilter
CommonTagsCustomizer
CustomHealthIndicator
Actuator endpoint
AutoConfiguration
Properties</code></pre><p>Service’da faqat:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;uz.company&lt;/groupId&gt;
    &lt;artifactId&gt;company-observability-starter&lt;/artifactId&gt;
    &lt;version&gt;1.0.0&lt;/version&gt;
&lt;/dependency&gt;</code></pre><p>Config:</p><pre><code class="language-yaml">company:
  observability:
    correlation-id-header: X-Correlation-Id
    service-name: order-service</code></pre><p>Natija:</p><pre><code class="language-text">Hamma service bir xil log format
Hamma service bir xil metrics tag
Hamma service bir xil correlation id handling</code></pre><p>Bu custom Spring extension’ning haqiqiy foydasi.</p><hr><h3>24. Common mistakes</h3><h4>1. Starter juda ko‘p ish qiladi</h4><p>Yomon:</p><pre><code class="language-text">company-platform-starter
  security
  kafka
  database
  redis
  actuator
  tracing
  exception
  migration
  cache</code></pre><p>Bunday starter juda og‘ir bo‘ladi.</p><p>Yaxshiroq:</p><pre><code class="language-text">company-security-starter
company-kafka-starter
company-observability-starter
company-multitenancy-starter</code></pre><hr><h4>2. Override qilish imkoni yo‘q</h4><p>Yomon:</p><pre><code class="language-java">@Bean
public AuditLogger auditLogger() {
    return new AuditLogger();
}</code></pre><p>Yaxshi:</p><pre><code class="language-java">@Bean
@ConditionalOnMissingBean
public AuditLogger auditLogger() {
    return new AuditLogger();
}</code></pre><hr><h4>3. Property bilan o‘chirib bo‘lmaydi</h4><p>Har starterda disable option bo‘lsin:</p><pre><code class="language-yaml">company:
  audit:
    enabled: false</code></pre><hr><h4>4. Internal API documentation yo‘q</h4><p>Starterda README bo‘lishi kerak:</p><pre><code class="language-text">- Nima qiladi?
- Qanday dependency qo‘shiladi?
- Qanday config bor?
- Default behavior nima?
- Qanday override qilinadi?
- Qanday troubleshoot qilinadi?</code></pre><hr><h4>5. Auto-config haddan tashqari agressiv</h4><p>Starter user project’da kutilmagan bean yaratmasligi kerak.</p><hr><h3>25. Custom Spring extensions checklist</h3><pre><code class="language-text">[ ] Extension haqiqatan reusablemi?
[ ] Auto-configuration shartli ishlaydimi?
[ ] @ConditionalOnMissingBean ishlatilganmi?
[ ] Property orqali enable/disable bormi?
[ ] ConfigurationProperties bor-mi?
[ ] Metadata/autocomplete uchun configuration processor bormi?
[ ] AutoConfiguration.imports to‘g‘ri yozilganmi?
[ ] Actuator endpointlar secure qilinganmi?
[ ] AOP self-invocation muammosi hisobga olinganmi?
[ ] Lifecycle start/stop tartibi aniqmi?
[ ] ApplicationContextRunner bilan test qilinganmi?
[ ] Versioning strategy bormi?
[ ] README va config example bormi?
[ ] Breaking change release’da aniq yozilganmi?</code></pre><hr><h3>26. Senior interview javob</h3><p>Suhbatda so‘rasa:</p><blockquote><p>Custom Spring starter yoki auto-configuration yozganmisiz? Qanday ishlaydi?</p></blockquote><p>Javob:</p><pre><code class="language-text">Custom Spring starter umumiy platform config’ni reusable qilish uchun yoziladi.
Masalan, company-observability-starter yoki company-security-starter.

Starter ichida auto-configuration class bo‘ladi, u @AutoConfiguration bilan belgilanadi va
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports file orqali Spring Boot’ga tanitiladi.

Auto-configuration majburan bean yaratmasligi kerak. Shuning uchun @ConditionalOnClass,
@ConditionalOnProperty, @ConditionalOnMissingBean kabi condition annotationlar ishlatiladi.
User o‘z bean’ini bersa, starter back off qilishi kerak.

ConfigurationProperties orqali application.yml’dan type-safe config olinadi.
Agar kerak bo‘lsa custom annotation + AOP, custom Actuator endpoint, HealthIndicator,
ApplicationListener yoki SmartLifecycle qo‘shiladi.

Bunday starter ApplicationContextRunner bilan test qilinadi:
default bean yaratilishi, property bilan disable bo‘lishi, user custom bean bersa override ishlashi tekshiriladi.</code></pre><hr><h3>27. Qisqa xulosa</h3><p><strong>Custom Spring extensions</strong> Senior developer uchun Spring ekotizimini chuqur tushunishni ko‘rsatadi.</p><p>Qisqa xulosa qilsak:</p><pre><code class="language-text">Custom extension - takrorlanadigan platform kodni reusable, configurable va override qilinadigan Spring module’ga aylantirish.</code></pre><p>Eng muhim tushunchalar:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Mavzu</p></th><th rowspan="1" colspan="1"><p>Esda qoladigan qoida</p></th></tr><tr><td rowspan="1" colspan="1"><p>Auto-configuration</p></td><td rowspan="1" colspan="1"><p>Dependency/property/bean asosida avtomatik config</p></td></tr><tr><td rowspan="1" colspan="1"><p>Custom starter</p></td><td rowspan="1" colspan="1"><p>Dependency + auto-config + common code</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>AutoConfiguration.imports</code></p></td><td rowspan="1" colspan="1"><p>Spring Boot 3 auto-config registration</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>@ConditionalOnMissingBean</code></p></td><td rowspan="1" colspan="1"><p>User override qila olsin</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>@ConfigurationProperties</code></p></td><td rowspan="1" colspan="1"><p>Type-safe config</p></td></tr><tr><td rowspan="1" colspan="1"><p>Custom annotation + AOP</p></td><td rowspan="1" colspan="1"><p>Cross-cutting concern uchun</p></td></tr><tr><td rowspan="1" colspan="1"><p>Custom Actuator endpoint</p></td><td rowspan="1" colspan="1"><p>Operational visibility uchun</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>ApplicationListener</code></p></td><td rowspan="1" colspan="1"><p>Spring eventlarga javob berish</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>SmartLifecycle</code></p></td><td rowspan="1" colspan="1"><p>Start/stop tartibini boshqarish</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>ApplicationContextRunner</code></p></td><td rowspan="1" colspan="1"><p>Starter testlari uchun</p></td></tr></tbody></table>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[REST Client & HTTP]]></title>
            <link>https://blog.jtscorp.uz/rest-client-http</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/rest-client-http</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>REST client</strong> - Spring Boot application boshqa servis yoki tashqi API’ga HTTP so‘rov yuborishi uchun ishlatiladi.</p><p>Bu bo‘lim ichida quyidagilar bor: <code>RestTemplate</code>, <code>WebClient</code>, <code>HttpInterface</code>, timeout va retry configuration.</p><hr><h3>1. REST client nima?</h3><p>Oldingi darslarda biz REST API yozdik:</p><pre><code class="language-text">Frontend / Mobile App → Spring Boot API → Database</code></pre><p>Lekin real loyihada backend faqat database bilan ishlamaydi. U boshqa servislar bilan ham gaplashadi:</p><pre><code class="language-text">Spring Boot API → Payment service
Spring Boot API → SMS service
Spring Boot API → Telegram bot API
Spring Boot API → CRM service
Spring Boot API → Currency exchange API
Spring Boot API → Delivery service</code></pre><p>Mana shu tashqi servisga HTTP request yuboradigan qism <strong>REST client</strong> deyiladi.</p><hr><h3>2. Server va client farqi</h3><h4>Biz server bo‘lgan holat</h4><p>Frontend bizga request yuboradi:</p><pre><code class="language-http">GET /api/products</code></pre><p>Bizning controller javob qaytaradi:</p><pre><code class="language-java">@GetMapping(&quot;/api/products&quot;)
public List&lt;ProductResponse&gt; getProducts() {
    return productService.getProducts();
}</code></pre><p>Bu yerda biz <strong>servermiz</strong>.</p><hr><h4>Biz client bo‘lgan holat</h4><p>Biz boshqa servisga request yuboramiz:</p><pre><code class="language-http">GET https://api.example.com/currency/USD</code></pre><p>Bu yerda biz <strong>clientmiz</strong>.</p><pre><code class="language-text">Bizning Spring Boot app → tashqi API</code></pre><hr><h3>3. HTTP qisqa eslatma</h3><p>REST client ishlatishdan oldin HTTP asoslarini bilish kerak.</p><h4>HTTP methodlar</h4><table><tbody><tr><th rowspan="1" colspan="1"><p>Method</p></th><th rowspan="1" colspan="1"><p>Ma’nosi</p></th><th rowspan="1" colspan="1"><p>Misol</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>GET</code></p></td><td rowspan="1" colspan="1"><p>Ma’lumot olish</p></td><td rowspan="1" colspan="1"><p>Product list olish</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>POST</code></p></td><td rowspan="1" colspan="1"><p>Yangi ma’lumot yaratish</p></td><td rowspan="1" colspan="1"><p>Order yaratish</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>PUT</code></p></td><td rowspan="1" colspan="1"><p>To‘liq update</p></td><td rowspan="1" colspan="1"><p>Product update</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>PATCH</code></p></td><td rowspan="1" colspan="1"><p>Qisman update</p></td><td rowspan="1" colspan="1"><p>Order status update</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>DELETE</code></p></td><td rowspan="1" colspan="1"><p>O‘chirish</p></td><td rowspan="1" colspan="1"><p>Product delete</p></td></tr></tbody></table><hr><h4>HTTP status code</h4><table><tbody><tr><th rowspan="1" colspan="1"><p>Status</p></th><th rowspan="1" colspan="1"><p>Ma’nosi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>200 OK</code></p></td><td rowspan="1" colspan="1"><p>Muvaffaqiyatli</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>201 Created</code></p></td><td rowspan="1" colspan="1"><p>Yaratildi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>400 Bad Request</code></p></td><td rowspan="1" colspan="1"><p>Request noto‘g‘ri</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>401 Unauthorized</code></p></td><td rowspan="1" colspan="1"><p>Login kerak</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>403 Forbidden</code></p></td><td rowspan="1" colspan="1"><p>Ruxsat yo‘q</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>404 Not Found</code></p></td><td rowspan="1" colspan="1"><p>Topilmadi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>409 Conflict</code></p></td><td rowspan="1" colspan="1"><p>Conflict, masalan duplicate</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>500 Internal Server Error</code></p></td><td rowspan="1" colspan="1"><p>Server xatosi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>503 Service Unavailable</code></p></td><td rowspan="1" colspan="1"><p>Servis vaqtincha ishlamayapti</p></td></tr></tbody></table><p>REST client yozganda status codelarni to‘g‘ri ushlash juda muhim.</p><hr><h3>4. Spring’da REST client variantlari</h3><p>Spring’da bir nechta HTTP client bor:</p><pre><code class="language-text">RestTemplate
RestClient
WebClient
HttpInterface</code></pre><p>Spring Framework 6.1+ da <code>RestClient</code> ham bor, shuni ham bilib qo‘yish foydali.</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Client</p></th><th rowspan="1" colspan="1"><p>Turi</p></th><th rowspan="1" colspan="1"><p>Qachon ishlatiladi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>RestTemplate</code></p></td><td rowspan="1" colspan="1"><p>Synchronous / blocking</p></td><td rowspan="1" colspan="1"><p>Legacy projectlarda ko‘p uchraydi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>RestClient</code></p></td><td rowspan="1" colspan="1"><p>Synchronous / blocking</p></td><td rowspan="1" colspan="1"><p>Modern Spring’da RestTemplate o‘rniga qulayroq</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>WebClient</code></p></td><td rowspan="1" colspan="1"><p>Reactive / non-blocking</p></td><td rowspan="1" colspan="1"><p>Reactive, async, streaming, high-load</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>HttpInterface</code></p></td><td rowspan="1" colspan="1"><p>Declarative client</p></td><td rowspan="1" colspan="1"><p>Interface yozib, boilerplate kamaytirish</p></td></tr></tbody></table><p>Spring rasmiy hujjatlarida <code>RestTemplate</code> synchronous HTTP client ekani aytilgan. <code>WebClient</code> esa non-blocking reactive client sifatida ta’riflanadi. (<a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html" title="RestTemplate (Spring Framework 7.0.7 API)">Home</a>)</p><hr><h3>5. Blocking va non-blocking farqi</h3><h4>Blocking</h4><p>Request yuboriladi va thread javob kelguncha kutadi.</p><pre><code class="language-text">Thread → request yubordi → javob kutdi → davom etdi</code></pre><p><code>RestTemplate</code> va <code>RestClient</code> blocking client hisoblanadi.</p><p>Oddiy backend API’lar uchun tushunish oson.</p><hr><h4>Non-blocking</h4><p>Request yuboriladi, lekin thread bekor kutib turmaydi.</p><pre><code class="language-text">Thread → request yubordi → boshqa ishga o‘tdi
Javob kelganda callback/reactive pipeline ishlaydi</code></pre><p><code>WebClient</code> non-blocking reactive client. Rasmiy hujjatlarda <code>WebClient</code> request bajarish uchun HTTP client library kerak qilishi va Reactor Netty, JDK HttpClient, Jetty Reactive HttpClient kabi variantlarni qo‘llashi aytiladi. (<a href="https://docs.spring.io/spring-framework/reference/web/webflux-webclient.html" title="WebClient :: Spring Framework">Home</a>)</p><hr><h3>6. Qaysi birini tanlash kerak?</h3><p>Java developer uchun amaliy qoida:</p><pre><code class="language-text">Legacy project → RestTemplate ko‘rasiz
Oddiy modern blocking API → RestClient
Reactive/WebFlux project → WebClient
Boilerplate kamaytirish → HttpInterface</code></pre><p>Agar siz hozir Spring Boot MVC bilan oddiy REST API yozayotgan bo‘lsangiz:</p><pre><code class="language-text">RestTemplate’ni tushuning
RestClient/WebClient’ni ham bilib qo‘ying
HttpInterface bilan tanishing</code></pre><hr><h3>7. Example scenario</h3><p>Tasavvur qilamiz, bizda <code>Product API</code> bor. U tashqi <code>Currency API</code>dan kurs olib, product narxini USD’dan UZS’ga o‘tkazadi.</p><p>Tashqi API taxminan shunday response qaytaradi:</p><pre><code class="language-json">{
  &quot;base&quot;: &quot;USD&quot;,
  &quot;target&quot;: &quot;UZS&quot;,
  &quot;rate&quot;: 12650.50
}</code></pre><p>DTO:</p><pre><code class="language-java">import java.math.BigDecimal;

public record CurrencyRateResponse(
        String base,
        String target,
        BigDecimal rate
) {
}</code></pre><hr><h3>8. <code>RestTemplate</code></h3><p><code>RestTemplate</code> eski, lekin hali ko‘p loyihalarda uchraydi. Spring hujjatlarida u HTTP request bajaradigan synchronous client sifatida berilgan. (<a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html" title="RestTemplate (Spring Framework 7.0.7 API)">Home</a>)</p><h4>Dependency</h4><p>Agar sizda <code>spring-boot-starter-web</code> bo‘lsa, odatda kerakli web client classlar mavjud bo‘ladi.</p><hr><h4>Bean yaratish</h4><pre><code class="language-java">import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestClientConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}</code></pre><hr><h4>GET request</h4><pre><code class="language-java">import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class CurrencyClient {

    private final RestTemplate restTemplate;

    public CurrencyClient(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public CurrencyRateResponse getUsdToUzsRate() {
        String url = &quot;https://api.example.com/currency/USD/UZS&quot;;

        return restTemplate.getForObject(url, CurrencyRateResponse.class);
    }
}</code></pre><p>Bu yerda:</p><pre><code class="language-text">getForObject → GET request yuboradi va response body’ni objectga aylantiradi</code></pre><hr><h4><code>ResponseEntity</code> bilan olish</h4><p>Agar status, header ham kerak bo‘lsa:</p><pre><code class="language-java">import org.springframework.http.ResponseEntity;

public CurrencyRateResponse getUsdToUzsRate() {
    String url = &quot;https://api.example.com/currency/USD/UZS&quot;;

    ResponseEntity&lt;CurrencyRateResponse&gt; response =
            restTemplate.getForEntity(url, CurrencyRateResponse.class);

    if (!response.getStatusCode().is2xxSuccessful()) {
        throw new BadRequestException(&quot;Currency API xato qaytardi&quot;);
    }

    return response.getBody();
}</code></pre><hr><h4>POST request</h4><p>Masalan SMS servisga request yuboramiz.</p><p>Request DTO:</p><pre><code class="language-java">public record SmsSendRequest(
        String phone,
        String message
) {
}</code></pre><p>Response DTO:</p><pre><code class="language-java">public record SmsSendResponse(
        String id,
        String status
) {
}</code></pre><p>Client:</p><pre><code class="language-java">public SmsSendResponse sendSms(String phone, String message) {
    String url = &quot;https://sms.example.com/api/send&quot;;

    SmsSendRequest request = new SmsSendRequest(phone, message);

    return restTemplate.postForObject(
            url,
            request,
            SmsSendResponse.class
    );
}</code></pre><hr><h3>9. <code>exchange()</code> method</h3><p><code>exchange()</code> ko‘proq nazorat beradi: method, headers, body, response type.</p><pre><code class="language-java">import org.springframework.http.*;
import org.springframework.stereotype.Service;

@Service
public class SmsClient {

    private final RestTemplate restTemplate;

    public SmsClient(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public SmsSendResponse sendSms(String phone, String message) {
        String url = &quot;https://sms.example.com/api/send&quot;;

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(&quot;token-value&quot;);

        SmsSendRequest body = new SmsSendRequest(phone, message);

        HttpEntity&lt;SmsSendRequest&gt; entity = new HttpEntity&lt;&gt;(body, headers);

        ResponseEntity&lt;SmsSendResponse&gt; response = restTemplate.exchange(
                url,
                HttpMethod.POST,
                entity,
                SmsSendResponse.class
        );

        return response.getBody();
    }
}</code></pre><p>Bu yerda:</p><pre><code class="language-text">HttpHeaders → headerlar
HttpEntity  → body + header
exchange    → to‘liq HTTP request</code></pre><hr><h3>10. RestTemplate timeout</h3><p>Timeout juda muhim. Timeout bo‘lmasa, tashqi API osilib qolsa, sizning servis ham kutib qoladi.</p><p>Minimal config:</p><pre><code class="language-java">import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                .connectTimeout(Duration.ofSeconds(2))
                .readTimeout(Duration.ofSeconds(5))
                .build();
    }
}</code></pre><h4>Connect timeout</h4><pre><code class="language-text">Tashqi serverga ulanish uchun maksimal kutish vaqti</code></pre><h4>Read timeout</h4><pre><code class="language-text">Ulanib bo‘lgandan keyin response kelishini kutish vaqti</code></pre><p>Amaliy qoida:</p><pre><code class="language-text">Timeoutsiz HTTP client - production’da xavfli.</code></pre><hr><h3>11. RestTemplate xatolarini ushlash</h3><p><code>RestTemplate</code> 4xx/5xx statuslarda exception tashlashi mumkin.</p><p>Masalan:</p><pre><code class="language-java">import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;

public CurrencyRateResponse getUsdToUzsRate() {
    try {
        return restTemplate.getForObject(
                &quot;https://api.example.com/currency/USD/UZS&quot;,
                CurrencyRateResponse.class
        );
    } catch (HttpClientErrorException ex) {
        throw new BadRequestException(&quot;Currency API client xato: &quot; + ex.getStatusCode());
    } catch (HttpServerErrorException ex) {
        throw new ExternalServiceException(&quot;Currency API server xato&quot;);
    } catch (ResourceAccessException ex) {
        throw new ExternalServiceException(&quot;Currency API timeout yoki network xato&quot;);
    }
}</code></pre><p>Custom exception:</p><pre><code class="language-java">public class ExternalServiceException extends RuntimeException {

    public ExternalServiceException(String message) {
        super(message);
    }
}</code></pre><hr><h3>12. <code>WebClient</code></h3><p><code>WebClient</code> - modern, reactive HTTP client.</p><p>Rasmiy Javadoc’da <code>WebClient</code> non-blocking, reactive API ekanligi aytiladi. (<a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html" title="WebClient (Spring Framework 7.0.7 API)">Home</a>)</p><h4>Dependency</h4><p><code>WebClient</code> uchun odatda:</p><pre><code class="language-gradle">implementation &#039;org.springframework.boot:spring-boot-starter-webflux&#039;</code></pre><p>Maven:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-webflux&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>MVC projectda ham <code>WebClient</code> ishlatish mumkin. Faqat reactive dependency qo‘shiladi.</p><hr><h4>WebClient bean</h4><pre><code class="language-java">import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient currencyWebClient() {
        return WebClient.builder()
                .baseUrl(&quot;https://api.example.com&quot;)
                .build();
    }
}</code></pre><hr><h4>GET request</h4><p>Reactive ko‘rinish:</p><pre><code class="language-java">import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Service
public class CurrencyWebClient {

    private final WebClient webClient;

    public CurrencyWebClient(WebClient currencyWebClient) {
        this.webClient = currencyWebClient;
    }

    public Mono&lt;CurrencyRateResponse&gt; getUsdToUzsRate() {
        return webClient.get()
                .uri(&quot;/currency/USD/UZS&quot;)
                .retrieve()
                .bodyToMono(CurrencyRateResponse.class);
    }
}</code></pre><p>Bu <code>Mono&lt;CurrencyRateResponse&gt;</code> qaytaradi.</p><hr><h4>MVC projectda <code>.block()</code> ishlatish</h4><p>Agar siz oddiy Spring MVC projectda bo‘lsangiz va service method oddiy object qaytarishi kerak bo‘lsa:</p><pre><code class="language-java">public CurrencyRateResponse getUsdToUzsRateBlocking() {
    return webClient.get()
            .uri(&quot;/currency/USD/UZS&quot;)
            .retrieve()
            .bodyToMono(CurrencyRateResponse.class)
            .block();
}</code></pre><p>Lekin ehtiyot bo‘ling:</p><pre><code class="language-text">WebFlux reactive flow ichida block() ishlatish yomon.
MVC projectda tashqi API chaqirish uchun ba’zan ishlatiladi.</code></pre><hr><h3>13. WebClient error handling</h3><pre><code class="language-java">import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;

public Mono&lt;CurrencyRateResponse&gt; getUsdToUzsRate() {
    return webClient.get()
            .uri(&quot;/currency/USD/UZS&quot;)
            .retrieve()
            .onStatus(
                    HttpStatusCode::is4xxClientError,
                    response -&gt; Mono.error(
                            new BadRequestException(&quot;Currency API client xato&quot;)
                    )
            )
            .onStatus(
                    HttpStatusCode::is5xxServerError,
                    response -&gt; Mono.error(
                            new ExternalServiceException(&quot;Currency API server xato&quot;)
                    )
            )
            .bodyToMono(CurrencyRateResponse.class);
}</code></pre><hr><h3>14. WebClient timeout</h3><p>Reactor Netty bilan timeout config:</p><pre><code class="language-java">import io.netty.channel.ChannelOption;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;

import java.time.Duration;

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient currencyWebClient() {
        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
                .responseTimeout(Duration.ofSeconds(5));

        return WebClient.builder()
                .baseUrl(&quot;https://api.example.com&quot;)
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();
    }
}</code></pre><p>Bu yerda:</p><pre><code class="language-text">CONNECT_TIMEOUT_MILLIS → ulanish timeout
responseTimeout       → response kutish timeout</code></pre><hr><h3>15. WebClient POST request</h3><pre><code class="language-java">public Mono&lt;SmsSendResponse&gt; sendSms(String phone, String message) {
    SmsSendRequest request = new SmsSendRequest(phone, message);

    return webClient.post()
            .uri(&quot;/api/send&quot;)
            .header(&quot;Authorization&quot;, &quot;Bearer token-value&quot;)
            .bodyValue(request)
            .retrieve()
            .bodyToMono(SmsSendResponse.class);
}</code></pre><p>Blocking variant:</p><pre><code class="language-java">public SmsSendResponse sendSmsBlocking(String phone, String message) {
    return webClient.post()
            .uri(&quot;/api/send&quot;)
            .header(&quot;Authorization&quot;, &quot;Bearer token-value&quot;)
            .bodyValue(new SmsSendRequest(phone, message))
            .retrieve()
            .bodyToMono(SmsSendResponse.class)
            .block();
}</code></pre><hr><h3>16. <code>RestClient</code></h3><p>Spring Framework 6.1&#039;dan boshlab <code>RestClient</code> bor. Rasmiy hujjatda <code>RestClient</code> synchronous HTTP client bo‘lib, fluent API orqali request bajarishi aytilgan. (<a href="https://docs.spring.io/spring-framework/reference/integration/rest-clients.html" title="REST Clients :: Spring Framework">Home</a>)</p><p>Bu mavzuda alohida ko‘rsatilmagan, lekin hozirgi Spring loyihalarda bilish foydali.</p><h4>Bean</h4><pre><code class="language-java">import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;

@Configuration
public class RestClientConfig {

    @Bean
    public RestClient currencyRestClient() {
        return RestClient.builder()
                .baseUrl(&quot;https://api.example.com&quot;)
                .build();
    }
}</code></pre><h4>GET request</h4><pre><code class="language-java">import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;

@Service
public class CurrencyRestClient {

    private final RestClient restClient;

    public CurrencyRestClient(RestClient currencyRestClient) {
        this.restClient = currencyRestClient;
    }

    public CurrencyRateResponse getUsdToUzsRate() {
        return restClient.get()
                .uri(&quot;/currency/USD/UZS&quot;)
                .retrieve()
                .body(CurrencyRateResponse.class);
    }
}</code></pre><p>Bu <code>RestTemplate</code>ga qaraganda ko‘proq fluent va o‘qilishi qulay.</p><hr><h3>17. <code>HttpInterface</code></h3><p><code>HttpInterface</code> - declarative HTTP client. Ya’ni siz interface yozasiz, Spring implementation’ni proxy orqali yaratadi.</p><p>Rasmiy hujjatlarda HTTP Interface client’lar REST client ustida declarative HTTP service interface yaratish usuli sifatida berilgan. (<a href="https://docs.spring.io/spring-framework/reference/integration/rest-clients.html" title="REST Clients :: Spring Framework">Home</a>)</p><h4>Interface</h4><pre><code class="language-java">import org.springframework.web.service.annotation.GetExchange;

public interface CurrencyApiClient {

    @GetExchange(&quot;/currency/USD/UZS&quot;)
    CurrencyRateResponse getUsdToUzsRate();
}</code></pre><p>Agar path variable kerak bo‘lsa:</p><pre><code class="language-java">import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.service.annotation.GetExchange;

public interface CurrencyApiClient {

    @GetExchange(&quot;/currency/{base}/{target}&quot;)
    CurrencyRateResponse getRate(
            @PathVariable String base,
            @PathVariable String target
    );
}</code></pre><hr><h4>HttpInterface config</h4><p>Spring 6&#039;da <code>HttpServiceProxyFactory</code> orqali proxy yaratiladi.</p><pre><code class="language-java">import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.support.RestClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;

@Configuration
public class HttpInterfaceConfig {

    @Bean
    public RestClient currencyRestClient() {
        return RestClient.builder()
                .baseUrl(&quot;https://api.example.com&quot;)
                .build();
    }

    @Bean
    public CurrencyApiClient currencyApiClient(RestClient currencyRestClient) {
        HttpServiceProxyFactory factory = HttpServiceProxyFactory
                .builderFor(RestClientAdapter.create(currencyRestClient))
                .build();

        return factory.createClient(CurrencyApiClient.class);
    }
}</code></pre><p>Service’da ishlatish:</p><pre><code class="language-java">@Service
public class CurrencyService {

    private final CurrencyApiClient currencyApiClient;

    public CurrencyService(CurrencyApiClient currencyApiClient) {
        this.currencyApiClient = currencyApiClient;
    }

    public BigDecimal getUsdRate() {
        CurrencyRateResponse response = currencyApiClient.getRate(&quot;USD&quot;, &quot;UZS&quot;);
        return response.rate();
    }
}</code></pre><hr><h3>18. HttpInterface POST request</h3><pre><code class="language-java">import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.service.annotation.PostExchange;

public interface SmsApiClient {

    @PostExchange(&quot;/api/send&quot;)
    SmsSendResponse sendSms(@RequestBody SmsSendRequest request);
}</code></pre><p>Service:</p><pre><code class="language-java">@Service
public class SmsService {

    private final SmsApiClient smsApiClient;

    public SmsService(SmsApiClient smsApiClient) {
        this.smsApiClient = smsApiClient;
    }

    public void sendOrderCreatedSms(String phone) {
        SmsSendResponse response = smsApiClient.sendSms(
                new SmsSendRequest(phone, &quot;Order yaratildi&quot;)
        );

        if (!&quot;SUCCESS&quot;.equals(response.status())) {
            throw new ExternalServiceException(&quot;SMS yuborilmadi&quot;);
        }
    }
}</code></pre><hr><h3>19. Config bilan base URL berish</h3><p>Hardcode yomon:</p><pre><code class="language-java">.baseUrl(&quot;https://api.example.com&quot;)</code></pre><p>Yaxshiroq:</p><p><code>application.yml</code>:</p><pre><code class="language-yaml">external:
  currency:
    base-url: https://api.example.com
  sms:
    base-url: https://sms.example.com
    token: ${SMS_TOKEN}</code></pre><p>Properties:</p><pre><code class="language-java">import jakarta.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Validated
@ConfigurationProperties(prefix = &quot;external.currency&quot;)
public record CurrencyApiProperties(

        @NotBlank
        String baseUrl
) {
}</code></pre><p>Config:</p><pre><code class="language-java">@Configuration
@EnableConfigurationProperties(CurrencyApiProperties.class)
public class HttpClientConfig {

    @Bean
    public RestClient currencyRestClient(CurrencyApiProperties properties) {
        return RestClient.builder()
                .baseUrl(properties.baseUrl())
                .build();
    }
}</code></pre><p>Bu professionalroq:</p><pre><code class="language-text">URL kodda emas
local/dev/prod’da alohida config bo‘ladi
secret env orqali beriladi</code></pre><hr><h3>20. Authorization header qo‘shish</h3><p>Ko‘p tashqi API token talab qiladi.</p><h4>RestClient bilan</h4><pre><code class="language-java">@Bean
public RestClient smsRestClient(SmsApiProperties properties) {
    return RestClient.builder()
            .baseUrl(properties.baseUrl())
            .defaultHeader(&quot;Authorization&quot;, &quot;Bearer &quot; + properties.token())
            .build();
}</code></pre><h4>WebClient bilan</h4><pre><code class="language-java">@Bean
public WebClient smsWebClient(SmsApiProperties properties) {
    return WebClient.builder()
            .baseUrl(properties.baseUrl())
            .defaultHeader(&quot;Authorization&quot;, &quot;Bearer &quot; + properties.token())
            .build();
}</code></pre><hr><h3>21. Timeout har doim bo‘lishi kerak</h3><p>REST client yozishda eng muhim production qoidalardan biri:</p><pre><code class="language-text">Har bir external API call timeout bilan ishlasin.</code></pre><p>Nega?</p><p>Agar tashqi SMS servis javob bermasa:</p><pre><code class="language-text">Sizning thread kutib qoladi
Requestlar yig‘iladi
Connection pool to‘ladi
API sekinlashadi
Oxiri service down bo‘lishi mumkin</code></pre><p>Timeoutlar:</p><pre><code class="language-text">connect timeout
read/response timeout</code></pre><p>Default uchun oddiy qiymatlar:</p><pre><code class="language-text">connect timeout: 1-3 sekund
read timeout: 3-10 sekund</code></pre><p>Lekin real qiymat biznesga bog‘liq.</p><hr><h3>22. Retry nima?</h3><p><strong>Retry</strong> - vaqtinchalik xato bo‘lsa requestni qayta urinish.</p><p>Masalan:</p><pre><code class="language-text">1-urinish → network timeout
2-urinish → 503
3-urinish → success</code></pre><p>Spring Retry loyihasi Spring applicationlarda declarative retry support berishini rasmiy repository ko‘rsatadi. (<a href="https://github.com/spring-projects/spring-retry" title="spring-projects/spring-retry">GitHub</a>)</p><hr><h4>Retry qachon kerak?</h4><p>Retry foydali:</p><pre><code class="language-text">Network glitch
503 Service Unavailable
504 Gateway Timeout
Temporary timeout
Rate limitdan keyin qisqa kutish</code></pre><p>Retry xavfli:</p><pre><code class="language-text">Payment charge qilish
Order yaratish
Pul yechish
Duplicate yaratadigan POST request</code></pre><p>Muhim qoida:</p><pre><code class="language-text">Retry faqat idempotent yoki xavfsiz operationlarda ishlatilsin.</code></pre><hr><h3>23. Idempotent nima?</h3><p>Idempotent operation - bir necha marta chaqirilsa ham natija buzilmaydi.</p><p>Odatda:</p><pre><code class="language-text">GET → idempotent
PUT → idempotent bo‘lishi mumkin
DELETE → odatda idempotent bo‘lishi mumkin
POST → ko‘pincha idempotent emas</code></pre><p>Masalan:</p><pre><code class="language-http">GET /currency/USD/UZS</code></pre><p>Buni retry qilish mumkin.</p><p>Lekin:</p><pre><code class="language-http">POST /payments/charge</code></pre><p>retry qilinsa, mijozdan ikki marta pul yechilishi mumkin.</p><p>Bunday holatda idempotency key kerak bo‘ladi. </p><hr><h3>24. Spring Retry dependency</h3><p>Gradle:</p><pre><code class="language-gradle">implementation &#039;org.springframework.retry:spring-retry&#039;
implementation &#039;org.springframework.boot:spring-boot-starter-aop&#039;</code></pre><p>Maven:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.retry&lt;/groupId&gt;
    &lt;artifactId&gt;spring-retry&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-aop&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>Enable:</p><pre><code class="language-java">import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.EnableRetry;

@Configuration
@EnableRetry
public class RetryConfig {
}</code></pre><hr><h3>25. <code>@Retryable</code></h3><pre><code class="language-java">import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;

@Service
public class CurrencyService {

    private final CurrencyApiClient currencyApiClient;

    public CurrencyService(CurrencyApiClient currencyApiClient) {
        this.currencyApiClient = currencyApiClient;
    }

    @Retryable(
            retryFor = ExternalServiceException.class,
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000)
    )
    public CurrencyRateResponse getUsdToUzsRate() {
        return currencyApiClient.getRate(&quot;USD&quot;, &quot;UZS&quot;);
    }
}</code></pre><p>Ma’nosi:</p><pre><code class="language-text">ExternalServiceException chiqsa retry qil
Maksimal 3 marta urinsin
Har urinish orasida 1 sekund kutadi</code></pre><hr><h4><code>@Recover</code></h4><p>Agar retrylar ham muvaffaqiyatsiz bo‘lsa:</p><pre><code class="language-java">import org.springframework.retry.annotation.Recover;

@Recover
public CurrencyRateResponse recover(ExternalServiceException ex) {
    throw new ExternalServiceException(&quot;Currency API vaqtincha ishlamayapti&quot;);
}</code></pre><p>Yoki fallback qiymat qaytarish mumkin, lekin ehtiyot bo‘ling:</p><pre><code class="language-java">@Recover
public CurrencyRateResponse recover(ExternalServiceException ex) {
    return new CurrencyRateResponse(
            &quot;USD&quot;,
            &quot;UZS&quot;,
            BigDecimal.valueOf(12600)
    );
}</code></pre><p>Bu biznesga bog‘liq. Moliyaviy kurslarda eski qiymat qaytarish xavfli bo‘lishi mumkin.</p><hr><h3>26. Retry’ni haddan oshirmang</h3><p>Yomon:</p><pre><code class="language-text">maxAttempts = 10
delay = 0</code></pre><p>Bu tashqi servisga yana ko‘proq bosim beradi.</p><p>Yaxshi:</p><pre><code class="language-text">maxAttempts = 2 yoki 3
delay = 500ms / 1s
exponential backoff</code></pre><p>Retry sababli umumiy request latency ham oshadi.</p><p>Masalan:</p><pre><code class="language-text">timeout 5s
retry 3 marta
jami kutish 15s+ bo‘lishi mumkin</code></pre><p>Shuning uchun timeout va retry birga o‘ylanishi kerak.</p><hr><h3>27. External API response’ni ichki API response’dan ajrating</h3><p>Tashqi API DTO’sini ichki response sifatida qaytarmang.</p><p>Yomon:</p><pre><code class="language-java">@GetMapping(&quot;/rate&quot;)
public CurrencyRateResponse getRate() {
    return currencyClient.getUsdToUzsRate();
}</code></pre><p>Yaxshiroq:</p><pre><code class="language-java">public record ProductPriceResponse(
        Long productId,
        BigDecimal priceUsd,
        BigDecimal priceUzs
) {
}</code></pre><p>Service:</p><pre><code class="language-java">public ProductPriceResponse getProductPriceInUzs(Long productId) {
    Product product = productRepository.findById(productId)
            .orElseThrow(() -&gt; new NotFoundException(&quot;Product topilmadi&quot;));

    CurrencyRateResponse rate = currencyClient.getUsdToUzsRate();

    BigDecimal priceUzs = product.getPriceUsd().multiply(rate.rate());

    return new ProductPriceResponse(
            product.getId(),
            product.getPriceUsd(),
            priceUzs
    );
}</code></pre><p>Sabab:</p><pre><code class="language-text">External API o‘zgarsa, sizning public API buzilmasin.</code></pre><hr><h3>28. ExternalServiceException handler</h3><p>Oldingi exception handling darsiga qo‘shamiz.</p><pre><code class="language-java">public class ExternalServiceException extends RuntimeException {

    public ExternalServiceException(String message) {
        super(message);
    }
}</code></pre><p>Global handler:</p><pre><code class="language-java">@ExceptionHandler(ExternalServiceException.class)
public ResponseEntity&lt;ErrorResponse&gt; handleExternalService(
        ExternalServiceException ex,
        HttpServletRequest request
) {
    ErrorResponse response = new ErrorResponse(
            &quot;EXTERNAL_SERVICE_ERROR&quot;,
            ex.getMessage(),
            HttpStatus.BAD_GATEWAY.value(),
            request.getRequestURI(),
            LocalDateTime.now(),
            null
    );

    return ResponseEntity
            .status(HttpStatus.BAD_GATEWAY)
            .body(response);
}</code></pre><p>Nega <code>502 Bad Gateway</code>?</p><p>Chunki sizning API ishlayapti, lekin upstream/tashqi service bilan muammo bor.</p><hr><h3>29. Logging</h3><p>External call xatolarini log qilish kerak.</p><pre><code class="language-java">private static final Logger log =
        LoggerFactory.getLogger(CurrencyClient.class);

public CurrencyRateResponse getUsdToUzsRate() {
    try {
        return restClient.get()
                .uri(&quot;/currency/USD/UZS&quot;)
                .retrieve()
                .body(CurrencyRateResponse.class);
    } catch (Exception ex) {
        log.error(&quot;Currency API call failed&quot;, ex);
        throw new ExternalServiceException(&quot;Currency API vaqtincha ishlamayapti&quot;);
    }
}</code></pre><p>Lekin token, password, secretlarni log qilmang.</p><p>Yomon:</p><pre><code class="language-java">log.info(&quot;SMS token: {}&quot;, token);</code></pre><p>Mutlaqo qilinmaydi.</p><hr><h3>30. Observability uchun nima log qilish kerak?</h3><p>Yaxshi log:</p><pre><code class="language-text">external service nomi
endpoint nomi
status code
duration
correlation id / trace id</code></pre><p>Yomon log:</p><pre><code class="language-text">token
password
full card number
secret key
shaxsiy ma’lumotlar</code></pre><hr><h3>31. Product API bilan to‘liq misol</h3><h4><code>application.yml</code></h4><pre><code class="language-yaml">external:
  currency:
    base-url: https://api.example.com
    connect-timeout-ms: 2000
    read-timeout-ms: 5000</code></pre><h4>Properties</h4><pre><code class="language-java">import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Validated
@ConfigurationProperties(prefix = &quot;external.currency&quot;)
public record CurrencyApiProperties(

        @NotBlank
        String baseUrl,

        @Min(100)
        int connectTimeoutMs,

        @Min(100)
        int readTimeoutMs
) {
}</code></pre><h4>RestClient config</h4><pre><code class="language-java">@Configuration
@EnableConfigurationProperties(CurrencyApiProperties.class)
public class CurrencyClientConfig {

    @Bean
    public RestClient currencyRestClient(CurrencyApiProperties properties) {
        return RestClient.builder()
                .baseUrl(properties.baseUrl())
                .build();
    }
}</code></pre><h4>Interface</h4><pre><code class="language-java">public interface CurrencyClient {

    CurrencyRateResponse getRate(String base, String target);
}</code></pre><h4>Implementation</h4><pre><code class="language-java">import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;

@Component
public class CurrencyRestClientImpl implements CurrencyClient {

    private final RestClient restClient;

    public CurrencyRestClientImpl(RestClient currencyRestClient) {
        this.restClient = currencyRestClient;
    }

    @Override
    public CurrencyRateResponse getRate(String base, String target) {
        try {
            return restClient.get()
                    .uri(&quot;/currency/{base}/{target}&quot;, base, target)
                    .retrieve()
                    .body(CurrencyRateResponse.class);
        } catch (Exception ex) {
            throw new ExternalServiceException(&quot;Currency API vaqtincha ishlamayapti&quot;);
        }
    }
}</code></pre><h4>Product service</h4><pre><code class="language-java">@Service
public class ProductPriceService {

    private final ProductRepository productRepository;
    private final CurrencyClient currencyClient;

    public ProductPriceService(ProductRepository productRepository,
                               CurrencyClient currencyClient) {
        this.productRepository = productRepository;
        this.currencyClient = currencyClient;
    }

    @Transactional(readOnly = true)
    public ProductPriceResponse getPriceInUzs(Long productId) {
        Product product = productRepository.findById(productId)
                .orElseThrow(() -&gt; new NotFoundException(&quot;Product topilmadi&quot;));

        CurrencyRateResponse rate = currencyClient.getRate(&quot;USD&quot;, &quot;UZS&quot;);

        BigDecimal priceUzs = product.getPriceUsd().multiply(rate.rate());

        return new ProductPriceResponse(
                product.getId(),
                product.getPriceUsd(),
                priceUzs
        );
    }
}</code></pre><hr><h3>32. Testing: tashqi API’ni real chaqirmang</h3><p>Unit testda haqiqiy SMS yoki currency API chaqirish yomon.</p><p>Nega?</p><pre><code class="language-text">Test sekinlashadi
Internet kerak bo‘ladi
External API limit tugashi mumkin
Pullik SMS ketib qolishi mumkin
Test beqaror bo‘ladi</code></pre><p>Yaxshiroq:</p><pre><code class="language-text">CurrencyClient mock qilinadi
WireMock ishlatiladi
MockWebServer ishlatiladi</code></pre><p>Boshlanishiga  kamida interface orqali ajratishni biling:</p><pre><code class="language-java">public interface CurrencyClient {
    CurrencyRateResponse getRate(String base, String target);
}</code></pre><p>Testda fake implementation berish mumkin.</p><hr><h3>33. Ko‘p uchraydigan xatolar</h3><h4>Xato 1: Timeout qo‘ymaslik</h4><p>Yomon:</p><pre><code class="language-java">new RestTemplate()</code></pre><p>Yaxshi:</p><pre><code class="language-java">builder
    .connectTimeout(Duration.ofSeconds(2))
    .readTimeout(Duration.ofSeconds(5))
    .build()</code></pre><hr><h4>Xato 2: Tokenni hardcode qilish</h4><p>Yomon:</p><pre><code class="language-java">.header(&quot;Authorization&quot;, &quot;Bearer abc123&quot;)</code></pre><p>Yaxshi:</p><pre><code class="language-yaml">sms:
  token: ${SMS_TOKEN}</code></pre><hr><h4>Xato 3: Har joyda client yaratish</h4><p>Yomon:</p><pre><code class="language-java">public void send() {
    RestTemplate restTemplate = new RestTemplate();
}</code></pre><p>Yaxshi:</p><pre><code class="language-java">@Bean
public RestTemplate restTemplate() {}</code></pre><p>Yoki:</p><pre><code class="language-java">@Bean
public RestClient restClient() {}</code></pre><hr><h4>Xato 4: External API DTO’sini domain modeli qilib ishlatish</h4><p>Yomon:</p><pre><code class="language-java">ProductService external API response’ga to‘liq bog‘lanib qoladi</code></pre><p>Yaxshi:</p><pre><code class="language-text">External DTO → Client layer
Internal DTO/domain → Service/API layer</code></pre><hr><h4>Xato 5: Har bir xatoda retry qilish</h4><p>Yomon:</p><pre><code class="language-text">400 Bad Request bo‘lsa ham retry
401 Unauthorized bo‘lsa ham retry
403 Forbidden bo‘lsa ham retry</code></pre><p>Bular retry bilan tuzalmaydi.</p><p>Retry ko‘proq:</p><pre><code class="language-text">timeout
503
temporary network error</code></pre><p>uchun.</p><hr><h4>Xato 6: Payment POST requestni retry qilish</h4><p>Bu juda xavfli bo‘lishi mumkin.</p><pre><code class="language-text">1-request pul yechdi, lekin response kelmadi
retry yana pul yechdi</code></pre><p>Bunday holatda idempotency key ishlatiladi.</p><hr><h3>34. Java developer uchun qisqa qoida</h3><pre><code class="language-text">RestTemplate → legacy blocking client
RestClient → modern blocking client
WebClient → reactive/non-blocking client
HttpInterface → declarative client
Timeout → majburiy
Retry → faqat vaqtinchalik va xavfsiz xatolarda
ExternalServiceException → tashqi servis xatolari uchun
Config → base URL va token kodda emas</code></pre><hr><h3>35. Amaliy vazifa</h3><p>Oldingi <code>Product CRUD API</code> loyihasiga external currency client qo‘shing.</p><h4>Talablar</h4><h4>Config</h4><pre><code class="language-yaml">external:
  currency:
    base-url: https://api.example.com
    connect-timeout-ms: 2000
    read-timeout-ms: 5000</code></pre><h4>DTO</h4><pre><code class="language-java">public record CurrencyRateResponse(
        String base,
        String target,
        BigDecimal rate
) {
}</code></pre><pre><code class="language-java">public record ProductPriceResponse(
        Long productId,
        BigDecimal priceUsd,
        BigDecimal priceUzs
) {
}</code></pre><h4>Client</h4><pre><code class="language-text">CurrencyClient interface
CurrencyRestClientImpl implementation</code></pre><h4>Service</h4><pre><code class="language-text">ProductPriceService productni topadi
CurrencyClient orqali USD→UZS kurs oladi
priceUzs hisoblaydi</code></pre><h4>Exception</h4><pre><code class="language-text">ExternalServiceException
GlobalExceptionHandler → 502 Bad Gateway</code></pre><h4>Muhim qoida</h4><pre><code class="language-text">Currency API ishlamasa, clientga 500 emas, 502 qaytaring.
Timeout qo‘ying.
Base URL configdan olinsin.</code></pre><hr><h3>Xulosa</h3><p><strong>REST client &amp; HTTP</strong> mavzusida Java developer quyidagilarni bilishi kerak:</p><pre><code class="language-text">HTTP methodlar va status codelar
RestTemplate
WebClient
RestClient
HttpInterface
GET / POST request yuborish
Header qo‘shish
Bearer token yuborish
Timeout config
Retry config
ExternalServiceException
502 Bad Gateway
External DTO va internal DTO ajratish</code></pre><p>Eng muhim professional qoida:</p><pre><code class="language-text">Tashqi API chaqiruvi hech qachon timeout, error handling va configsiz yozilmaydi.</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[REST API]]></title>
            <link>https://blog.jtscorp.uz/rest-api</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/rest-api</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p>Bu bo‘limda Spring Boot’da birinchi haqiqiy <strong>REST API</strong> yozishni o‘rganamiz. Bu mavzu ichida <code>@RestController</code>, <code>@RequestMapping</code>, <code>@GetMapping</code>, <code>@PostMapping</code>, <code>@PutMapping</code>, <code>@DeleteMapping</code>, <code>@PathVariable</code>, <code>@RequestParam</code>, <code>@RequestBody</code>, <code>ResponseEntity</code> va HTTP status codes bor.</p><hr><h3>1. REST API nima?</h3><p><strong>REST API</strong> - frontend, mobile app yoki boshqa servis backend bilan gaplashishi uchun ishlatiladigan HTTP API.</p><p>Masalan, mobil app backenddan userlar ro‘yxatini so‘raydi:</p><pre><code class="language-http">GET /users</code></pre><p>Backend javob qaytaradi:</p><pre><code class="language-json">[
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Ali&quot;
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Vali&quot;
  }
]</code></pre><p>Oddiy qilib:</p><blockquote><p>REST API - client va server o‘rtasidagi kelishilgan HTTP muloqot.</p></blockquote><hr><h3>2. REST API qayerda ishlatiladi?</h3><p>Masalan:</p><pre><code class="language-text">Android app → Spring Boot API
iOS app     → Spring Boot API
React app   → Spring Boot API
Telegram bot → Spring Boot API
Boshqa microservice → Spring Boot API</code></pre><p>Backend odatda JSON qaytaradi.</p><hr><h3>3. HTTP methodlar</h3><p>REST API’da eng ko‘p ishlatiladigan methodlar:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Method</p></th><th rowspan="1" colspan="1"><p>Vazifasi</p></th><th rowspan="1" colspan="1"><p>Misol</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>GET</code></p></td><td rowspan="1" colspan="1"><p>Ma’lumot olish</p></td><td rowspan="1" colspan="1"><p>Userlarni olish</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>POST</code></p></td><td rowspan="1" colspan="1"><p>Yangi ma’lumot yaratish</p></td><td rowspan="1" colspan="1"><p>Yangi user qo‘shish</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>PUT</code></p></td><td rowspan="1" colspan="1"><p>Ma’lumotni to‘liq yangilash</p></td><td rowspan="1" colspan="1"><p>Userni update qilish</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>PATCH</code></p></td><td rowspan="1" colspan="1"><p>Qisman yangilash</p></td><td rowspan="1" colspan="1"><p>Faqat phone numberni update qilish</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>DELETE</code></p></td><td rowspan="1" colspan="1"><p>Ma’lumot o‘chirish</p></td><td rowspan="1" colspan="1"><p>Userni o‘chirish</p></td></tr></tbody></table><hr><h3>4. Birinchi controller</h3><p>Spring Boot’da REST API yozish uchun <code>@RestController</code> ishlatiladi.</p><pre><code class="language-java">@RestController
public class HelloController {

    @GetMapping(&quot;/hello&quot;)
    public String hello() {
        return &quot;Salom Spring Boot&quot;;
    }
}</code></pre><p>Brauzerda ochamiz:</p><pre><code class="language-text">http://localhost:8080/hello</code></pre><p>Natija:</p><pre><code class="language-text">Salom Spring Boot</code></pre><hr><h3>5. <code>@RestController</code> nima?</h3><p><code>@RestController</code> - bu class REST API controller ekanini bildiradi.</p><p>Ya’ni:</p><pre><code class="language-java">@RestController
public class UserController {
}</code></pre><p>Spring bu classni HTTP request qabul qiladigan controller sifatida ko‘radi.</p><p>Muhim jihati:</p><blockquote><p><code>@RestController</code> methoddan qaytgan qiymatni avtomatik HTTP response body qilib beradi.</p></blockquote><p>Masalan:</p><pre><code class="language-java">@GetMapping(&quot;/name&quot;)
public String name() {
    return &quot;Ali&quot;;
}</code></pre><p>Javob body:</p><pre><code class="language-text">Ali</code></pre><hr><h3>6. <code>@RequestMapping</code> nima?</h3><p><code>@RequestMapping</code> umumiy path berish uchun ishlatiladi.</p><p>Masalan:</p><pre><code class="language-java">@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {
}</code></pre><p>Endi shu controller ichidagi hamma endpoint <code>/users</code> bilan boshlanadi.</p><pre><code class="language-java">@GetMapping
public String getUsers() {
    return &quot;Users list&quot;;
}</code></pre><p>Full path:</p><pre><code class="language-http">GET /users</code></pre><hr><h3>7. <code>@GetMapping</code></h3><p><code>GET</code> - ma’lumot olish uchun.</p><pre><code class="language-java">@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {

    @GetMapping
    public String getUsers() {
        return &quot;Userlar ro&#039;yxati&quot;;
    }
}</code></pre><p>Request:</p><pre><code class="language-http">GET /users</code></pre><p>Response:</p><pre><code class="language-text">Userlar ro&#039;yxati</code></pre><hr><p>8. <code>@PostMapping</code></p><p><code>POST</code> - yangi ma’lumot yaratish uchun.</p><pre><code class="language-java">@PostMapping
public String createUser() {
    return &quot;User yaratildi&quot;;
}</code></pre><p>Request:</p><pre><code class="language-http">POST /users</code></pre><p>Response:</p><pre><code class="language-text">User yaratildi</code></pre><p>Lekin real loyihada <code>POST</code> odatda body bilan keladi.</p><hr><h3>9. <code>@PutMapping</code></h3><p><code>PUT</code> - mavjud ma’lumotni yangilash uchun.</p><pre><code class="language-java">@PutMapping(&quot;/{id}&quot;)
public String updateUser(@PathVariable Long id) {
    return &quot;User yangilandi: &quot; + id;
}</code></pre><p>Request:</p><pre><code class="language-http">PUT /users/5</code></pre><p>Response:</p><pre><code class="language-text">User yangilandi: 5</code></pre><hr><h3>10. <code>@DeleteMapping</code></h3><p><code>DELETE</code> - ma’lumotni o‘chirish uchun.</p><pre><code class="language-java">@DeleteMapping(&quot;/{id}&quot;)
public String deleteUser(@PathVariable Long id) {
    return &quot;User o&#039;chirildi: &quot; + id;
}</code></pre><p>Request:</p><pre><code class="language-http">DELETE /users/5</code></pre><p>Response:</p><pre><code class="language-text">User o&#039;chirildi: 5</code></pre><hr><h3>11. To‘liq CRUD endpointlar</h3><pre><code class="language-java">@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {

    @GetMapping
    public String getUsers() {
        return &quot;Barcha userlar&quot;;
    }

    @GetMapping(&quot;/{id}&quot;)
    public String getUserById(@PathVariable Long id) {
        return &quot;User ID: &quot; + id;
    }

    @PostMapping
    public String createUser() {
        return &quot;User yaratildi&quot;;
    }

    @PutMapping(&quot;/{id}&quot;)
    public String updateUser(@PathVariable Long id) {
        return &quot;User yangilandi: &quot; + id;
    }

    @DeleteMapping(&quot;/{id}&quot;)
    public String deleteUser(@PathVariable Long id) {
        return &quot;User o&#039;chirildi: &quot; + id;
    }
}</code></pre><p>Endpointlar:</p><pre><code class="language-text">GET    /users
GET    /users/1
POST   /users
PUT    /users/1
DELETE /users/1</code></pre><hr><h3>12. <code>@PathVariable</code> nima?</h3><p><code>@PathVariable</code> - URL ichidagi qiymatni olish uchun ishlatiladi.</p><p>Masalan:</p><pre><code class="language-http">GET /users/10</code></pre><p>Bu yerda <code>10</code> - path variable.</p><p>Kod:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public String getUser(@PathVariable Long id) {
    return &quot;User ID: &quot; + id;
}</code></pre><p>Natija:</p><pre><code class="language-text">User ID: 10</code></pre><hr><h4><code>@PathVariable</code> nomi boshqacha bo‘lsa</h4><pre><code class="language-java">@GetMapping(&quot;/{userId}&quot;)
public String getUser(@PathVariable Long userId) {
    return &quot;User ID: &quot; + userId;
}</code></pre><p>Yoki aniq ko‘rsatish mumkin:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public String getUser(@PathVariable(&quot;id&quot;) Long userId) {
    return &quot;User ID: &quot; + userId;
}</code></pre><hr><h3>13. <code>@RequestParam</code> nima?</h3><p><code>@RequestParam</code> - URL query parameter olish uchun ishlatiladi.</p><p>Masalan:</p><pre><code class="language-http">GET /users?name=Ali</code></pre><p>Kod:</p><pre><code class="language-java">@GetMapping(&quot;/search&quot;)
public String searchUser(@RequestParam String name) {
    return &quot;Qidirilgan user: &quot; + name;
}</code></pre><p>Request:</p><pre><code class="language-http">GET /users/search?name=Ali</code></pre><p>Response:</p><pre><code class="language-text">Qidirilgan user: Ali</code></pre><hr><h4>Bir nechta <code>@RequestParam</code></h4><pre><code class="language-http">GET /users/search?name=Ali&amp;age=25</code></pre><pre><code class="language-java">@GetMapping(&quot;/search&quot;)
public String searchUser(
        @RequestParam String name,
        @RequestParam Integer age
) {
    return name + &quot; - &quot; + age;
}</code></pre><hr><h4>Optional <code>@RequestParam</code></h4><p>Agar parameter kelmasa ham xato bermasin desak:</p><pre><code class="language-java">@GetMapping(&quot;/search&quot;)
public String searchUser(
        @RequestParam(required = false) String name
) {
    return &quot;Name: &quot; + name;
}</code></pre><p>Default qiymat berish:</p><pre><code class="language-java">@GetMapping(&quot;/search&quot;)
public String searchUser(
        @RequestParam(defaultValue = &quot;unknown&quot;) String name
) {
    return &quot;Name: &quot; + name;
}</code></pre><hr><h3>14. <code>@PathVariable</code> vs <code>@RequestParam</code></h3><table><tbody><tr><th rowspan="1" colspan="1"><p>Turi</p></th><th rowspan="1" colspan="1"><p>Qayerdan oladi?</p></th><th rowspan="1" colspan="1"><p>Misol</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>@PathVariable</code></p></td><td rowspan="1" colspan="1"><p>URL path ichidan</p></td><td rowspan="1" colspan="1"><p><code>/users/5</code></p></td></tr><tr><td rowspan="1" colspan="1"><p><code>@RequestParam</code></p></td><td rowspan="1" colspan="1"><p>Query parameterdan</p></td><td rowspan="1" colspan="1"><p><code>/users?name=Ali</code></p></td></tr></tbody></table><p>Qachon qaysi biri?</p><pre><code class="language-text">/users/5             → aniq bitta user olish → @PathVariable
/users?name=Ali      → filter/search qilish → @RequestParam
/users?page=1&amp;size=20 → pagination → @RequestParam</code></pre><hr><h3>15. <code>@RequestBody</code> nima?</h3><p><code>@RequestBody</code> - HTTP request body ichidagi JSON’ni Java objectga aylantirib beradi.</p><p>Masalan frontend shunday JSON yuboradi:</p><pre><code class="language-json">{
  &quot;name&quot;: &quot;Ali&quot;,
  &quot;age&quot;: 25
}</code></pre><p>Biz Java’da DTO class yozamiz:</p><pre><code class="language-java">public class CreateUserRequest {

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}</code></pre><p>Controller:</p><pre><code class="language-java">@PostMapping
public String createUser(@RequestBody CreateUserRequest request) {
    return &quot;User yaratildi: &quot; + request.getName();
}</code></pre><p>Request:</p><pre><code class="language-http">POST /users
Content-Type: application/json</code></pre><p>Body:</p><pre><code class="language-json">{
  &quot;name&quot;: &quot;Ali&quot;,
  &quot;age&quot;: 25
}</code></pre><p>Response:</p><pre><code class="language-text">User yaratildi: Ali</code></pre><hr><h3>16. Lombok bilan DTO</h3><p>Agar Lombok ishlatsak:</p><pre><code class="language-java">@Getter
@Setter
public class CreateUserRequest {
    private String name;
    private Integer age;
}</code></pre><p>Yoki Java record:</p><pre><code class="language-java">public record CreateUserRequest(
        String name,
        Integer age
) {
}</code></pre><p>Spring Boot’da record DTO uchun juda qulay.</p><hr><h3>17. Response object qaytarish</h3><p>Text emas, object qaytarsak, Spring avtomatik JSON qiladi.</p><pre><code class="language-java">public record UserResponse(
        Long id,
        String name,
        Integer age
) {
}</code></pre><p>Controller:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public UserResponse getUser(@PathVariable Long id) {
    return new UserResponse(id, &quot;Ali&quot;, 25);
}</code></pre><p>Response:</p><pre><code class="language-json">{
  &quot;id&quot;: 1,
  &quot;name&quot;: &quot;Ali&quot;,
  &quot;age&quot;: 25
}</code></pre><hr><h3>18. List qaytarish</h3><pre><code class="language-java">@GetMapping
public List&lt;UserResponse&gt; getUsers() {
    return List.of(
            new UserResponse(1L, &quot;Ali&quot;, 25),
            new UserResponse(2L, &quot;Vali&quot;, 30)
    );
}</code></pre><p>Response:</p><pre><code class="language-json">[
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Ali&quot;,
    &quot;age&quot;: 25
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Vali&quot;,
    &quot;age&quot;: 30
  }
]</code></pre><hr><h3>19. <code>ResponseEntity</code> nima?</h3><p><code>ResponseEntity</code> - HTTP response’ni to‘liq nazorat qilish uchun ishlatiladi.</p><p>U orqali quyidagilarni belgilaymiz:</p><pre><code class="language-text">Response body
HTTP status code
Header</code></pre><p>Oddiy misol:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public ResponseEntity&lt;UserResponse&gt; getUser(@PathVariable Long id) {
    UserResponse response = new UserResponse(id, &quot;Ali&quot;, 25);
    return ResponseEntity.ok(response);
}</code></pre><p>Bu:</p><pre><code class="language-text">HTTP status: 200 OK
Body: user JSON</code></pre><hr><h3>20. <code>201 Created</code> qaytarish</h3><p>Yangi resource yaratilganda <code>201 Created</code> qaytarish yaxshi.</p><pre><code class="language-java">@PostMapping
public ResponseEntity&lt;UserResponse&gt; createUser(
        @RequestBody CreateUserRequest request
) {
    UserResponse response = new UserResponse(1L, request.name(), request.age());

    return ResponseEntity
            .status(HttpStatus.CREATED)
            .body(response);
}</code></pre><p>Response:</p><pre><code class="language-text">HTTP status: 201 Created</code></pre><p>Body:</p><pre><code class="language-json">{
  &quot;id&quot;: 1,
  &quot;name&quot;: &quot;Ali&quot;,
  &quot;age&quot;: 25
}</code></pre><hr><h3>21. <code>204 No Content</code> qaytarish</h3><p>Delete qilinganda ko‘pincha body qaytarilmaydi.</p><pre><code class="language-java">@DeleteMapping(&quot;/{id}&quot;)
public ResponseEntity&lt;Void&gt; deleteUser(@PathVariable Long id) {
    return ResponseEntity.noContent().build();
}</code></pre><p>Response:</p><pre><code class="language-text">HTTP status: 204 No Content
Body yo‘q</code></pre><hr><h3>22. HTTP status codes</h3><p>Eng kerakli statuslar:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Status</p></th><th rowspan="1" colspan="1"><p>Nomi</p></th><th rowspan="1" colspan="1"><p>Qachon ishlatiladi?</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>200</code></p></td><td rowspan="1" colspan="1"><p>OK</p></td><td rowspan="1" colspan="1"><p>Muvaffaqiyatli GET/PUT</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>201</code></p></td><td rowspan="1" colspan="1"><p>Created</p></td><td rowspan="1" colspan="1"><p>Yangi ma’lumot yaratildi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>204</code></p></td><td rowspan="1" colspan="1"><p>No Content</p></td><td rowspan="1" colspan="1"><p>Muvaffaqiyatli, lekin body yo‘q</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>400</code></p></td><td rowspan="1" colspan="1"><p>Bad Request</p></td><td rowspan="1" colspan="1"><p>Noto‘g‘ri request</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>401</code></p></td><td rowspan="1" colspan="1"><p>Unauthorized</p></td><td rowspan="1" colspan="1"><p>Login qilmagan</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>403</code></p></td><td rowspan="1" colspan="1"><p>Forbidden</p></td><td rowspan="1" colspan="1"><p>Ruxsat yo‘q</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>404</code></p></td><td rowspan="1" colspan="1"><p>Not Found</p></td><td rowspan="1" colspan="1"><p>Ma’lumot topilmadi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>409</code></p></td><td rowspan="1" colspan="1"><p>Conflict</p></td><td rowspan="1" colspan="1"><p>Ma’lumot conflict bo‘ldi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>500</code></p></td><td rowspan="1" colspan="1"><p>Internal Server Error</p></td><td rowspan="1" colspan="1"><p>Server ichki xatosi</p></td></tr></tbody></table><hr><h3>23. To‘liq mini REST API</h3><h4><code>UserResponse</code></h4><pre><code class="language-java">public record UserResponse(
        Long id,
        String name,
        Integer age
) {
}</code></pre><h4><code>CreateUserRequest</code></h4><pre><code class="language-java">public record CreateUserRequest(
        String name,
        Integer age
) {
}</code></pre><h4><code>UserController</code></h4><pre><code class="language-java">@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {

    @GetMapping
    public List&lt;UserResponse&gt; getUsers() {
        return List.of(
                new UserResponse(1L, &quot;Ali&quot;, 25),
                new UserResponse(2L, &quot;Vali&quot;, 30)
        );
    }

    @GetMapping(&quot;/{id}&quot;)
    public ResponseEntity&lt;UserResponse&gt; getUser(@PathVariable Long id) {
        UserResponse response = new UserResponse(id, &quot;Ali&quot;, 25);
        return ResponseEntity.ok(response);
    }

    @PostMapping
    public ResponseEntity&lt;UserResponse&gt; createUser(
            @RequestBody CreateUserRequest request
    ) {
        UserResponse response = new UserResponse(3L, request.name(), request.age());

        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(response);
    }

    @PutMapping(&quot;/{id}&quot;)
    public ResponseEntity&lt;UserResponse&gt; updateUser(
            @PathVariable Long id,
            @RequestBody CreateUserRequest request
    ) {
        UserResponse response = new UserResponse(id, request.name(), request.age());
        return ResponseEntity.ok(response);
    }

    @DeleteMapping(&quot;/{id}&quot;)
    public ResponseEntity&lt;Void&gt; deleteUser(@PathVariable Long id) {
        return ResponseEntity.noContent().build();
    }
}</code></pre><hr><h3>24. Postman yoki browser orqali test</h3><h4>Browser bilan</h4><p>Browser faqat <code>GET</code> endpointlarni oson test qiladi:</p><pre><code class="language-text">http://localhost:8080/users
http://localhost:8080/users/1</code></pre><h4>Postman bilan</h4><p><code>POST</code>, <code>PUT</code>, <code>DELETE</code> uchun Postman ishlatish qulay.</p><p>POST request:</p><pre><code class="language-http">POST http://localhost:8080/users
Content-Type: application/json</code></pre><p>Body:</p><pre><code class="language-json">{
  &quot;name&quot;: &quot;Hasan&quot;,
  &quot;age&quot;: 22
}</code></pre><p>Expected response:</p><pre><code class="language-json">{
  &quot;id&quot;: 3,
  &quot;name&quot;: &quot;Hasan&quot;,
  &quot;age&quot;: 22
}</code></pre><p>Status:</p><pre><code class="language-text">201 Created</code></pre><hr><h3>25. <code>curl</code> bilan test</h3><p>Terminalda:</p><pre><code class="language-bash">curl http://localhost:8080/users</code></pre><p>POST:</p><pre><code class="language-bash">curl -X POST http://localhost:8080/users \
  -H &quot;Content-Type: application/json&quot; \
  -d &#039;{&quot;name&quot;:&quot;Hasan&quot;,&quot;age&quot;:22}&#039;</code></pre><p>PUT:</p><pre><code class="language-bash">curl -X PUT http://localhost:8080/users/1 \
  -H &quot;Content-Type: application/json&quot; \
  -d &#039;{&quot;name&quot;:&quot;Ali Updated&quot;,&quot;age&quot;:26}&#039;</code></pre><p>DELETE:</p><pre><code class="language-bash">curl -X DELETE http://localhost:8080/users/1</code></pre><hr><h3>26. Controller → Service → Repository qilish</h3><p>Bo&#039;shlanishiga  controller ichida hamma narsani yozib ketish mumkin, lekin real loyihada bu noto‘g‘ri.</p><p>Yaxshi structure:</p><pre><code class="language-text">controller → service → repository</code></pre><p>Masalan:</p><pre><code class="language-text">UserController request oladi
UserService biznes logika bajaradi
UserRepository data bilan ishlaydi</code></pre><hr><h4><code>UserService</code></h4><pre><code class="language-java">@Service
public class UserService {

    public List&lt;UserResponse&gt; getUsers() {
        return List.of(
                new UserResponse(1L, &quot;Ali&quot;, 25),
                new UserResponse(2L, &quot;Vali&quot;, 30)
        );
    }

    public UserResponse getUser(Long id) {
        return new UserResponse(id, &quot;Ali&quot;, 25);
    }

    public UserResponse createUser(CreateUserRequest request) {
        return new UserResponse(3L, request.name(), request.age());
    }
}</code></pre><h4><code>UserController</code></h4><pre><code class="language-java">@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public List&lt;UserResponse&gt; getUsers() {
        return userService.getUsers();
    }

    @GetMapping(&quot;/{id}&quot;)
    public ResponseEntity&lt;UserResponse&gt; getUser(@PathVariable Long id) {
        return ResponseEntity.ok(userService.getUser(id));
    }

    @PostMapping
    public ResponseEntity&lt;UserResponse&gt; createUser(
            @RequestBody CreateUserRequest request
    ) {
        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(userService.createUser(request));
    }
}</code></pre><p>Bu yaxshiroq, chunki controller faqat request/response bilan ishlaydi.</p><hr><h3>27. Eng ko‘p xatolar</h3><h4>1. <code>@RestController</code> yozmaslik</h4><p>Xato:</p><pre><code class="language-java">public class UserController {
}</code></pre><p>To‘g‘ri:</p><pre><code class="language-java">@RestController
public class UserController {
}</code></pre><hr><h4>2. <code>@RequestBody</code> unutish</h4><p>Xato:</p><pre><code class="language-java">@PostMapping
public String createUser(CreateUserRequest request) {
    return request.name();
}</code></pre><p>To‘g‘ri:</p><pre><code class="language-java">@PostMapping
public String createUser(@RequestBody CreateUserRequest request) {
    return request.name();
}</code></pre><hr><h4>3. <code>Content-Type</code> bermaslik</h4><p>POST request yuborganda header kerak:</p><pre><code class="language-http">Content-Type: application/json</code></pre><p>Aks holda Spring body’ni to‘g‘ri o‘qimasligi mumkin.</p><hr><h4>4. Path nomlari chalkashishi</h4><p>Xato:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public String getUser(@PathVariable Long userId) {
    return userId.toString();
}</code></pre><p>Bu holatda Spring ba’zan <code>id</code> bilan <code>userId</code>ni moslay olmaydi.</p><p>To‘g‘ri:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public String getUser(@PathVariable(&quot;id&quot;) Long userId) {
    return userId.toString();
}</code></pre><hr><h3>28. REST API naming qoidalari</h3><p>Yaxshi endpointlar:</p><pre><code class="language-text">GET    /users
GET    /users/1
POST   /users
PUT    /users/1
DELETE /users/1</code></pre><p>Yomonroq endpointlar:</p><pre><code class="language-text">GET /getUsers
POST /createUser
POST /deleteUser
GET /userDelete</code></pre><p>REST’da odatda fe’l emas, <strong>resource nomi</strong> ishlatiladi:</p><pre><code class="language-text">/users
/products
/orders
/payments</code></pre><p>Amalni HTTP method bildiradi:</p><pre><code class="language-text">GET    → olish
POST   → yaratish
PUT    → yangilash
DELETE → o‘chirish</code></pre><hr><h3>29. Interview uchun javoblar</h3><h4>Savol: <code>@RestController</code> nima?</h4><blockquote><p><code>@RestController</code> Spring’da REST API controller yaratish uchun ishlatiladi. U methoddan qaytgan qiymatni avtomatik HTTP response body qilib qaytaradi.</p></blockquote><h4>Savol: <code>@PathVariable</code> va <code>@RequestParam</code> farqi?</h4><blockquote><p><code>@PathVariable</code> URL path ichidagi qiymatni oladi, masalan <code>/users/1</code>. <code>@RequestParam</code> esa query parameter oladi, masalan <code>/users?name=Ali</code>.</p></blockquote><h4>Savol: <code>@RequestBody</code> nima?</h4><blockquote><p><code>@RequestBody</code> HTTP request body ichidagi JSON’ni Java obyektga convert qilib beradi.</p></blockquote><h4>Savol: <code>ResponseEntity</code> nima uchun kerak?</h4><blockquote><p><code>ResponseEntity</code> response body, HTTP status code va headerlarni boshqarish uchun kerak.</p></blockquote><hr><h3>30. Bo&#039;shlangich java developer uchun xulosa</h3><p>Bugungi mavzudan eslab qoling:</p><pre><code class="language-text">@RestController - REST API controller
@RequestMapping - umumiy path
@GetMapping - ma’lumot olish
@PostMapping - yangi ma’lumot yaratish
@PutMapping - ma’lumot yangilash
@DeleteMapping - ma’lumot o‘chirish
@PathVariable - URL pathdan qiymat olish
@RequestParam - query parameter olish
@RequestBody - JSON body olish
ResponseEntity - status/body/header boshqarish</code></pre><hr><h3>31. Amaliy vazifa</h3><p><code>Product API</code> yoz:</p><pre><code class="language-text">GET    /products
GET    /products/1
POST   /products
PUT    /products/1
DELETE /products/1</code></pre><p>DTO:</p><pre><code class="language-java">public record ProductRequest(
        String name,
        Double price
) {
}</code></pre><p>Response:</p><pre><code class="language-java">public record ProductResponse(
        Long id,
        String name,
        Double price
) {
}</code></pre><p>Controller service orqali ishlasin:</p><pre><code class="language-text">ProductController → ProductService</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Layered architecture]]></title>
            <link>https://blog.jtscorp.uz/layered-architecture</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/layered-architecture</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Spring Boot’da tartibli, tushunarli, kengaytirish oson bo‘lgan backend yozish</strong>.  <strong>Layered architecture</strong>, ya’ni qatlamli arxitektura. Bu mavzu ichida: <code>Controller → Service → Repository</code>, <code>DTO vs Entity</code>, mapping, <code>@Transactional</code>, package-by-layer va package-by-feature bor.</p><hr><h2>1. Layered Architecture nima?</h2><p><strong>Layered architecture</strong> - bu loyihani bir nechta qatlamlarga ajratib yozish usuli.</p><p>Oddiy qilib aytganda:</p><pre><code class="language-text">Client / Frontend / Mobile App
        ↓
Controller
        ↓
Service
        ↓
Repository
        ↓
Database</code></pre><p>Har bir qatlamning o‘z vazifasi bor. Hammasini bitta joyga yozib yubormaymiz.</p><hr><h2>2. Nega Layered Architecture kerak?</h2><p>Agar hamma kodni bitta <code>Controller</code> ichiga yozsak, loyiha tez buziladi.</p><p>Masalan:</p><pre><code class="language-java">@RestController
public class UserController {

    @PostMapping(&quot;/users&quot;)
    public User createUser(@RequestBody User user) {
        // validation
        // business logic
        // database save
        // response qaytarish
    }
}</code></pre><p>Bu yomon uslub. Chunki <code>Controller</code> ichida hamma narsa aralashib ketadi:</p><ul><li><p>HTTP request qabul qilish</p></li><li><p>ma’lumot tekshirish</p></li><li><p>biznes qoida</p></li><li><p>database bilan ishlash</p></li><li><p>response qaytarish</p></li></ul><p>Kichik loyiha bo‘lsa ham ishlaydi. Lekin real loyihada bu kodni tushunish, test qilish, o‘zgartirish qiyinlashadi.</p><hr><h2>3. Asosiy qatlamlar</h2><h3>3.1 Controller layer</h3><p><code>Controller</code> - tashqi dunyo bilan aloqa qiladigan qatlam.</p><p>Frontend, mobile app yoki Postman request yuboradi. Controller shu requestni qabul qiladi.</p><pre><code class="language-java">@RestController
@RequestMapping(&quot;/api/users&quot;)
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public UserResponse create(@RequestBody UserCreateRequest request) {
        return userService.create(request);
    }
}</code></pre><p>Controller vazifasi:</p><ul><li><p>request qabul qilish</p></li><li><p>request body olish</p></li><li><p>path variable olish</p></li><li><p>query param olish</p></li><li><p>service chaqirish</p></li><li><p>response qaytarish</p></li></ul><p>Controller qilmasligi kerak:</p><ul><li><p>databasega to‘g‘ridan-to‘g‘ri murojaat qilmasligi kerak</p></li><li><p>biznes logic yozmasligi kerak</p></li><li><p>murakkab hisob-kitob qilmasligi kerak</p></li></ul><p>Yomon misol:</p><pre><code class="language-java">@PostMapping
public User create(@RequestBody User user) {
    if (user.getAge() &lt; 18) {
        throw new RuntimeException(&quot;User must be adult&quot;);
    }

    return userRepository.save(user);
}</code></pre><p>Bu noto‘g‘ri. Chunki controller ichida business logic va repository ishlatilgan.</p><hr><h3>3.2 Service layer</h3><p><code>Service</code> - biznes logic yoziladigan joy.</p><p>Masalan:</p><ul><li><p>foydalanuvchi yaratish</p></li><li><p>buyurtma hisoblash</p></li><li><p>chegirma qo‘llash</p></li><li><p>balansni tekshirish</p></li><li><p>transaction boshqarish</p></li><li><p>bir nechta repository bilan ishlash</p></li></ul><pre><code class="language-java">@Service
public class UserService {

    private final UserRepository userRepository;
    private final UserMapper userMapper;

    public UserService(UserRepository userRepository, UserMapper userMapper) {
        this.userRepository = userRepository;
        this.userMapper = userMapper;
    }

    public UserResponse create(UserCreateRequest request) {
        if (request.age() &lt; 18) {
            throw new IllegalArgumentException(&quot;User must be adult&quot;);
        }

        User user = userMapper.toEntity(request);
        User savedUser = userRepository.save(user);

        return userMapper.toResponse(savedUser);
    }
}</code></pre><p>Service vazifasi:</p><ul><li><p>business logic</p></li><li><p>tekshiruvlar</p></li><li><p>repository chaqirish</p></li><li><p>transaction boshqarish</p></li><li><p>entity va DTO o‘rtasida ishlash</p></li></ul><p>Service qilmasligi kerak:</p><ul><li><p>HTTP status code bilan ko‘p ishlamasligi kerak</p></li><li><p><code>@RequestBody</code>, <code>@PathVariable</code> kabi web annotationlarga bog‘lanmasligi kerak</p></li><li><p>frontend formatiga haddan tashqari bog‘lanmasligi kerak</p></li></ul><hr><h3>3.3 Repository layer</h3><p><code>Repository</code> - database bilan ishlaydigan qatlam.</p><p>Spring Data JPA’da odatda interface bo‘ladi:</p><pre><code class="language-java">@Repository
public interface UserRepository extends JpaRepository&lt;User, Long&gt; {

    boolean existsByEmail(String email);

    Optional&lt;User&gt; findByEmail(String email);
}</code></pre><p>Repository vazifasi:</p><ul><li><p>databasega yozish</p></li><li><p>database’dan o‘qish</p></li><li><p>query bajarish</p></li><li><p>entity bilan ishlash</p></li></ul><p>Repository qilmasligi kerak:</p><ul><li><p>business logic yozmasligi kerak</p></li><li><p>DTO qaytarmasligi afzal</p></li><li><p>HTTP bilan aloqasi bo‘lmasligi kerak</p></li></ul><hr><h2>4. Entity nima?</h2><p><code>Entity</code> - database jadvaliga mos keladigan Java class.</p><pre><code class="language-java">@Entity
@Table(name = &quot;users&quot;)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String fullName;

    private String email;

    private Integer age;

    // getters/setters
}</code></pre><p>Bu class database bilan bog‘langan.</p><p>Masalan jadval:</p><pre><code class="language-text">users
-----
id
full_name
email
age</code></pre><p>Entity asosan <code>Repository</code> va <code>Service</code> qatlamida ishlatiladi.</p><hr><h2>5. DTO nima?</h2><p>DTO - <strong>Data Transfer Object</strong>.</p><p>Bu client bilan ma’lumot almashish uchun ishlatiladigan class.</p><p>Masalan request DTO:</p><pre><code class="language-java">public record UserCreateRequest(
        String fullName,
        String email,
        Integer age
) {
}</code></pre><p>Response DTO:</p><pre><code class="language-java">public record UserResponse(
        Long id,
        String fullName,
        String email
) {
}</code></pre><p>Bu yerda <code>age</code> response’da qaytmayapti. Chunki clientga hamma ma’lumotni berish shart emas.</p><hr><h2>6. DTO vs Entity - nima uchun ajratamiz?</h2><h3>Entity’ni to‘g‘ridan-to‘g‘ri API’da ishlatish yomon</h3><p>Yomon misol:</p><pre><code class="language-java">@PostMapping
public User create(@RequestBody User user) {
    return userRepository.save(user);
}</code></pre><p>Nega yomon?</p><p>Chunki <code>User</code> entity ichida databasega oid fieldlar bor. Masalan:</p><pre><code class="language-java">private String password;
private Boolean deleted;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;</code></pre><p>Agar entity’ni response qilib qaytarsangiz, noxush holat bo‘lishi mumkin:</p><pre><code class="language-json">{
  &quot;id&quot;: 1,
  &quot;fullName&quot;: &quot;Ali Valiyev&quot;,
  &quot;email&quot;: &quot;ali@mail.com&quot;,
  &quot;password&quot;: &quot;123456&quot;,
  &quot;deleted&quot;: false
}</code></pre><p>Bu xavfli.</p><p>Shuning uchun:</p><pre><code class="language-text">Request DTO → Service → Entity → Repository
Repository → Entity → Service → Response DTO</code></pre><hr><h2>7. Mapping nima?</h2><p>Mapping - DTO’dan Entity yasash yoki Entity’dan DTO yasash.</p><p>Masalan manual mapping:</p><pre><code class="language-java">@Component
public class UserMapper {

    public User toEntity(UserCreateRequest request) {
        User user = new User();
        user.setFullName(request.fullName());
        user.setEmail(request.email());
        user.setAge(request.age());
        return user;
    }

    public UserResponse toResponse(User user) {
        return new UserResponse(
                user.getId(),
                user.getFullName(),
                user.getEmail()
        );
    }
}</code></pre><p>Bu oddiy va tushunarli usul.</p><p>Katta loyihalarda <code>MapStruct</code> ishlatiladi.</p><hr><h2>8. To‘liq oqim qanday ishlaydi?</h2><p>Masalan frontend request yuboradi:</p><pre><code class="language-http">POST /api/users
Content-Type: application/json

{
  &quot;fullName&quot;: &quot;Ali Valiyev&quot;,
  &quot;email&quot;: &quot;ali@mail.com&quot;,
  &quot;age&quot;: 22
}</code></pre><p>Spring Boot’da oqim:</p><pre><code class="language-text">1. UserController requestni oladi
2. UserController UserService.create() chaqiradi
3. UserService biznes qoidani tekshiradi
4. UserService DTO’ni Entity’ga aylantiradi
5. UserRepository databasega saqlaydi
6. Service Entity’ni Response DTO’ga aylantiradi
7. Controller response qaytaradi</code></pre><p>Natija:</p><pre><code class="language-json">{
  &quot;id&quot;: 1,
  &quot;fullName&quot;: &quot;Ali Valiyev&quot;,
  &quot;email&quot;: &quot;ali@mail.com&quot;
}</code></pre><hr><h2>9. <code>@Transactional</code> qayerga qo‘yiladi?</h2><p><code>@Transactional</code> odatda <strong>Service layer</strong>ga qo‘yiladi.</p><p>Masalan:</p><pre><code class="language-java">@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final PaymentRepository paymentRepository;

    public OrderService(OrderRepository orderRepository,
                        PaymentRepository paymentRepository) {
        this.orderRepository = orderRepository;
        this.paymentRepository = paymentRepository;
    }

    @Transactional
    public void createOrder(CreateOrderRequest request) {
        orderRepository.save(new Order(...));
        paymentRepository.save(new Payment(...));
    }
}</code></pre><p>Nega service layer?</p><p>Chunki transaction odatda bitta repository emas, butun business operation uchun kerak bo‘ladi.</p><p>Masalan:</p><pre><code class="language-text">Order save bo‘ldi
Payment save bo‘lmadi</code></pre><p>Bunday holatda hammasi rollback bo‘lishi kerak. Ya’ni order ham saqlanmasligi kerak.</p><p>Shu sabab <code>@Transactional</code> repositoryga emas, service methodga qo‘yiladi.</p><hr><h2>10. Package-by-layer</h2><p>Bu eng oddiy struktura:</p><pre><code class="language-text">src/main/java/com/example/demo
 ├── controller
 │    └── UserController.java
 ├── service
 │    └── UserService.java
 ├── repository
 │    └── UserRepository.java
 ├── entity
 │    └── User.java
 ├── dto
 │    ├── UserCreateRequest.java
 │    └── UserResponse.java
 └── mapper
      └── UserMapper.java</code></pre><p>Bu boshlang&#039;ich uchun yaxshi boshlanish.</p><p>Afzalligi:</p><ul><li><p>tushunish oson</p></li><li><p>Spring Boot tutoriallarda ko‘p uchraydi</p></li><li><p>kichik loyihalar uchun qulay</p></li></ul><p>Kamchiligi:</p><ul><li><p>loyiha kattalashsa, fayllar juda ko‘payadi</p></li><li><p><code>user</code>, <code>order</code>, <code>product</code> kodlari har xil papkalarga sochilib ketadi</p></li></ul><hr><h2>11. Package-by-feature</h2><p>Bu real loyihalarda ko‘proq qulay bo‘ladi.</p><pre><code class="language-text">src/main/java/com/example/demo
 ├── user
 │    ├── UserController.java
 │    ├── UserService.java
 │    ├── UserRepository.java
 │    ├── User.java
 │    ├── UserCreateRequest.java
 │    ├── UserResponse.java
 │    └── UserMapper.java
 ├── order
 │    ├── OrderController.java
 │    ├── OrderService.java
 │    ├── OrderRepository.java
 │    └── Order.java
 └── product
      ├── ProductController.java
      ├── ProductService.java
      ├── ProductRepository.java
      └── Product.java</code></pre><p>Afzalligi:</p><ul><li><p>bitta feature’ga oid kod bitta joyda turadi</p></li><li><p>katta loyihada qulay</p></li><li><p>domain bo‘yicha tartibli bo‘ladi</p></li></ul><p>Java developer sifatida ikkalasini bilish kerak.</p><p>Amaliy maslahat:</p><pre><code class="language-text">Kichik o‘quv loyiha → package-by-layer
Real loyiha yoki katta loyiha → package-by-feature</code></pre><hr><h2>12. To‘liq mini misol</h2><h3>Entity</h3><pre><code class="language-java">@Entity
@Table(name = &quot;users&quot;)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String fullName;

    private String email;

    private Integer age;

    // getters/setters
}</code></pre><h3>DTO</h3><pre><code class="language-java">public record UserCreateRequest(
        String fullName,
        String email,
        Integer age
) {
}</code></pre><pre><code class="language-java">public record UserResponse(
        Long id,
        String fullName,
        String email
) {
}</code></pre><h3>Repository</h3><pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {

    boolean existsByEmail(String email);
}</code></pre><h3>Mapper</h3><pre><code class="language-java">@Component
public class UserMapper {

    public User toEntity(UserCreateRequest request) {
        User user = new User();
        user.setFullName(request.fullName());
        user.setEmail(request.email());
        user.setAge(request.age());
        return user;
    }

    public UserResponse toResponse(User user) {
        return new UserResponse(
                user.getId(),
                user.getFullName(),
                user.getEmail()
        );
    }
}</code></pre><h3>Service</h3><pre><code class="language-java">@Service
public class UserService {

    private final UserRepository userRepository;
    private final UserMapper userMapper;

    public UserService(UserRepository userRepository,
                       UserMapper userMapper) {
        this.userRepository = userRepository;
        this.userMapper = userMapper;
    }

    @Transactional
    public UserResponse create(UserCreateRequest request) {
        if (userRepository.existsByEmail(request.email())) {
            throw new IllegalArgumentException(&quot;Email already exists&quot;);
        }

        if (request.age() &lt; 18) {
            throw new IllegalArgumentException(&quot;User must be adult&quot;);
        }

        User user = userMapper.toEntity(request);
        User savedUser = userRepository.save(user);

        return userMapper.toResponse(savedUser);
    }
}</code></pre><h3>Controller</h3><pre><code class="language-java">@RestController
@RequestMapping(&quot;/api/users&quot;)
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public UserResponse create(@RequestBody UserCreateRequest request) {
        return userService.create(request);
    }
}</code></pre><hr><h2>13. Eng ko‘p qilinadigan xatolar</h2><h3>Xato 1: Controller’dan repository chaqirish</h3><pre><code class="language-java">@RestController
public class UserController {

    private final UserRepository userRepository;

    @PostMapping(&quot;/users&quot;)
    public User create(@RequestBody User user) {
        return userRepository.save(user);
    }
}</code></pre><p>To‘g‘risi:</p><pre><code class="language-text">Controller → Service → Repository</code></pre><hr><h3>Xato 2: Entity’ni response qilib qaytarish</h3><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public User getById(@PathVariable Long id) {
    return userRepository.findById(id).orElseThrow();
}</code></pre><p>To‘g‘risi:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public UserResponse getById(@PathVariable Long id) {
    return userService.getById(id);
}</code></pre><hr><h3>Xato 3: Service ichida HTTP bilan ishlash</h3><p>Yomon:</p><pre><code class="language-java">public ResponseEntity&lt;UserResponse&gt; create(UserCreateRequest request) {
    ...
}</code></pre><p>Service <code>ResponseEntity</code> qaytarmagani yaxshi. <code>ResponseEntity</code> - web/controller tomonga yaqin narsa.</p><p>Yaxshiroq:</p><pre><code class="language-java">public UserResponse create(UserCreateRequest request) {
    ...
}</code></pre><hr><h2>14. Qisqa qoida</h2><p>Esda saqlash uchun:</p><pre><code class="language-text">Controller - HTTP bilan ishlaydi
Service - biznes logic bilan ishlaydi
Repository - database bilan ishlaydi
Entity - database modeli
DTO - API modeli
Mapper - DTO va Entity o‘rtasida aylantiradi</code></pre><hr><h2>15. Javachi uchun vazifa</h2><p>Bitta kichik loyiha yozing:</p><pre><code class="language-text">Product CRUD API</code></pre><p>Kerakli endpointlar:</p><pre><code class="language-http">POST   /api/products
GET    /api/products
GET    /api/products/{id}
PUT    /api/products/{id}
DELETE /api/products/{id}</code></pre><p>Struktura:</p><pre><code class="language-text">product
 ├── Product.java
 ├── ProductRepository.java
 ├── ProductService.java
 ├── ProductController.java
 ├── ProductCreateRequest.java
 ├── ProductUpdateRequest.java
 ├── ProductResponse.java
 └── ProductMapper.java</code></pre><p>Product fieldlari:</p><pre><code class="language-text">id
name
price
quantity</code></pre><p>Asosiy qoida:</p><pre><code class="language-text">Controller repository’ni chaqirmasin.
Entity response sifatida qaytmasin.
Business logic service ichida bo‘lsin.</code></pre><hr><h2>Xulosa</h2><p><strong>Layered architecture</strong> - Spring Boot developer uchun eng muhim mavzulardan biri.</p><p>Bu arxitektura kodni tartibli qiladi:</p><pre><code class="language-text">Controller → Service → Repository → Database</code></pre><p>DTO va Entity’ni ajratish esa xavfsizlik, tozalik va kelajakda kengaytirish uchun juda muhim.</p>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Spring Data JPA chuqurroq]]></title>
            <link>https://blog.jtscorp.uz/spring-data-jpa-chuqurroq</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/spring-data-jpa-chuqurroq</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p>Bu mavzuda muhim qism: real database bilan ishlash, query yozish, pagination, sorting va entity relationlarni tushunish. Bu bo‘lim ichida quyidagilar bor: <strong>Derived query methods</strong>, <code>@Query</code>, <strong>Pagination &amp; Sorting</strong>, <strong>One-to-One</strong>, <strong>One-to-Many</strong>, <strong>Many-to-Many</strong>, <code>CascadeType</code>, <code>FetchType</code>, <code>@ManyToOne join columns</code>.</p><hr><h2>1. Spring Data JPA nima?</h2><p><strong>Spring Data JPA</strong> - database bilan ishlashni osonlashtiradigan Spring moduli.</p><p>Oddiy qilib:</p><pre><code class="language-text">Java object  →  JPA/Hibernate  →  SQL  →  Database</code></pre><p>Siz ko‘p hollarda SQL yozmaysiz. Spring Data JPA siz uchun query yaratadi.</p><p>Masalan:</p><pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
}</code></pre><p>Shu bitta interface orqali sizda avtomatik CRUD methodlar paydo bo‘ladi:</p><pre><code class="language-java">save()
findById()
findAll()
deleteById()
existsById()
count()</code></pre><hr><h2>2. Entity va Repository eslatma</h2><h3>Entity</h3><pre><code class="language-java">@Entity
@Table(name = &quot;users&quot;)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String fullName;

    private String email;

    private Integer age;

    // getters/setters
}</code></pre><p>Bu <code>users</code> jadvaliga mos class.</p><hr><h3>Repository</h3><pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
}</code></pre><p>Bu yerda:</p><pre><code class="language-java">JpaRepository&lt;User, Long&gt;</code></pre><p>ma’nosi:</p><pre><code class="language-text">User - qaysi entity bilan ishlaydi
Long - entity id tipi</code></pre><hr><h2>3. Built-in CRUD methodlar</h2><p>Spring Data JPA sizga tayyor methodlar beradi.</p><h3>Saqlash</h3><pre><code class="language-java">User user = new User();
user.setFullName(&quot;Ali Valiyev&quot;);
user.setEmail(&quot;ali@mail.com&quot;);
user.setAge(22);

userRepository.save(user);</code></pre><p>Agar <code>id == null</code> bo‘lsa - yangi record yaratadi.</p><p>Agar <code>id</code> mavjud bo‘lsa - update qiladi.</p><hr><h3>ID orqali topish</h3><pre><code class="language-java">User user = userRepository.findById(1L)
        .orElseThrow(() -&gt; new RuntimeException(&quot;User not found&quot;));</code></pre><p><code>findById()</code> <code>Optional&lt;User&gt;</code> qaytaradi.</p><p>Yomon uslub:</p><pre><code class="language-java">User user = userRepository.findById(1L).get();</code></pre><p>Nega yomon? Agar user topilmasa, <code>NoSuchElementException</code> chiqadi.</p><p>Yaxshiroq:</p><pre><code class="language-java">User user = userRepository.findById(1L)
        .orElseThrow(() -&gt; new RuntimeException(&quot;User not found&quot;));</code></pre><hr><h3>Hammasini olish</h3><pre><code class="language-java">List&lt;User&gt; users = userRepository.findAll();</code></pre><p>Lekin katta jadvalda ehtiyot bo‘lish kerak. Agar jadvalda 1 million data bo‘lsa, <code>findAll()</code> hammasini RAM’ga olib keladi.</p><p>Real loyihada pagination ishlatiladi.</p><hr><h3>O‘chirish</h3><pre><code class="language-java">userRepository.deleteById(1L);</code></pre><p>Yoki:</p><pre><code class="language-java">userRepository.delete(user);</code></pre><hr><h2>4. Derived Query Methods</h2><p><strong>Derived query</strong> - method nomiga qarab Spring o‘zi query yaratadi.</p><p>Masalan:</p><pre><code class="language-java">Optional&lt;User&gt; findByEmail(String email);</code></pre><p>Spring buni taxminan shunday SQLga aylantiradi:</p><pre><code class="language-sql">SELECT * FROM users WHERE email = ?</code></pre><hr><h3>Eng ko‘p ishlatiladiganlari</h3><pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {

    Optional&lt;User&gt; findByEmail(String email);

    List&lt;User&gt; findByAge(Integer age);

    List&lt;User&gt; findByAgeGreaterThan(Integer age);

    List&lt;User&gt; findByAgeLessThan(Integer age);

    boolean existsByEmail(String email);

    long countByAgeGreaterThan(Integer age);

    void deleteByEmail(String email);
}</code></pre><hr><h3><code>And</code>, <code>Or</code></h3><pre><code class="language-java">List&lt;User&gt; findByFullNameAndAge(String fullName, Integer age);</code></pre><p>SQLga yaqin ma’nosi:</p><pre><code class="language-sql">SELECT * FROM users
WHERE full_name = ? AND age = ?</code></pre><p>Yana:</p><pre><code class="language-java">List&lt;User&gt; findByFullNameOrEmail(String fullName, String email);</code></pre><hr><h3><code>Containing</code>, <code>StartsWith</code>, <code>EndsWith</code></h3><pre><code class="language-java">List&lt;User&gt; findByFullNameContaining(String text);</code></pre><p>Bu <code>LIKE</code> queryga o‘xshaydi:</p><pre><code class="language-sql">WHERE full_name LIKE &#039;%text%&#039;</code></pre><p>Misollar:</p><pre><code class="language-java">List&lt;User&gt; findByFullNameStartingWith(String prefix);

List&lt;User&gt; findByFullNameEndingWith(String suffix);</code></pre><hr><h3><code>IgnoreCase</code></h3><pre><code class="language-java">Optional&lt;User&gt; findByEmailIgnoreCase(String email);</code></pre><p>Masalan:</p><pre><code class="language-text">ALI@MAIL.COM
ali@mail.com
Ali@Mail.Com</code></pre><p>bularni bir xil deb qidirishi mumkin.</p><hr><h3><code>OrderBy</code></h3><pre><code class="language-java">List&lt;User&gt; findByAgeGreaterThanOrderByFullNameAsc(Integer age);</code></pre><p>Ma’nosi:</p><pre><code class="language-sql">WHERE age &gt; ?
ORDER BY full_name ASC</code></pre><p>Yana:</p><pre><code class="language-java">List&lt;User&gt; findByAgeGreaterThanOrderByCreatedAtDesc(Integer age);</code></pre><hr><h2>5. Derived query haddan oshsa yomonlashadi</h2><p>Bu method o‘qilishi qiyin:</p><pre><code class="language-java">List&lt;User&gt; findByFullNameContainingAndAgeGreaterThanAndEmailEndingWithOrderByCreatedAtDesc(
        String name,
        Integer age,
        String emailDomain
);</code></pre><p>Bunday holatda <code>@Query</code> ishlatgan yaxshi.</p><hr><h2>6. <code>@Query</code> nima?</h2><p><code>@Query</code> - query’ni o‘zingiz yozishingiz uchun ishlatiladi.</p><p>Ikki turi bor:</p><pre><code class="language-text">JPQL query
Native SQL query</code></pre><hr><h2>7. JPQL query</h2><p>JPQL database jadval nomi bilan emas, <strong>Entity nomi</strong> bilan ishlaydi.</p><p>Entity:</p><pre><code class="language-java">@Entity
public class User {
    private Long id;
    private String fullName;
    private String email;
    private Integer age;
}</code></pre><p>Repository:</p><pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {

    @Query(&quot;select u from User u where u.age &gt;= :age&quot;)
    List&lt;User&gt; findAdults(@Param(&quot;age&quot;) Integer age);
}</code></pre><p>Bu yerda <code>User</code> - jadval emas, entity class nomi.</p><p><code>u.age</code> - column emas, Java field nomi.</p><hr><h3>JPQL’da DTO qaytarish</h3><p>Masalan response uchun faqat kerakli fieldlarni olish:</p><pre><code class="language-java">public record UserShortResponse(
        Long id,
        String fullName
) {
}</code></pre><p>Repository:</p><pre><code class="language-java">@Query(&quot;&quot;&quot;
       select new com.example.demo.user.UserShortResponse(u.id, u.fullName)
       from User u
       where u.age &gt;= :age
       &quot;&quot;&quot;)
List&lt;UserShortResponse&gt; findAdultUsers(@Param(&quot;age&quot;) Integer age);</code></pre><p>Bu usul katta entity’ni to‘liq olib kelmaslik uchun foydali.</p><hr><h2>8. Native SQL query</h2><p>Native query - to‘g‘ridan-to‘g‘ri SQL yozish.</p><pre><code class="language-java">@Query(value = &quot;select * from users where age &gt;= :age&quot;, nativeQuery = true)
List&lt;User&gt; findAdultsNative(@Param(&quot;age&quot;) Integer age);</code></pre><p>Qachon kerak?</p><ul><li><p>murakkab SQL bo‘lsa</p></li><li><p>database-specific function ishlatilsa</p></li><li><p>performance uchun maxsus query yozish kerak bo‘lsa</p></li><li><p>window function, CTE ishlatilsa</p></li></ul><p>Lekin har doim native query yozish shart emas. Oddiy holatda derived query yoki JPQL yetadi.</p><hr><h2>9. Pagination nima?</h2><p>Pagination - data’ni sahifalab olish.</p><p>Masalan hamma userlarni birdan olish yomon:</p><pre><code class="language-java">List&lt;User&gt; users = userRepository.findAll();</code></pre><p>Agar 500 000 ta user bo‘lsa, server qiynaladi.</p><p>Shuning uchun:</p><pre><code class="language-text">1-sahifa: 20 ta user
2-sahifa: 20 ta user
3-sahifa: 20 ta user</code></pre><hr><h3>Pageable bilan ishlash</h3><p>Repository:</p><pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {

    Page&lt;User&gt; findByAgeGreaterThan(Integer age, Pageable pageable);
}</code></pre><p>Service:</p><pre><code class="language-java">public Page&lt;UserResponse&gt; getUsers(int page, int size) {
    Pageable pageable = PageRequest.of(page, size, Sort.by(&quot;id&quot;).descending());

    return userRepository.findAll(pageable)
            .map(userMapper::toResponse);
}</code></pre><p>Controller:</p><pre><code class="language-java">@GetMapping
public Page&lt;UserResponse&gt; getUsers(
        @RequestParam(defaultValue = &quot;0&quot;) int page,
        @RequestParam(defaultValue = &quot;20&quot;) int size
) {
    return userService.getUsers(page, size);
}</code></pre><p>Request:</p><pre><code class="language-http">GET /api/users?page=0&amp;size=20</code></pre><hr><h2>10. <code>Page</code>, <code>Slice</code>, <code>List</code> farqi</h2><h3><code>Page</code></h3><pre><code class="language-java">Page&lt;User&gt; users = userRepository.findAll(pageable);</code></pre><p><code>Page</code> qo‘shimcha ma’lumot beradi:</p><pre><code class="language-text">content
totalElements
totalPages
pageNumber
pageSize
first
last</code></pre><p>Kamchiligi: ko‘pincha qo‘shimcha <code>count query</code> ishlaydi.</p><hr><h3><code>Slice</code></h3><pre><code class="language-java">Slice&lt;User&gt; users = userRepository.findByAgeGreaterThan(18, pageable);</code></pre><p><code>Slice</code> faqat keyingi sahifa bor-yo‘qligini biladi:</p><pre><code class="language-text">content
hasNext
hasPrevious</code></pre><p><code>totalElements</code> va <code>totalPages</code> yo‘q.</p><p>Katta data uchun <code>Slice</code> ba’zan yengilroq bo‘ladi.</p><hr><h3><code>List</code></h3><pre><code class="language-java">List&lt;User&gt; users = userRepository.findByAgeGreaterThan(18, pageable);</code></pre><p>Faqat data qaytaradi. Pagination metadata yo‘q.</p><hr><h2>11. Sorting</h2><p>Sorting - tartiblash.</p><pre><code class="language-java">Sort sort = Sort.by(&quot;fullName&quot;).ascending();
List&lt;User&gt; users = userRepository.findAll(sort);</code></pre><p>Yoki:</p><pre><code class="language-java">Sort sort = Sort.by(&quot;createdAt&quot;).descending();</code></pre><p>Pagination bilan:</p><pre><code class="language-java">Pageable pageable = PageRequest.of(
        0,
        20,
        Sort.by(&quot;createdAt&quot;).descending()
);</code></pre><p>Ko‘p field bo‘yicha sorting:</p><pre><code class="language-java">Sort sort = Sort.by(&quot;age&quot;).descending()
        .and(Sort.by(&quot;fullName&quot;).ascending());</code></pre><hr><h2>12. Entity relationshiplar</h2><p>Real loyihada entitylar bir-biri bilan bog‘langan bo‘ladi.</p><p>Masalan:</p><pre><code class="language-text">User → Orders
Order → Product
Product → Category</code></pre><p>JPA’da asosiy relationshiplar:</p><pre><code class="language-text">@OneToOne
@OneToMany
@ManyToOne
@ManyToMany</code></pre><hr><h2>13. <code>@ManyToOne</code></h2><p>Eng ko‘p ishlatiladigan relationlardan biri.</p><p>Masalan:</p><pre><code class="language-text">Bitta userda ko‘p order bo‘lishi mumkin.
Bitta order faqat bitta userga tegishli.</code></pre><p>Bu yerda <code>Order → User</code> munosabati <code>ManyToOne</code>.</p><pre><code class="language-java">@Entity
@Table(name = &quot;orders&quot;)
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private BigDecimal totalPrice;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;user_id&quot;)
    private User user;
}</code></pre><p>Database’da:</p><pre><code class="language-text">orders
------
id
total_price
user_id</code></pre><p><code>user_id</code> - foreign key.</p><hr><h3><code>@JoinColumn</code></h3><pre><code class="language-java">@JoinColumn(name = &quot;user_id&quot;)
private User user;</code></pre><p>Bu <code>orders</code> jadvalidagi <code>user_id</code> column <code>users.id</code> bilan bog‘lanishini bildiradi.</p><hr><h2>14. <code>@OneToMany</code></h2><p><code>User</code> tomondan qarasak:</p><pre><code class="language-text">Bitta userda ko‘p order bor.</code></pre><pre><code class="language-java">@Entity
@Table(name = &quot;users&quot;)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String fullName;

    @OneToMany(mappedBy = &quot;user&quot;)
    private List&lt;Order&gt; orders = new ArrayList&lt;&gt;();
}</code></pre><p>Bu yerda:</p><pre><code class="language-java">mappedBy = &quot;user&quot;</code></pre><p>ma’nosi: relation egasi <code>Order</code> entity ichidagi <code>user</code> field.</p><hr><h3>Muhim qoida</h3><p>Ko‘pincha <code>@ManyToOne</code> tomoni relation egasi bo‘ladi.</p><p>Ya’ni foreign key qaysi jadvalda bo‘lsa, relation egasi ham odatda o‘sha tomonda.</p><pre><code class="language-text">orders.user_id bor
demak relation egasi Order.user</code></pre><hr><h2>15. <code>@OneToOne</code></h2><p>Masalan:</p><pre><code class="language-text">User - UserProfile
Bitta userda bitta profile bor.
Bitta profile bitta userga tegishli.</code></pre><pre><code class="language-java">@Entity
public class UserProfile {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String bio;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;user_id&quot;)
    private User user;
}</code></pre><p>Yoki <code>User</code> tomonda:</p><pre><code class="language-java">@OneToOne(mappedBy = &quot;user&quot;)
private UserProfile profile;</code></pre><hr><h2>16. <code>@ManyToMany</code></h2><p>Masalan:</p><pre><code class="language-text">Student ko‘p Course olishi mumkin.
Course’da ko‘p Student bo‘lishi mumkin.</code></pre><pre><code class="language-java">@Entity
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String fullName;

    @ManyToMany
    @JoinTable(
            name = &quot;student_courses&quot;,
            joinColumns = @JoinColumn(name = &quot;student_id&quot;),
            inverseJoinColumns = @JoinColumn(name = &quot;course_id&quot;)
    )
    private List&lt;Course&gt; courses = new ArrayList&lt;&gt;();
}</code></pre><pre><code class="language-java">@Entity
public class Course {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
}</code></pre><p>Database’da oraliq jadval bo‘ladi:</p><pre><code class="language-text">student_courses
---------------
student_id
course_id</code></pre><hr><h3>Real loyihada <code>@ManyToMany</code>dan ehtiyot bo‘ling</h3><p>Ko‘p real holatda <code>@ManyToMany</code> o‘rniga alohida entity ishlatgan yaxshi.</p><p>Masalan:</p><pre><code class="language-text">StudentCourse</code></pre><p>Nega?</p><p>Chunki oraliq jadvalda qo‘shimcha fieldlar bo‘lishi mumkin:</p><pre><code class="language-text">student_id
course_id
joined_at
status
grade</code></pre><p>Shunda bunday yoziladi:</p><pre><code class="language-java">@Entity
public class StudentCourse {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Student student;

    @ManyToOne(fetch = FetchType.LAZY)
    private Course course;

    private LocalDateTime joinedAt;

    private String status;
}</code></pre><p>Bu usul real loyihada ancha nazoratli.</p><hr><h2>17. <code>FetchType.LAZY</code> va <code>FetchType.EAGER</code></h2><p>Bu juda muhim.</p><h3>LAZY</h3><p><code>LAZY</code> - bog‘langan entity kerak bo‘lganda yuklanadi.</p><pre><code class="language-java">@ManyToOne(fetch = FetchType.LAZY)
private User user;</code></pre><p>Masalan orderni oldingiz:</p><pre><code class="language-java">Order order = orderRepository.findById(1L).orElseThrow();</code></pre><p>Bu paytda <code>user</code> hali yuklanmagan bo‘lishi mumkin.</p><p>Agar keyin chaqirsangiz:</p><pre><code class="language-java">order.getUser().getFullName();</code></pre><p>shunda user yuklanadi.</p><hr><h3>EAGER</h3><p><code>EAGER</code> - bog‘langan entity darhol yuklanadi.</p><pre><code class="language-java">@ManyToOne(fetch = FetchType.EAGER)
private User user;</code></pre><p>Orderni olsangiz, user ham birga keladi.</p><p>Bu qulay ko‘rinadi, lekin katta loyihada performance muammo keltiradi.</p><hr><h3>Amaliy maslahat</h3><p>Ko‘pincha shunday ishlatiladi:</p><pre><code class="language-java">@ManyToOne(fetch = FetchType.LAZY)
@OneToOne(fetch = FetchType.LAZY)
@OneToMany(fetch = FetchType.LAZY)
@ManyToMany(fetch = FetchType.LAZY)</code></pre><p>Ya’ni defaultga ishonib ketmasdan, <code>LAZY</code>ni aniq yozish yaxshi.</p><hr><h2>18. N+1 problem</h2><p>Bu JPA’da eng mashhur performance muammolardan biri.</p><p>Tasavvur qiling:</p><pre><code class="language-java">List&lt;Order&gt; orders = orderRepository.findAll();

for (Order order : orders) {
    System.out.println(order.getUser().getFullName());
}</code></pre><p>Agar 100 ta order bo‘lsa:</p><pre><code class="language-text">1 ta query - orders olish uchun
100 ta query - har bir order userini olish uchun</code></pre><p>Jami:</p><pre><code class="language-text">101 query</code></pre><p>Bu <strong>N+1 problem</strong> deyiladi.</p><hr><h3>Yechim: fetch join</h3><pre><code class="language-java">@Query(&quot;&quot;&quot;
       select o from Order o
       join fetch o.user
       &quot;&quot;&quot;)
List&lt;Order&gt; findAllWithUser();</code></pre><p>Bu order va userni bitta query bilan olib keladi.</p><hr><h3>Yechim: EntityGraph</h3><pre><code class="language-java">@EntityGraph(attributePaths = &quot;user&quot;)
List&lt;Order&gt; findAll();</code></pre><p>Bu ham userni oldindan yuklab beradi.</p><hr><h2>19. <code>CascadeType</code></h2><p><code>Cascade</code> - bir entityga qilingan amal bog‘langan entityga ham ta’sir qilish-qilmasligini belgilaydi.</p><p>Masalan:</p><pre><code class="language-java">@OneToMany(mappedBy = &quot;user&quot;, cascade = CascadeType.ALL)
private List&lt;Order&gt; orders = new ArrayList&lt;&gt;();</code></pre><p>Agar user saqlansa, orderlar ham saqlanishi mumkin.</p><p>Agar user o‘chirilsa, orderlar ham o‘chishi mumkin.</p><hr><h3>Cascade turlari</h3><pre><code class="language-text">PERSIST  - save qilganda bog‘langan entity ham save bo‘ladi
MERGE    - update qilganda bog‘langan entity ham update bo‘ladi
REMOVE   - delete qilganda bog‘langan entity ham delete bo‘ladi
REFRESH  - refresh amalini uzatadi
DETACH   - detach amalini uzatadi
ALL      - hammasi</code></pre><hr><h3>Xavfli joyi</h3><pre><code class="language-java">@ManyToOne(cascade = CascadeType.ALL)
private User user;</code></pre><p>Bu xavfli bo‘lishi mumkin.</p><p>Masalan orderni o‘chirganda user ham o‘chib ketishi mumkin.</p><p>Amaliy qoida:</p><pre><code class="language-text">@ManyToOne tomonda CascadeType.ALL ishlatishga shoshilmang.</code></pre><p><code>CascadeType.REMOVE</code>ni ham ehtiyot ishlating.</p><hr><h2>20. <code>orphanRemoval</code></h2><p><code>orphanRemoval = true</code> - parentdan olib tashlangan child database’dan ham o‘chadi.</p><pre><code class="language-java">@OneToMany(
        mappedBy = &quot;user&quot;,
        cascade = CascadeType.ALL,
        orphanRemoval = true
)
private List&lt;Order&gt; orders = new ArrayList&lt;&gt;();</code></pre><p>Masalan:</p><pre><code class="language-java">user.getOrders().remove(order);</code></pre><p>Shunda <code>order</code> database’dan ham o‘chishi mumkin.</p><p>Bu kuchli feature, lekin ehtiyot ishlatish kerak.</p><hr><h2>21. Transaction bilan relationlar</h2><p>Relationlar bilan ishlaganda <code>@Transactional</code> muhim.</p><p>Masalan:</p><pre><code class="language-java">@Transactional
public OrderResponse createOrder(Long userId, CreateOrderRequest request) {
    User user = userRepository.findById(userId)
            .orElseThrow(() -&gt; new RuntimeException(&quot;User not found&quot;));

    Order order = new Order();
    order.setUser(user);
    order.setTotalPrice(request.totalPrice());

    Order savedOrder = orderRepository.save(order);

    return orderMapper.toResponse(savedOrder);
}</code></pre><p>Nega service’da <code>@Transactional</code> yaxshi?</p><p>Chunki bitta business operation ichida:</p><pre><code class="language-text">Userni topish
Order yaratish
Orderni saqlash
Response tayyorlash</code></pre><p>hammasi bir transaction doirasida bo‘ladi.</p><hr><h2>22. Update qilishda ehtiyot bo‘lish</h2><p>Yomonroq usul:</p><pre><code class="language-java">public User update(Long id, User request) {
    request.setId(id);
    return userRepository.save(request);
}</code></pre><p>Bu xavfli. Chunki request ichida kelmagan fieldlar <code>null</code> bo‘lib qolishi mumkin.</p><p>Yaxshiroq:</p><pre><code class="language-java">@Transactional
public UserResponse update(Long id, UserUpdateRequest request) {
    User user = userRepository.findById(id)
            .orElseThrow(() -&gt; new RuntimeException(&quot;User not found&quot;));

    user.setFullName(request.fullName());
    user.setAge(request.age());

    return userMapper.toResponse(user);
}</code></pre><p>Bu yerda <code>save()</code> chaqirmasangiz ham bo‘ladi, chunki entity transaction ichida managed holatda. Hibernate o‘zgarishni kuzatadi va transaction oxirida update qiladi.</p><p>Bu <strong>dirty checking</strong> deyiladi.</p><hr><h2>23. Dirty Checking nima?</h2><p>Dirty checking - Hibernate entitydagi o‘zgarishlarni avtomatik aniqlashi.</p><pre><code class="language-java">@Transactional
public void changeEmail(Long userId, String newEmail) {
    User user = userRepository.findById(userId)
            .orElseThrow();

    user.setEmail(newEmail);
}</code></pre><p>Bu yerda <code>save()</code> yo‘q.</p><p>Lekin transaction tugaganda Hibernate taxminan shunday SQL bajaradi:</p><pre><code class="language-sql">UPDATE users SET email = ? WHERE id = ?</code></pre><p>Bu faqat entity managed holatda va transaction ichida bo‘lsa ishlaydi.</p><hr><h2>24. DTO projection</h2><p>Ba’zan entityni to‘liq olish kerak emas.</p><p>Masalan faqat:</p><pre><code class="language-text">id
fullName
email</code></pre><p>kerak.</p><p>Interface projection:</p><pre><code class="language-java">public interface UserView {
    Long getId();
    String getFullName();
    String getEmail();
}</code></pre><p>Repository:</p><pre><code class="language-java">List&lt;UserView&gt; findByAgeGreaterThan(Integer age);</code></pre><p>Bu response uchun yengilroq bo‘lishi mumkin.</p><hr><h2>25. Amaliy Product + Category misol</h2><h3>Category entity</h3><pre><code class="language-java">@Entity
@Table(name = &quot;categories&quot;)
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
}</code></pre><h3>Product entity</h3><pre><code class="language-java">@Entity
@Table(name = &quot;products&quot;)
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private BigDecimal price;

    private Integer quantity;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;category_id&quot;)
    private Category category;
}</code></pre><h3>ProductRepository</h3><pre><code class="language-java">public interface ProductRepository extends JpaRepository&lt;Product, Long&gt; {

    List&lt;Product&gt; findByNameContainingIgnoreCase(String name);

    Page&lt;Product&gt; findByCategoryId(Long categoryId, Pageable pageable);

    boolean existsByName(String name);

    @Query(&quot;&quot;&quot;
           select p from Product p
           join fetch p.category
           where p.id = :id
           &quot;&quot;&quot;)
    Optional&lt;Product&gt; findByIdWithCategory(@Param(&quot;id&quot;) Long id);
}</code></pre><h3>ProductResponse</h3><pre><code class="language-java">public record ProductResponse(
        Long id,
        String name,
        BigDecimal price,
        Integer quantity,
        Long categoryId,
        String categoryName
) {
}</code></pre><h3>Mapper</h3><pre><code class="language-java">@Component
public class ProductMapper {

    public ProductResponse toResponse(Product product) {
        Category category = product.getCategory();

        return new ProductResponse(
                product.getId(),
                product.getName(),
                product.getPrice(),
                product.getQuantity(),
                category != null ? category.getId() : null,
                category != null ? category.getName() : null
        );
    }
}</code></pre><hr><h2>26. Javachi uchun eng muhim qoidalar</h2><h3>Repository’da</h3><p>Repository faqat database bilan ishlasin:</p><pre><code class="language-java">public interface ProductRepository extends JpaRepository&lt;Product, Long&gt; {
}</code></pre><p>Business logic yozmang.</p><hr><h3>Service’da</h3><p>Service business operationni boshqarsin:</p><pre><code class="language-java">@Transactional
public ProductResponse create(ProductCreateRequest request) {
    // validate
    // find category
    // create product
    // save
    // return response
}</code></pre><hr><h3>Controller’da</h3><p>Controller faqat request/response bilan ishlasin:</p><pre><code class="language-java">@PostMapping
public ProductResponse create(@RequestBody ProductCreateRequest request) {
    return productService.create(request);
}</code></pre><hr><h2>27. Ko‘p uchraydigan xatolar</h2><h3>Xato 1: <code>findAll()</code>ni doim ishlatish</h3><pre><code class="language-java">List&lt;Product&gt; products = productRepository.findAll();</code></pre><p>Katta jadvalda yomon.</p><p>Yaxshiroq:</p><pre><code class="language-java">Page&lt;Product&gt; products = productRepository.findAll(pageable);</code></pre><hr><h3>Xato 2: Entity’ni API response qilish</h3><pre><code class="language-java">@GetMapping
public List&lt;Product&gt; getAll() {
    return productRepository.findAll();
}</code></pre><p>Yaxshiroq:</p><pre><code class="language-java">@GetMapping
public Page&lt;ProductResponse&gt; getAll(...) {
    return productService.getAll(...);
}</code></pre><hr><h3>Xato 3: <code>EAGER</code>ni ko‘p ishlatish</h3><pre><code class="language-java">@ManyToOne(fetch = FetchType.EAGER)
private Category category;</code></pre><p>Kichik loyihada sezilmasligi mumkin. Lekin katta loyihada ortiqcha query va memory muammo beradi.</p><p>Yaxshiroq:</p><pre><code class="language-java">@ManyToOne(fetch = FetchType.LAZY)
private Category category;</code></pre><hr><h3>Xato 4: Har joyga <code>CascadeType.ALL</code> qo‘yish</h3><pre><code class="language-java">@ManyToOne(cascade = CascadeType.ALL)
private Category category;</code></pre><p>Bu category’ni noto‘g‘ri update yoki delete qilib yuborishi mumkin.</p><hr><h3>Xato 5: Update’da yangi entity yasab save qilish</h3><pre><code class="language-java">Product product = new Product();
product.setId(id);
product.setName(request.name());
productRepository.save(product);</code></pre><p>Bu eski fieldlarni yo‘qotishi mumkin.</p><p>Yaxshiroq:</p><pre><code class="language-java">Product product = productRepository.findById(id).orElseThrow();
product.setName(request.name());
product.setPrice(request.price());</code></pre><hr><h2>28. Amaliy vazifa</h2><p>Oldingi <code>Product CRUD API</code> loyihasiga <code>Category</code> qo‘shing.</p><h3>Entitylar</h3><pre><code class="language-text">Category
- id
- name

Product
- id
- name
- price
- quantity
- category</code></pre><h3>Endpointlar</h3><pre><code class="language-http">POST   /api/categories
GET    /api/categories

POST   /api/products
GET    /api/products?page=0&amp;size=20
GET    /api/products/{id}
GET    /api/products?categoryId=1
PUT    /api/products/{id}
DELETE /api/products/{id}</code></pre><h3>Talablar</h3><pre><code class="language-text">Product entity Category bilan @ManyToOne orqali bog‘lansin.
Product response ichida categoryId va categoryName bo‘lsin.
GET /api/products pagination bilan ishlasin.
Repository’da findByCategoryId ishlating.
Service methodlarga @Transactional qo‘ying.
Entity’ni to‘g‘ridan-to‘g‘ri response qilmang.</code></pre><hr><h2>Xulosa</h2><p><strong>Spring Data JPA deeper</strong> mavzusida Java developer quyidagilarni yaxshi bilishi kerak:</p><pre><code class="language-text">JpaRepository CRUD methodlari
Derived query methods
@Query bilan JPQL va native SQL
Pagination va sorting
Page, Slice, List farqi
@OneToOne, @OneToMany, @ManyToOne, @ManyToMany
@JoinColumn
FetchType.LAZY vs EAGER
CascadeType xavflari
N+1 problem
@Transactional va dirty checking
DTO projection</code></pre><p>Eng muhim amaliy qoida:</p><pre><code class="language-text">Controller API bilan ishlaydi.
Service business logic va transactionni boshqaradi.
Repository database bilan ishlaydi.
Entity database modeli.
DTO API modeli.</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Validation]]></title>
            <link>https://blog.jtscorp.uz/validation</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/validation</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Validation</strong> - foydalanuvchi yoki frontend yuborgan ma’lumotni tekshirish. Bu bo‘lim ichida quyidagilar bor: <strong>Bean Validation</strong>, <code>@Valid</code>, <code>@Validated</code>, <code>@NotNull</code>, <code>@NotBlank</code>, <code>@Size</code>, <code>@Email</code>, <code>@Min</code>, <code>@Max</code>, custom constraint annotations va validation groups.</p><hr><h2>1. Validation nima?</h2><p>Masalan frontend sizga user yaratish uchun request yuboradi:</p><pre><code class="language-json">{
  &quot;fullName&quot;: &quot;&quot;,
  &quot;email&quot;: &quot;wrong-email&quot;,
  &quot;age&quot;: -5
}</code></pre><p>Bu data noto‘g‘ri:</p><pre><code class="language-text">fullName bo‘sh
email formati xato
age manfiy</code></pre><p>Shu narsalarni databasega saqlab yubormaslik kerak. Mana shu tekshirish jarayoni <strong>validation</strong> deyiladi.</p><hr><h2>2. Validation qayerda ishlatiladi?</h2><p>Spring Boot’da odatda validation <strong>DTO</strong> classlarda yoziladi.</p><p>Masalan:</p><pre><code class="language-java">public record UserCreateRequest(
        String fullName,
        String email,
        Integer age
) {
}</code></pre><p>Buni validation bilan kuchaytiramiz:</p><pre><code class="language-java">import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record UserCreateRequest(

        @NotBlank(message = &quot;Full name bo‘sh bo‘lmasligi kerak&quot;)
        String fullName,

        @NotBlank(message = &quot;Email bo‘sh bo‘lmasligi kerak&quot;)
        @Email(message = &quot;Email formati noto‘g‘ri&quot;)
        String email,

        @NotNull(message = &quot;Age majburiy&quot;)
        @Min(value = 18, message = &quot;Yosh kamida 18 bo‘lishi kerak&quot;)
        Integer age
) {
}</code></pre><p>Endi noto‘g‘ri request kelsa, Spring uni avtomatik ushlab qoladi.</p><hr><h2>3. Kerakli dependency</h2><p>Spring Boot 3&#039;da validation ishlatish uchun odatda shu dependency kerak bo‘ladi:</p><h3>Maven</h3><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-validation&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><h3>Gradle</h3><pre><code class="language-gradle">implementation &#039;org.springframework.boot:spring-boot-starter-validation&#039;</code></pre><p>Agar bu dependency bo‘lmasa, <code>@Valid</code>, <code>@NotBlank</code>, <code>@Email</code> ishlamaydi yoki kutilganidek validation qilmaydi.</p><hr><h2>4. <code>@Valid</code> nima qiladi?</h2><p>DTO ichidagi validation annotationlarni ishga tushirish uchun controller’da <code>@Valid</code> yoziladi.</p><pre><code class="language-java">@RestController
@RequestMapping(&quot;/api/users&quot;)
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public UserResponse create(@Valid @RequestBody UserCreateRequest request) {
        return userService.create(request);
    }
}</code></pre><p>Bu yerda:</p><pre><code class="language-java">@Valid @RequestBody UserCreateRequest request</code></pre><p>ma’nosi:</p><pre><code class="language-text">Request body’dan UserCreateRequest yasagin,
keyin uning validation qoidalarini tekshirgin.</code></pre><p>Agar validation xato bo‘lsa, method ichiga kirmaydi.</p><p>Ya’ni bu kod chaqirilmaydi:</p><pre><code class="language-java">return userService.create(request);</code></pre><hr><h2>5. Eng ko‘p ishlatiladigan annotationlar</h2><h3><code>@NotNull</code></h3><p>Qiymat <code>null</code> bo‘lmasligi kerak.</p><pre><code class="language-java">@NotNull(message = &quot;Age majburiy&quot;)
private Integer age;</code></pre><p>Bu holat xato:</p><pre><code class="language-json">{
  &quot;age&quot;: null
}</code></pre><p>Lekin bu xato emas:</p><pre><code class="language-json">{
  &quot;age&quot;: 0
}</code></pre><p><code>@NotNull</code> faqat <code>null</code>ni tekshiradi.</p><hr><h3><code>@NotBlank</code></h3><p>String bo‘sh yoki faqat probel bo‘lmasligi kerak.</p><pre><code class="language-java">@NotBlank(message = &quot;Name bo‘sh bo‘lmasligi kerak&quot;)
private String name;</code></pre><p>Quyidagilar xato:</p><pre><code class="language-json">{ &quot;name&quot;: &quot;&quot; }</code></pre><pre><code class="language-json">{ &quot;name&quot;: &quot;   &quot; }</code></pre><pre><code class="language-json">{ &quot;name&quot;: null }</code></pre><p>String uchun ko‘pincha <code>@NotBlank</code> ishlatiladi.</p><hr><h3><code>@NotEmpty</code></h3><p>Collection, array yoki string bo‘sh bo‘lmasligi kerak.</p><pre><code class="language-java">@NotEmpty(message = &quot;Taglar bo‘sh bo‘lmasligi kerak&quot;)
private List&lt;String&gt; tags;</code></pre><p>Farqi:</p><pre><code class="language-text">@NotNull  → null bo‘lmasin
@NotEmpty → null bo‘lmasin va size &gt; 0 bo‘lsin
@NotBlank → null bo‘lmasin, &quot;&quot; bo‘lmasin, &quot;   &quot; bo‘lmasin</code></pre><hr><h3><code>@Size</code></h3><p>String yoki list uzunligini tekshiradi.</p><pre><code class="language-java">@Size(min = 3, max = 50, message = &quot;Name 3 dan 50 tagacha belgi bo‘lishi kerak&quot;)
private String name;</code></pre><p>Misol:</p><pre><code class="language-json">{ &quot;name&quot;: &quot;Al&quot; }</code></pre><p>Bu xato, chunki uzunligi 2.</p><hr><h3><code>@Email</code></h3><p>Email formatini tekshiradi.</p><pre><code class="language-java">@Email(message = &quot;Email formati noto‘g‘ri&quot;)
private String email;</code></pre><p>Xato:</p><pre><code class="language-json">{ &quot;email&quot;: &quot;ali-mail.com&quot; }</code></pre><p>To‘g‘ri:</p><pre><code class="language-json">{ &quot;email&quot;: &quot;ali@mail.com&quot; }</code></pre><p>Amaliy maslahat: <code>@Email</code> bilan birga <code>@NotBlank</code> ham qo‘ying.</p><pre><code class="language-java">@NotBlank(message = &quot;Email majburiy&quot;)
@Email(message = &quot;Email formati noto‘g‘ri&quot;)
private String email;</code></pre><p>Chunki <code>@Email</code> har doim ham bo‘sh qiymatni siz kutgandek ushlamasligi mumkin.</p><hr><h3><code>@Min</code> va <code>@Max</code></h3><p>Sonning minimal va maksimal qiymatini tekshiradi.</p><pre><code class="language-java">@Min(value = 18, message = &quot;Yosh kamida 18 bo‘lishi kerak&quot;)
@Max(value = 100, message = &quot;Yosh 100 dan katta bo‘lmasligi kerak&quot;)
private Integer age;</code></pre><hr><h3><code>@Positive</code> va <code>@PositiveOrZero</code></h3><p>Musbat sonlarni tekshiradi.</p><pre><code class="language-java">@Positive(message = &quot;Narx musbat bo‘lishi kerak&quot;)
private BigDecimal price;</code></pre><p>Bu xato:</p><pre><code class="language-json">{ &quot;price&quot;: 0 }</code></pre><p>Chunki <code>@Positive</code> uchun son <code>0</code> dan katta bo‘lishi kerak.</p><p>Agar <code>0</code> ham mumkin bo‘lsa:</p><pre><code class="language-java">@PositiveOrZero(message = &quot;Narx 0 yoki musbat bo‘lishi kerak&quot;)
private BigDecimal price;</code></pre><hr><h3><code>@Past</code> va <code>@Future</code></h3><p>Sana bilan ishlaganda kerak bo‘ladi.</p><pre><code class="language-java">@Past(message = &quot;Tug‘ilgan sana o‘tmishda bo‘lishi kerak&quot;)
private LocalDate birthDate;</code></pre><pre><code class="language-java">@Future(message = &quot;Deadline kelajakda bo‘lishi kerak&quot;)
private LocalDate deadline;</code></pre><hr><h2>6. Product request validation misoli</h2><p>Oldingi <code>Product CRUD API</code> uchun request DTO:</p><pre><code class="language-java">import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import jakarta.validation.constraints.Size;

import java.math.BigDecimal;

public record ProductCreateRequest(

        @NotBlank(message = &quot;Product name majburiy&quot;)
        @Size(min = 2, max = 100, message = &quot;Product name 2 dan 100 tagacha bo‘lishi kerak&quot;)
        String name,

        @NotNull(message = &quot;Price majburiy&quot;)
        @Positive(message = &quot;Price 0 dan katta bo‘lishi kerak&quot;)
        BigDecimal price,

        @NotNull(message = &quot;Quantity majburiy&quot;)
        @PositiveOrZero(message = &quot;Quantity 0 yoki musbat bo‘lishi kerak&quot;)
        Integer quantity,

        @NotNull(message = &quot;Category id majburiy&quot;)
        Long categoryId
) {
}</code></pre><p>Controller:</p><pre><code class="language-java">@PostMapping
public ProductResponse create(@Valid @RequestBody ProductCreateRequest request) {
    return productService.create(request);
}</code></pre><p>Xato request:</p><pre><code class="language-json">{
  &quot;name&quot;: &quot;&quot;,
  &quot;price&quot;: -100,
  &quot;quantity&quot;: -2,
  &quot;categoryId&quot;: null
}</code></pre><p>Bu request service’ga yetib bormaydi.</p><hr><h2>7. Update request alohida bo‘lishi kerak</h2><p>Create va update uchun bitta DTO ishlatish har doim ham yaxshi emas.</p><p>Create’da hamma field majburiy bo‘lishi mumkin:</p><pre><code class="language-java">public record ProductCreateRequest(

        @NotBlank(message = &quot;Name majburiy&quot;)
        String name,

        @NotNull(message = &quot;Price majburiy&quot;)
        @Positive(message = &quot;Price 0 dan katta bo‘lishi kerak&quot;)
        BigDecimal price,

        @NotNull(message = &quot;Quantity majburiy&quot;)
        @PositiveOrZero(message = &quot;Quantity 0 yoki musbat bo‘lishi kerak&quot;)
        Integer quantity
) {
}</code></pre><p>Update’da esa ba’zi fieldlar optional bo‘lishi mumkin:</p><pre><code class="language-java">public record ProductUpdateRequest(

        @Size(min = 2, max = 100, message = &quot;Name 2 dan 100 tagacha bo‘lishi kerak&quot;)
        String name,

        @Positive(message = &quot;Price 0 dan katta bo‘lishi kerak&quot;)
        BigDecimal price,

        @PositiveOrZero(message = &quot;Quantity 0 yoki musbat bo‘lishi kerak&quot;)
        Integer quantity
) {
}</code></pre><p>Bu yerda <code>@NotNull</code> yo‘q, chunki update’da hammasini yuborish majburiy emas.</p><hr><h2>8. Validation xatolari qanday qaytadi?</h2><p>Default holatda Spring Boot validation xatosini qaytaradi, lekin response ko‘pincha chiroyli bo‘lmaydi.</p><p>Shuning uchun <code>@RestControllerAdvice</code> bilan global error handler yoziladi.</p><pre><code class="language-java">@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity&lt;Map&lt;String, String&gt;&gt; handleValidation(
            MethodArgumentNotValidException ex
    ) {
        Map&lt;String, String&gt; errors = new HashMap&lt;&gt;();

        ex.getBindingResult()
                .getFieldErrors()
                .forEach(error -&gt;
                        errors.put(error.getField(), error.getDefaultMessage())
                );

        return ResponseEntity.badRequest().body(errors);
    }
}</code></pre><p>Endi xato response shunday bo‘ladi:</p><pre><code class="language-json">{
  &quot;name&quot;: &quot;Product name majburiy&quot;,
  &quot;price&quot;: &quot;Price 0 dan katta bo‘lishi kerak&quot;,
  &quot;quantity&quot;: &quot;Quantity 0 yoki musbat bo‘lishi kerak&quot;,
  &quot;categoryId&quot;: &quot;Category id majburiy&quot;
}</code></pre><p>Bu frontend uchun ancha qulay.</p><hr><h2>9. Yaxshiroq error response format</h2><p>Real loyihada hamma errorlar bir xil formatda bo‘lgani yaxshi.</p><p>Masalan:</p><pre><code class="language-java">public record ErrorResponse(
        String code,
        String message,
        Map&lt;String, String&gt; fields
) {
}</code></pre><p>Handler:</p><pre><code class="language-java">@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleValidation(
            MethodArgumentNotValidException ex
    ) {
        Map&lt;String, String&gt; fields = new HashMap&lt;&gt;();

        ex.getBindingResult()
                .getFieldErrors()
                .forEach(error -&gt;
                        fields.put(error.getField(), error.getDefaultMessage())
                );

        ErrorResponse response = new ErrorResponse(
                &quot;VALIDATION_ERROR&quot;,
                &quot;Request data noto‘g‘ri&quot;,
                fields
        );

        return ResponseEntity.badRequest().body(response);
    }
}</code></pre><p>Response:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;VALIDATION_ERROR&quot;,
  &quot;message&quot;: &quot;Request data noto‘g‘ri&quot;,
  &quot;fields&quot;: {
    &quot;name&quot;: &quot;Product name majburiy&quot;,
    &quot;price&quot;: &quot;Price 0 dan katta bo‘lishi kerak&quot;
  }
}</code></pre><hr><h2>10. <code>@Valid</code> nested objectlarda</h2><p>Agar request ichida boshqa object bo‘lsa, ichki objectni ham tekshirish uchun <code>@Valid</code> kerak.</p><p>Masalan:</p><pre><code class="language-java">public record CreateOrderRequest(

        @NotNull(message = &quot;Customer majburiy&quot;)
        @Valid
        CustomerRequest customer,

        @NotEmpty(message = &quot;Order itemlar bo‘sh bo‘lmasligi kerak&quot;)
        List&lt;@Valid OrderItemRequest&gt; items
) {
}</code></pre><p>Customer:</p><pre><code class="language-java">public record CustomerRequest(

        @NotBlank(message = &quot;Customer name majburiy&quot;)
        String name,

        @NotBlank(message = &quot;Phone majburiy&quot;)
        String phone
) {
}</code></pre><p>Order item:</p><pre><code class="language-java">public record OrderItemRequest(

        @NotNull(message = &quot;Product id majburiy&quot;)
        Long productId,

        @NotNull(message = &quot;Quantity majburiy&quot;)
        @Positive(message = &quot;Quantity 0 dan katta bo‘lishi kerak&quot;)
        Integer quantity
) {
}</code></pre><p>Muhim joy:</p><pre><code class="language-java">List&lt;@Valid OrderItemRequest&gt; items</code></pre><p>Bu list ichidagi har bir itemni ham validate qiladi.</p><hr><h2>11. PathVariable va RequestParam validation</h2><p>Faqat <code>@RequestBody</code> emas, <code>@PathVariable</code> va <code>@RequestParam</code> ham validate qilinadi.</p><p>Buning uchun controller class ustiga <code>@Validated</code> qo‘yiladi.</p><pre><code class="language-java">@RestController
@RequestMapping(&quot;/api/products&quot;)
@Validated
public class ProductController {

    @GetMapping(&quot;/{id}&quot;)
    public ProductResponse getById(
            @PathVariable @Positive(message = &quot;Id musbat bo‘lishi kerak&quot;) Long id
    ) {
        return productService.getById(id);
    }

    @GetMapping
    public Page&lt;ProductResponse&gt; getAll(
            @RequestParam(defaultValue = &quot;0&quot;) @Min(0) int page,
            @RequestParam(defaultValue = &quot;20&quot;) @Min(1) @Max(100) int size
    ) {
        return productService.getAll(page, size);
    }
}</code></pre><p>Request:</p><pre><code class="language-http">GET /api/products?page=-1&amp;size=1000</code></pre><p>Bu xato bo‘ladi.</p><hr><h2>12. <code>@Valid</code> va <code>@Validated</code> farqi</h2><h3><code>@Valid</code></h3><p>Ko‘proq request body DTO’ni validate qilish uchun ishlatiladi:</p><pre><code class="language-java">public ProductResponse create(@Valid @RequestBody ProductCreateRequest request)</code></pre><h3><code>@Validated</code></h3><p>Spring’ning annotationi. Ko‘proq:</p><pre><code class="language-text">PathVariable validation
RequestParam validation
Validation groups
Service method validation</code></pre><p>uchun ishlatiladi.</p><p>Misol:</p><pre><code class="language-java">@Validated
@RestController
public class ProductController {
}</code></pre><p>Oddiy qoida:</p><pre><code class="language-text">@RequestBody DTO uchun → @Valid
@PathVariable / @RequestParam uchun → class ustiga @Validated
Validation groups uchun → @Validated(Group.class)</code></pre><hr><h2>13. Service method validation</h2><p>Ba’zan validation service methodlarda ham kerak bo‘ladi.</p><pre><code class="language-java">@Service
@Validated
public class ProductService {

    public ProductResponse getById(
            @Positive(message = &quot;Id musbat bo‘lishi kerak&quot;) Long id
    ) {
        // logic
    }
}</code></pre><p>Lekin amaliyotda ko‘pincha request validation controller’da qilinadi. Service’da esa business validation bo‘ladi.</p><hr><h2>14. Input validation vs Business validation</h2><p>Bularni ajratish juda muhim.</p><h3>Input validation</h3><p>Request formatini tekshiradi:</p><pre><code class="language-text">name bo‘sh emasmi?
email to‘g‘ri formatdami?
age 18 dan kattami?
price musbatmi?</code></pre><p>Bular DTO’da annotation bilan yoziladi.</p><pre><code class="language-java">@NotBlank
@Email
@Min
@Positive</code></pre><hr><h3>Business validation</h3><p>Biznes qoida bo‘yicha tekshiradi:</p><pre><code class="language-text">Email database’da bor-yo‘qligi
Product stock yetarlimi
User blocked emasmi
Category mavjudmi
Order status CANCELLED bo‘lsa update qilish mumkin emasmi</code></pre><p>Bular service layer’da yoziladi.</p><p>Masalan:</p><pre><code class="language-java">@Transactional
public ProductResponse create(ProductCreateRequest request) {
    if (productRepository.existsByName(request.name())) {
        throw new BadRequestException(&quot;Product name already exists&quot;);
    }

    Category category = categoryRepository.findById(request.categoryId())
            .orElseThrow(() -&gt; new NotFoundException(&quot;Category not found&quot;));

    Product product = productMapper.toEntity(request, category);

    return productMapper.toResponse(productRepository.save(product));
}</code></pre><p><code>categoryId</code> null emasligini DTO tekshiradi. Lekin category database’da bormi - buni service tekshiradi.</p><hr><h2>15. Custom exception bilan ishlash</h2><p>Service’da <code>IllegalArgumentException</code> ishlatish mumkin, lekin real loyihada custom exception yaxshiroq.</p><pre><code class="language-java">public class BadRequestException extends RuntimeException {

    public BadRequestException(String message) {
        super(message);
    }
}</code></pre><pre><code class="language-java">public class NotFoundException extends RuntimeException {

    public NotFoundException(String message) {
        super(message);
    }
}</code></pre><p>Global handler:</p><pre><code class="language-java">@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BadRequestException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleBadRequest(BadRequestException ex) {
        return ResponseEntity.badRequest().body(
                new ErrorResponse(&quot;BAD_REQUEST&quot;, ex.getMessage(), null)
        );
    }

    @ExceptionHandler(NotFoundException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleNotFound(NotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
                new ErrorResponse(&quot;NOT_FOUND&quot;, ex.getMessage(), null)
        );
    }
}</code></pre><hr><h2>16. Custom validation annotation</h2><p>Ba’zan tayyor annotation yetmaydi.</p><p>Masalan telefon raqam <code>+998</code> bilan boshlanishi kerak.</p><h3>Annotation yaratamiz</h3><pre><code class="language-java">import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = UzbekPhoneValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UzbekPhone {

    String message() default &quot;Telefon raqam +998 bilan boshlanishi kerak&quot;;

    Class&lt;?&gt;[] groups() default {};

    Class&lt;? extends Payload&gt;[] payload() default {};
}</code></pre><h3>Validator yozamiz</h3><pre><code class="language-java">import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class UzbekPhoneValidator implements ConstraintValidator&lt;UzbekPhone, String&gt; {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null || value.isBlank()) {
            return true;
        }

        return value.matches(&quot;^\\+998\\d{9}$&quot;);
    }
}</code></pre><p>Bu yerda:</p><pre><code class="language-text">+998901234567</code></pre><p>to‘g‘ri.</p><pre><code class="language-text">901234567</code></pre><p>xato.</p><h3>DTO’da ishlatamiz</h3><pre><code class="language-java">public record CustomerCreateRequest(

        @NotBlank(message = &quot;Name majburiy&quot;)
        String name,

        @NotBlank(message = &quot;Phone majburiy&quot;)
        @UzbekPhone
        String phone
) {
}</code></pre><p>Nega validator’da <code>null</code> uchun <code>true</code> qaytardik?</p><p>Chunki <code>null</code> yoki bo‘shlikni <code>@NotBlank</code> tekshiradi. <code>@UzbekPhone</code> faqat formatni tekshiradi.</p><hr><h2>17. Validation Groups</h2><p>Ba’zan bitta DTO create va update’da har xil qoida bilan ishlatiladi.</p><p>Masalan:</p><pre><code class="language-java">public interface CreateGroup {
}

public interface UpdateGroup {
}</code></pre><p>DTO:</p><pre><code class="language-java">public record ProductRequest(

        @NotNull(groups = UpdateGroup.class, message = &quot;Id update uchun majburiy&quot;)
        Long id,

        @NotBlank(groups = CreateGroup.class, message = &quot;Name create uchun majburiy&quot;)
        @Size(min = 2, max = 100, message = &quot;Name 2 dan 100 tagacha bo‘lishi kerak&quot;)
        String name,

        @NotNull(groups = CreateGroup.class, message = &quot;Price create uchun majburiy&quot;)
        @Positive(message = &quot;Price 0 dan katta bo‘lishi kerak&quot;)
        BigDecimal price
) {
}</code></pre><p>Controller:</p><pre><code class="language-java">@PostMapping
public ProductResponse create(
        @Validated(CreateGroup.class) @RequestBody ProductRequest request
) {
    return productService.create(request);
}

@PutMapping
public ProductResponse update(
        @Validated(UpdateGroup.class) @RequestBody ProductRequest request
) {
    return productService.update(request);
}</code></pre><p>Lekin Java developer uchun amaliy tavsiya:</p><pre><code class="language-text">Validation groupsni bilib qo‘ying.
Lekin ko‘p holatda alohida CreateRequest va UpdateRequest yozish ancha oson va toza.</code></pre><p>Ya’ni:</p><pre><code class="language-text">ProductCreateRequest
ProductUpdateRequest</code></pre><p>ko‘pincha yaxshiroq.</p><hr><h2>18. Entity’da validation yozsa bo‘ladimi?</h2><p>Texnik jihatdan bo‘ladi:</p><pre><code class="language-java">@Entity
public class User {

    @NotBlank
    private String fullName;
}</code></pre><p>Lekin API request validation uchun DTO’da yozish yaxshiroq.</p><p>Nega?</p><p>Chunki entity - database modeli.</p><p>DTO - request/response modeli.</p><p>Create va update qoidalari har xil bo‘lishi mumkin.</p><p>Masalan:</p><pre><code class="language-text">Create’da password majburiy.
Update’da password majburiy emas.</code></pre><p>Agar hammasini entity’ga yozsangiz, keyin chalkashib ketadi.</p><p>Amaliy qoida:</p><pre><code class="language-text">API validation → DTO
Database constraints → Entity yoki migration SQL
Business validation → Service</code></pre><hr><h2>19. Database constraint validationdan alohidami?</h2><p>Ha, alohida.</p><p>DTO validation:</p><pre><code class="language-java">@NotBlank
@Email</code></pre><p>bu request bosqichida ishlaydi.</p><p>Database constraint:</p><pre><code class="language-java">@Column(nullable = false, unique = true)
private String email;</code></pre><p>yoki SQL migration:</p><pre><code class="language-sql">alter table users
add constraint uq_users_email unique (email);</code></pre><p>Bu database bosqichida ishlaydi.</p><p>Ikkalasi ham kerak bo‘lishi mumkin.</p><p>Nega?</p><p>Chunki DTO validation foydalanuvchiga chiroyli xato qaytaradi.</p><p>Database constraint esa oxirgi himoya. Agar koddagi xato sabab duplicate email o‘tib ketsa ham, database ushlab qoladi.</p><hr><h2>20. Real Product validation to‘liq misol</h2><h3>ProductCreateRequest</h3><pre><code class="language-java">public record ProductCreateRequest(

        @NotBlank(message = &quot;Name majburiy&quot;)
        @Size(min = 2, max = 100, message = &quot;Name 2 dan 100 tagacha bo‘lishi kerak&quot;)
        String name,

        @NotNull(message = &quot;Price majburiy&quot;)
        @Positive(message = &quot;Price 0 dan katta bo‘lishi kerak&quot;)
        BigDecimal price,

        @NotNull(message = &quot;Quantity majburiy&quot;)
        @PositiveOrZero(message = &quot;Quantity 0 yoki musbat bo‘lishi kerak&quot;)
        Integer quantity,

        @NotNull(message = &quot;Category id majburiy&quot;)
        @Positive(message = &quot;Category id musbat bo‘lishi kerak&quot;)
        Long categoryId
) {
}</code></pre><h3>ProductUpdateRequest</h3><pre><code class="language-java">public record ProductUpdateRequest(

        @Size(min = 2, max = 100, message = &quot;Name 2 dan 100 tagacha bo‘lishi kerak&quot;)
        String name,

        @Positive(message = &quot;Price 0 dan katta bo‘lishi kerak&quot;)
        BigDecimal price,

        @PositiveOrZero(message = &quot;Quantity 0 yoki musbat bo‘lishi kerak&quot;)
        Integer quantity,

        @Positive(message = &quot;Category id musbat bo‘lishi kerak&quot;)
        Long categoryId
) {
}</code></pre><h3>Controller</h3><pre><code class="language-java">@RestController
@RequestMapping(&quot;/api/products&quot;)
@Validated
public class ProductController {

    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @PostMapping
    public ProductResponse create(
            @Valid @RequestBody ProductCreateRequest request
    ) {
        return productService.create(request);
    }

    @PutMapping(&quot;/{id}&quot;)
    public ProductResponse update(
            @PathVariable @Positive(message = &quot;Id musbat bo‘lishi kerak&quot;) Long id,
            @Valid @RequestBody ProductUpdateRequest request
    ) {
        return productService.update(id, request);
    }

    @GetMapping
    public Page&lt;ProductResponse&gt; getAll(
            @RequestParam(defaultValue = &quot;0&quot;) @Min(0) int page,
            @RequestParam(defaultValue = &quot;20&quot;) @Min(1) @Max(100) int size
    ) {
        return productService.getAll(page, size);
    }
}</code></pre><h3>Service</h3><pre><code class="language-java">@Service
public class ProductService {

    private final ProductRepository productRepository;
    private final CategoryRepository categoryRepository;
    private final ProductMapper productMapper;

    public ProductService(ProductRepository productRepository,
                          CategoryRepository categoryRepository,
                          ProductMapper productMapper) {
        this.productRepository = productRepository;
        this.categoryRepository = categoryRepository;
        this.productMapper = productMapper;
    }

    @Transactional
    public ProductResponse create(ProductCreateRequest request) {
        if (productRepository.existsByName(request.name())) {
            throw new BadRequestException(&quot;Product name already exists&quot;);
        }

        Category category = categoryRepository.findById(request.categoryId())
                .orElseThrow(() -&gt; new NotFoundException(&quot;Category not found&quot;));

        Product product = productMapper.toEntity(request, category);
        Product savedProduct = productRepository.save(product);

        return productMapper.toResponse(savedProduct);
    }
}</code></pre><p>Bu yerda validation ikki qismga bo‘lingan:</p><pre><code class="language-text">DTO validation:
- name bo‘sh emas
- price musbat
- quantity 0 yoki musbat
- categoryId null emas

Service validation:
- product name unique
- category database’da mavjud</code></pre><hr><h2>21. Ko‘p uchraydigan xatolar</h2><h3>Xato 1: <code>@Valid</code>ni unutish</h3><p>DTO’da annotation bor:</p><pre><code class="language-java">public record UserCreateRequest(
        @NotBlank String name
) {
}</code></pre><p>Lekin controller’da <code>@Valid</code> yo‘q:</p><pre><code class="language-java">@PostMapping
public UserResponse create(@RequestBody UserCreateRequest request) {
    return userService.create(request);
}</code></pre><p>Natija: validation ishlamaydi.</p><p>To‘g‘risi:</p><pre><code class="language-java">@PostMapping
public UserResponse create(@Valid @RequestBody UserCreateRequest request) {
    return userService.create(request);
}</code></pre><hr><h3>Xato 2: String uchun <code>@NotNull</code> ishlatish</h3><pre><code class="language-java">@NotNull
private String name;</code></pre><p>Bu <code>&quot;&quot;</code> yoki <code>&quot; &quot;</code> ni ushlamaydi.</p><p>Yaxshiroq:</p><pre><code class="language-java">@NotBlank
private String name;</code></pre><hr><h3>Xato 3: <code>@Email</code>ni yolg‘iz ishlatish</h3><pre><code class="language-java">@Email
private String email;</code></pre><p>Yaxshiroq:</p><pre><code class="language-java">@NotBlank
@Email
private String email;</code></pre><hr><h3>Xato 4: Business validationni DTO annotation bilan qilishga urinish</h3><p>Masalan:</p><pre><code class="language-text">Email database’da unique bo‘lishi kerak</code></pre><p>Buni oddiy <code>@Email</code> yoki <code>@NotBlank</code> bilan hal qilib bo‘lmaydi.</p><p>Bu service’da tekshiriladi:</p><pre><code class="language-java">if (userRepository.existsByEmail(request.email())) {
    throw new BadRequestException(&quot;Email already exists&quot;);
}</code></pre><hr><h3>Xato 5: Entity’ni request sifatida olish</h3><p>Yomon:</p><pre><code class="language-java">@PostMapping
public ProductResponse create(@Valid @RequestBody Product product) {
    return productService.create(product);
}</code></pre><p>Yaxshi:</p><pre><code class="language-java">@PostMapping
public ProductResponse create(@Valid @RequestBody ProductCreateRequest request) {
    return productService.create(request);
}</code></pre><hr><h2>22. Java developer uchun qisqa qoida</h2><pre><code class="language-text">DTO - input format validation
Controller - @Valid / @Validated ishlatadi
Service - business validation qiladi
Repository - database tekshiruvlari uchun ishlatiladi
Database - constraint bilan oxirgi himoya qiladi</code></pre><hr><h2>23. Amaliy vazifa</h2><p>Oldingi <code>Product CRUD API</code> loyihasiga validation qo‘shing.</p><h3>Talablar</h3><p><code>ProductCreateRequest</code>:</p><pre><code class="language-text">name:
- majburiy
- 2 dan 100 gacha belgi

price:
- majburiy
- 0 dan katta

quantity:
- majburiy
- 0 yoki musbat

categoryId:
- majburiy
- musbat</code></pre><p><code>ProductUpdateRequest</code>:</p><pre><code class="language-text">name:
- optional
- kelsa 2 dan 100 gacha

price:
- optional
- kelsa 0 dan katta

quantity:
- optional
- kelsa 0 yoki musbat

categoryId:
- optional
- kelsa musbat</code></pre><p>Controller:</p><pre><code class="language-text">POST /api/products → @Valid
PUT /api/products/{id} → @Valid + @Positive id
GET /api/products?page=0&amp;size=20 → page &gt;= 0, size 1..100</code></pre><p>Global error response:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;VALIDATION_ERROR&quot;,
  &quot;message&quot;: &quot;Request data noto‘g‘ri&quot;,
  &quot;fields&quot;: {
    &quot;name&quot;: &quot;Name majburiy&quot;,
    &quot;price&quot;: &quot;Price 0 dan katta bo‘lishi kerak&quot;
  }
}</code></pre><hr><h2>Xulosa</h2><p><strong>Validation</strong> Spring Boot’da noto‘g‘ri ma’lumotni erta ushlash uchun ishlatiladi.</p><p>Eng muhimlari:</p><pre><code class="language-text">@NotNull    → null bo‘lmasin
@NotBlank   → String bo‘sh bo‘lmasin
@NotEmpty   → collection/string bo‘sh bo‘lmasin
@Size       → uzunlik/size oralig‘i
@Email      → email format
@Min/@Max   → son oralig‘i
@Positive   → 0 dan katta
@Valid      → DTO validationni ishga tushiradi
@Validated  → method param va groups uchun</code></pre><p>Eng muhim arxitektura qoida:</p><pre><code class="language-text">DTO validation - request formati uchun
Service validation - biznes qoida uchun
Database constraint - oxirgi himoya uchun</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Exception Handling]]></title>
            <link>https://blog.jtscorp.uz/exception-handling</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/exception-handling</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Exception handling</strong> - Spring Boot’da xatolarni tartibli ushlash va clientga tushunarli response qaytarish usuli.</p><p>Bu bo‘lim ichida quyidagilar bor: <code>@ControllerAdvice / @RestControllerAdvice</code>, <code>@ExceptionHandler</code>, custom exception classes, <code>ProblemDetail</code>, consistent error response format.</p><hr><h2>1. Exception handling nima?</h2><p>API ishlaganda har xil xatolar bo‘ladi:</p><pre><code class="language-text">User topilmadi
Email allaqachon mavjud
Validation xato
Database xato
Token noto‘g‘ri
Ruxsat yo‘q</code></pre><p>Agar bu xatolarni tartibli ushlamasak, clientga noaniq response qaytadi:</p><pre><code class="language-json">{
  &quot;timestamp&quot;: &quot;2026-05-21T10:00:00&quot;,
  &quot;status&quot;: 500,
  &quot;error&quot;: &quot;Internal Server Error&quot;,
  &quot;path&quot;: &quot;/api/users/99&quot;
}</code></pre><p>Bu frontend yoki mobile developer uchun yetarli emas.</p><p>Yaxshi API xatosi shunday bo‘lishi kerak:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;USER_NOT_FOUND&quot;,
  &quot;message&quot;: &quot;User topilmadi&quot;,
  &quot;status&quot;: 404,
  &quot;path&quot;: &quot;/api/users/99&quot;
}</code></pre><hr><h2>2. Exception nima?</h2><p>Java’da <strong>exception</strong> - dastur ishlayotgan paytda yuz beradigan xato.</p><p>Masalan:</p><pre><code class="language-java">User user = userRepository.findById(id)
        .orElseThrow(() -&gt; new RuntimeException(&quot;User not found&quot;));</code></pre><p>Agar user topilmasa, exception tashlanadi.</p><pre><code class="language-text">throw new RuntimeException(...)</code></pre><p>ya’ni: &quot;bu holatda normal davom etib bo‘lmaydi&quot;.</p><hr><h2>3. Spring Boot’da default xato response</h2><p>Agar siz exceptionni o‘zingiz ushlamasangiz, Spring Boot default error qaytaradi.</p><p>Masalan:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public UserResponse getById(@PathVariable Long id) {
    throw new RuntimeException(&quot;User not found&quot;);
}</code></pre><p>Response odatda shunga o‘xshash bo‘ladi:</p><pre><code class="language-json">{
  &quot;timestamp&quot;: &quot;2026-05-21T10:00:00.000+00:00&quot;,
  &quot;status&quot;: 500,
  &quot;error&quot;: &quot;Internal Server Error&quot;,
  &quot;path&quot;: &quot;/api/users/1&quot;
}</code></pre><p>Muammo:</p><pre><code class="language-text">status noto‘g‘ri: 500 emas, 404 bo‘lishi kerak
message aniq emas
frontend uchun code yo‘q
hamma xato bir xil formatda emas</code></pre><p>Shuning uchun global exception handler yoziladi.</p><hr><h2>4. <code>@RestControllerAdvice</code> nima?</h2><p><code>@RestControllerAdvice</code> - butun project bo‘yicha exceptionlarni ushlaydigan global class.</p><pre><code class="language-java">@RestControllerAdvice
public class GlobalExceptionHandler {
}</code></pre><p>Bu class ichida har xil exceptionlar uchun handler methodlar yoziladi.</p><hr><h2>5. <code>@ExceptionHandler</code> nima?</h2><p><code>@ExceptionHandler</code> - qaysi exceptionni ushlash kerakligini bildiradi.</p><pre><code class="language-java">@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity&lt;String&gt; handleRuntimeException(RuntimeException ex) {
        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(ex.getMessage());
    }
}</code></pre><p>Agar projectda <code>RuntimeException</code> chiqsa, Spring shu methodni chaqiradi.</p><hr><h2>6. Lekin hamma joyda <code>RuntimeException</code> ishlatish yomon</h2><p>Yomonroq:</p><pre><code class="language-java">throw new RuntimeException(&quot;User not found&quot;);</code></pre><p>Nega?</p><p>Chunki <code>RuntimeException</code> juda umumiy. U orqali xato turi aniq bilinmaydi.</p><p>Yaxshiroq:</p><pre><code class="language-java">throw new NotFoundException(&quot;User topilmadi&quot;);</code></pre><p>Yoki:</p><pre><code class="language-java">throw new BadRequestException(&quot;Email allaqachon mavjud&quot;);</code></pre><hr><h2>7. Custom exception classlar</h2><p>Real loyihada o‘z exception classlaringiz bo‘lgani yaxshi.</p><h3><code>NotFoundException</code></h3><pre><code class="language-java">public class NotFoundException extends RuntimeException {

    public NotFoundException(String message) {
        super(message);
    }
}</code></pre><h3><code>BadRequestException</code></h3><pre><code class="language-java">public class BadRequestException extends RuntimeException {

    public BadRequestException(String message) {
        super(message);
    }
}</code></pre><h3><code>ForbiddenException</code></h3><pre><code class="language-java">public class ForbiddenException extends RuntimeException {

    public ForbiddenException(String message) {
        super(message);
    }
}</code></pre><hr><h2>8. Service’da custom exception ishlatish</h2><p>Masalan <code>UserService</code>:</p><pre><code class="language-java">@Service
public class UserService {

    private final UserRepository userRepository;
    private final UserMapper userMapper;

    public UserService(UserRepository userRepository,
                       UserMapper userMapper) {
        this.userRepository = userRepository;
        this.userMapper = userMapper;
    }

    public UserResponse getById(Long id) {
        User user = userRepository.findById(id)
                .orElseThrow(() -&gt; new NotFoundException(&quot;User topilmadi&quot;));

        return userMapper.toResponse(user);
    }

    @Transactional
    public UserResponse create(UserCreateRequest request) {
        if (userRepository.existsByEmail(request.email())) {
            throw new BadRequestException(&quot;Email allaqachon mavjud&quot;);
        }

        User user = userMapper.toEntity(request);
        User savedUser = userRepository.save(user);

        return userMapper.toResponse(savedUser);
    }
}</code></pre><p>Bu yerda:</p><pre><code class="language-text">User topilmasa → NotFoundException
Email mavjud bo‘lsa → BadRequestException</code></pre><hr><h2>9. Error response DTO</h2><p>Clientga bir xil formatda xato qaytarish uchun DTO yozamiz.</p><pre><code class="language-java">import java.time.LocalDateTime;
import java.util.Map;

public record ErrorResponse(
        String code,
        String message,
        int status,
        String path,
        LocalDateTime timestamp,
        Map&lt;String, String&gt; fields
) {
}</code></pre><p>Bu yerda:</p><pre><code class="language-text">code       → xato kodi
message    → odam tushunadigan xabar
status     → HTTP status
path       → qaysi endpointda xato bo‘ldi
timestamp  → qachon xato bo‘ldi
fields     → validation xatolari uchun</code></pre><hr><h2>10. GlobalExceptionHandler to‘liq misol</h2><pre><code class="language-java">import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.time.LocalDateTime;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NotFoundException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleNotFound(
            NotFoundException ex,
            HttpServletRequest request
    ) {
        ErrorResponse response = new ErrorResponse(
                &quot;NOT_FOUND&quot;,
                ex.getMessage(),
                HttpStatus.NOT_FOUND.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                null
        );

        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
    }

    @ExceptionHandler(BadRequestException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleBadRequest(
            BadRequestException ex,
            HttpServletRequest request
    ) {
        ErrorResponse response = new ErrorResponse(
                &quot;BAD_REQUEST&quot;,
                ex.getMessage(),
                HttpStatus.BAD_REQUEST.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                null
        );

        return ResponseEntity.badRequest().body(response);
    }
}</code></pre><hr><h2>11. Natija qanday bo‘ladi?</h2><p>Request:</p><pre><code class="language-http">GET /api/users/99</code></pre><p>Agar user topilmasa:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;NOT_FOUND&quot;,
  &quot;message&quot;: &quot;User topilmadi&quot;,
  &quot;status&quot;: 404,
  &quot;path&quot;: &quot;/api/users/99&quot;,
  &quot;timestamp&quot;: &quot;2026-05-21T15:30:12&quot;,
  &quot;fields&quot;: null
}</code></pre><p>Request:</p><pre><code class="language-http">POST /api/users</code></pre><p>Agar email mavjud bo‘lsa:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;BAD_REQUEST&quot;,
  &quot;message&quot;: &quot;Email allaqachon mavjud&quot;,
  &quot;status&quot;: 400,
  &quot;path&quot;: &quot;/api/users&quot;,
  &quot;timestamp&quot;: &quot;2026-05-21T15:32:44&quot;,
  &quot;fields&quot;: null
}</code></pre><hr><h2>12. Validation exception handling</h2><p>Oldingi darsda <code>@Valid</code> ishlatdik.</p><p>Masalan request xato:</p><pre><code class="language-json">{
  &quot;fullName&quot;: &quot;&quot;,
  &quot;email&quot;: &quot;wrong-email&quot;,
  &quot;age&quot;: -5
}</code></pre><p>DTO:</p><pre><code class="language-java">public record UserCreateRequest(

        @NotBlank(message = &quot;Full name majburiy&quot;)
        String fullName,

        @NotBlank(message = &quot;Email majburiy&quot;)
        @Email(message = &quot;Email formati noto‘g‘ri&quot;)
        String email,

        @NotNull(message = &quot;Age majburiy&quot;)
        @Min(value = 18, message = &quot;Yosh kamida 18 bo‘lishi kerak&quot;)
        Integer age
) {
}</code></pre><p>Validation xatosi uchun Spring <code>MethodArgumentNotValidException</code> tashlaydi.</p><p>Uni ushlaymiz:</p><pre><code class="language-java">import org.springframework.web.bind.MethodArgumentNotValidException;

import java.util.HashMap;
import java.util.Map;

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity&lt;ErrorResponse&gt; handleValidation(
        MethodArgumentNotValidException ex,
        HttpServletRequest request
) {
    Map&lt;String, String&gt; fields = new HashMap&lt;&gt;();

    ex.getBindingResult()
            .getFieldErrors()
            .forEach(error -&gt;
                    fields.put(error.getField(), error.getDefaultMessage())
            );

    ErrorResponse response = new ErrorResponse(
            &quot;VALIDATION_ERROR&quot;,
            &quot;Request data noto‘g‘ri&quot;,
            HttpStatus.BAD_REQUEST.value(),
            request.getRequestURI(),
            LocalDateTime.now(),
            fields
    );

    return ResponseEntity.badRequest().body(response);
}</code></pre><p>Response:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;VALIDATION_ERROR&quot;,
  &quot;message&quot;: &quot;Request data noto‘g‘ri&quot;,
  &quot;status&quot;: 400,
  &quot;path&quot;: &quot;/api/users&quot;,
  &quot;timestamp&quot;: &quot;2026-05-21T15:35:01&quot;,
  &quot;fields&quot;: {
    &quot;fullName&quot;: &quot;Full name majburiy&quot;,
    &quot;email&quot;: &quot;Email formati noto‘g‘ri&quot;,
    &quot;age&quot;: &quot;Yosh kamida 18 bo‘lishi kerak&quot;
  }
}</code></pre><hr><h2>13. RequestParam va PathVariable validation xatosi</h2><p>Agar controller’da shunday yozgan bo‘lsak:</p><pre><code class="language-java">@RestController
@RequestMapping(&quot;/api/products&quot;)
@Validated
public class ProductController {

    @GetMapping
    public Page&lt;ProductResponse&gt; getAll(
            @RequestParam(defaultValue = &quot;0&quot;) @Min(0) int page,
            @RequestParam(defaultValue = &quot;20&quot;) @Min(1) @Max(100) int size
    ) {
        return productService.getAll(page, size);
    }
}</code></pre><p>Request:</p><pre><code class="language-http">GET /api/products?page=-1&amp;size=500</code></pre><p>Bunda odatda <code>ConstraintViolationException</code> chiqadi.</p><p>Handler:</p><pre><code class="language-java">import jakarta.validation.ConstraintViolationException;

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity&lt;ErrorResponse&gt; handleConstraintViolation(
        ConstraintViolationException ex,
        HttpServletRequest request
) {
    Map&lt;String, String&gt; fields = new HashMap&lt;&gt;();

    ex.getConstraintViolations()
            .forEach(error -&gt; {
                String field = error.getPropertyPath().toString();
                String message = error.getMessage();
                fields.put(field, message);
            });

    ErrorResponse response = new ErrorResponse(
            &quot;VALIDATION_ERROR&quot;,
            &quot;Request parameter noto‘g‘ri&quot;,
            HttpStatus.BAD_REQUEST.value(),
            request.getRequestURI(),
            LocalDateTime.now(),
            fields
    );

    return ResponseEntity.badRequest().body(response);
}</code></pre><p>Response taxminan:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;VALIDATION_ERROR&quot;,
  &quot;message&quot;: &quot;Request parameter noto‘g‘ri&quot;,
  &quot;status&quot;: 400,
  &quot;path&quot;: &quot;/api/products&quot;,
  &quot;timestamp&quot;: &quot;2026-05-21T15:40:20&quot;,
  &quot;fields&quot;: {
    &quot;getAll.page&quot;: &quot;must be greater than or equal to 0&quot;,
    &quot;getAll.size&quot;: &quot;must be less than or equal to 100&quot;
  }
}</code></pre><hr><h2>14. Type mismatch xatolari</h2><p>Masalan endpoint:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public UserResponse getById(@PathVariable Long id) {
    return userService.getById(id);
}</code></pre><p>Client noto‘g‘ri yubordi:</p><pre><code class="language-http">GET /api/users/abc</code></pre><p><code>abc</code> ni <code>Long</code> ga aylantirib bo‘lmaydi.</p><p>Bunda Spring odatda <code>MethodArgumentTypeMismatchException</code> tashlaydi.</p><pre><code class="language-java">import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity&lt;ErrorResponse&gt; handleTypeMismatch(
        MethodArgumentTypeMismatchException ex,
        HttpServletRequest request
) {
    String message = &quot;Parameter formati noto‘g‘ri: &quot; + ex.getName();

    ErrorResponse response = new ErrorResponse(
            &quot;TYPE_MISMATCH&quot;,
            message,
            HttpStatus.BAD_REQUEST.value(),
            request.getRequestURI(),
            LocalDateTime.now(),
            null
    );

    return ResponseEntity.badRequest().body(response);
}</code></pre><p>Response:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;TYPE_MISMATCH&quot;,
  &quot;message&quot;: &quot;Parameter formati noto‘g‘ri: id&quot;,
  &quot;status&quot;: 400,
  &quot;path&quot;: &quot;/api/users/abc&quot;,
  &quot;timestamp&quot;: &quot;2026-05-21T15:42:10&quot;,
  &quot;fields&quot;: null
}</code></pre><hr><h2>15. JSON noto‘g‘ri bo‘lsa</h2><p>Client noto‘g‘ri JSON yuborishi mumkin:</p><pre><code class="language-json">{
  &quot;name&quot;: &quot;Laptop&quot;,
  &quot;price&quot;: 
}</code></pre><p>Bu JSON invalid.</p><p>Bunda Spring <code>HttpMessageNotReadableException</code> tashlaydi.</p><pre><code class="language-java">import org.springframework.http.converter.HttpMessageNotReadableException;

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity&lt;ErrorResponse&gt; handleNotReadable(
        HttpMessageNotReadableException ex,
        HttpServletRequest request
) {
    ErrorResponse response = new ErrorResponse(
            &quot;INVALID_JSON&quot;,
            &quot;Request body noto‘g‘ri yoki JSON format xato&quot;,
            HttpStatus.BAD_REQUEST.value(),
            request.getRequestURI(),
            LocalDateTime.now(),
            null
    );

    return ResponseEntity.badRequest().body(response);
}</code></pre><p>Response:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;INVALID_JSON&quot;,
  &quot;message&quot;: &quot;Request body noto‘g‘ri yoki JSON format xato&quot;,
  &quot;status&quot;: 400,
  &quot;path&quot;: &quot;/api/products&quot;,
  &quot;timestamp&quot;: &quot;2026-05-21T15:45:00&quot;,
  &quot;fields&quot;: null
}</code></pre><hr><h2>16. <code>Exception.class</code> fallback handler</h2><p>Har doim eng oxirida umumiy exception handler bo‘lgani yaxshi.</p><pre><code class="language-java">@ExceptionHandler(Exception.class)
public ResponseEntity&lt;ErrorResponse&gt; handleGeneralException(
        Exception ex,
        HttpServletRequest request
) {
    ErrorResponse response = new ErrorResponse(
            &quot;INTERNAL_SERVER_ERROR&quot;,
            &quot;Serverda kutilmagan xato yuz berdi&quot;,
            HttpStatus.INTERNAL_SERVER_ERROR.value(),
            request.getRequestURI(),
            LocalDateTime.now(),
            null
    );

    return ResponseEntity
            .status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(response);
}</code></pre><p>Muhim: production’da clientga <code>ex.getMessage()</code>ni doim qaytarish xavfli bo‘lishi mumkin.</p><p>Yomon:</p><pre><code class="language-java">&quot;message&quot;: ex.getMessage()</code></pre><p>Chunki unda database, table, stack trace yoki ichki texnik ma’lumot chiqib ketishi mumkin.</p><p>Yaxshiroq:</p><pre><code class="language-java">&quot;Serverda kutilmagan xato yuz berdi&quot;</code></pre><p>Lekin logga exceptionni yozish kerak.</p><hr><h2>17. Logging qo‘shish</h2><p>Global handler ichida xatoni log qilish foydali.</p><pre><code class="language-java">import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log =
            LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleGeneralException(
            Exception ex,
            HttpServletRequest request
    ) {
        log.error(&quot;Unexpected error. path={}&quot;, request.getRequestURI(), ex);

        ErrorResponse response = new ErrorResponse(
                &quot;INTERNAL_SERVER_ERROR&quot;,
                &quot;Serverda kutilmagan xato yuz berdi&quot;,
                HttpStatus.INTERNAL_SERVER_ERROR.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                null
        );

        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(response);
    }
}</code></pre><p>Clientga umumiy xabar qaytadi, lekin backend logda to‘liq stack trace qoladi.</p><hr><h2>18. To‘liq GlobalExceptionHandler</h2><pre><code class="language-java">import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log =
            LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(NotFoundException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleNotFound(
            NotFoundException ex,
            HttpServletRequest request
    ) {
        ErrorResponse response = new ErrorResponse(
                &quot;NOT_FOUND&quot;,
                ex.getMessage(),
                HttpStatus.NOT_FOUND.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                null
        );

        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
    }

    @ExceptionHandler(BadRequestException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleBadRequest(
            BadRequestException ex,
            HttpServletRequest request
    ) {
        ErrorResponse response = new ErrorResponse(
                &quot;BAD_REQUEST&quot;,
                ex.getMessage(),
                HttpStatus.BAD_REQUEST.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                null
        );

        return ResponseEntity.badRequest().body(response);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleValidation(
            MethodArgumentNotValidException ex,
            HttpServletRequest request
    ) {
        Map&lt;String, String&gt; fields = new HashMap&lt;&gt;();

        ex.getBindingResult()
                .getFieldErrors()
                .forEach(error -&gt;
                        fields.put(error.getField(), error.getDefaultMessage())
                );

        ErrorResponse response = new ErrorResponse(
                &quot;VALIDATION_ERROR&quot;,
                &quot;Request data noto‘g‘ri&quot;,
                HttpStatus.BAD_REQUEST.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                fields
        );

        return ResponseEntity.badRequest().body(response);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleConstraintViolation(
            ConstraintViolationException ex,
            HttpServletRequest request
    ) {
        Map&lt;String, String&gt; fields = new HashMap&lt;&gt;();

        ex.getConstraintViolations()
                .forEach(error -&gt;
                        fields.put(
                                error.getPropertyPath().toString(),
                                error.getMessage()
                        )
                );

        ErrorResponse response = new ErrorResponse(
                &quot;VALIDATION_ERROR&quot;,
                &quot;Request parameter noto‘g‘ri&quot;,
                HttpStatus.BAD_REQUEST.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                fields
        );

        return ResponseEntity.badRequest().body(response);
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleTypeMismatch(
            MethodArgumentTypeMismatchException ex,
            HttpServletRequest request
    ) {
        ErrorResponse response = new ErrorResponse(
                &quot;TYPE_MISMATCH&quot;,
                &quot;Parameter formati noto‘g‘ri: &quot; + ex.getName(),
                HttpStatus.BAD_REQUEST.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                null
        );

        return ResponseEntity.badRequest().body(response);
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleNotReadable(
            HttpMessageNotReadableException ex,
            HttpServletRequest request
    ) {
        ErrorResponse response = new ErrorResponse(
                &quot;INVALID_JSON&quot;,
                &quot;Request body noto‘g‘ri yoki JSON format xato&quot;,
                HttpStatus.BAD_REQUEST.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                null
        );

        return ResponseEntity.badRequest().body(response);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity&lt;ErrorResponse&gt; handleGeneralException(
            Exception ex,
            HttpServletRequest request
    ) {
        log.error(&quot;Unexpected error. path={}&quot;, request.getRequestURI(), ex);

        ErrorResponse response = new ErrorResponse(
                &quot;INTERNAL_SERVER_ERROR&quot;,
                &quot;Serverda kutilmagan xato yuz berdi&quot;,
                HttpStatus.INTERNAL_SERVER_ERROR.value(),
                request.getRequestURI(),
                LocalDateTime.now(),
                null
        );

        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(response);
    }
}</code></pre><hr><h2>19. <code>ProblemDetail</code> nima?</h2><p>Spring 6&#039;da <code>ProblemDetail</code> degan tayyor error response modeli bor.</p><p>U RFC 7807 uslubiga yaqin.</p><p>Oddiy misol:</p><pre><code class="language-java">import org.springframework.http.ProblemDetail;

@ExceptionHandler(NotFoundException.class)
public ProblemDetail handleNotFound(NotFoundException ex) {
    ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
    problem.setTitle(&quot;Resource not found&quot;);
    problem.setDetail(ex.getMessage());

    return problem;
}</code></pre><p>Response taxminan:</p><pre><code class="language-json">{
  &quot;type&quot;: &quot;about:blank&quot;,
  &quot;title&quot;: &quot;Resource not found&quot;,
  &quot;status&quot;: 404,
  &quot;detail&quot;: &quot;User topilmadi&quot;,
  &quot;instance&quot;: &quot;/api/users/99&quot;
}</code></pre><p><code>ProblemDetail</code> foydali, chunki u standartga yaqin. Lekin boshlang&#039;ich bosqichda avval o‘zingizning <code>ErrorResponse</code> formatini tushunib olish yaxshiroq.</p><hr><h2>20. <code>@ResponseStatus</code> bilan exception</h2><p>Yana bir usul bor:</p><pre><code class="language-java">@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {

    public NotFoundException(String message) {
        super(message);
    }
}</code></pre><p>Bu usulda <code>NotFoundException</code> tashlansa, status <code>404</code> bo‘ladi.</p><p>Lekin kamchiligi:</p><pre><code class="language-text">Response formatni to‘liq nazorat qilish qiyinroq
Har xil xatolarni bir xil JSON formatda qaytarish noqulayroq</code></pre><p>Shuning uchun real REST API’da <code>@RestControllerAdvice</code> ko‘proq ishlatiladi.</p><hr><h2>21. HTTP statuslarni to‘g‘ri tanlash</h2><table><tbody><tr><th rowspan="1" colspan="1"><p>Holat</p></th><th rowspan="1" colspan="1"><p>Status</p></th><th rowspan="1" colspan="1"><p>Misol</p></th></tr><tr><td rowspan="1" colspan="1"><p>Validation xato</p></td><td rowspan="1" colspan="1"><p><code>400 Bad Request</code></p></td><td rowspan="1" colspan="1"><p><code>name</code> bo‘sh</p></td></tr><tr><td rowspan="1" colspan="1"><p>Noto‘g‘ri request</p></td><td rowspan="1" colspan="1"><p><code>400 Bad Request</code></p></td><td rowspan="1" colspan="1"><p>email allaqachon mavjud</p></td></tr><tr><td rowspan="1" colspan="1"><p>Login kerak</p></td><td rowspan="1" colspan="1"><p><code>401 Unauthorized</code></p></td><td rowspan="1" colspan="1"><p>token yo‘q</p></td></tr><tr><td rowspan="1" colspan="1"><p>Ruxsat yo‘q</p></td><td rowspan="1" colspan="1"><p><code>403 Forbidden</code></p></td><td rowspan="1" colspan="1"><p>user admin emas</p></td></tr><tr><td rowspan="1" colspan="1"><p>Resource topilmadi</p></td><td rowspan="1" colspan="1"><p><code>404 Not Found</code></p></td><td rowspan="1" colspan="1"><p>product id mavjud emas</p></td></tr><tr><td rowspan="1" colspan="1"><p>Conflict</p></td><td rowspan="1" colspan="1"><p><code>409 Conflict</code></p></td><td rowspan="1" colspan="1"><p>duplicate unique value</p></td></tr><tr><td rowspan="1" colspan="1"><p>Server xatosi</p></td><td rowspan="1" colspan="1"><p><code>500 Internal Server Error</code></p></td><td rowspan="1" colspan="1"><p>kutilmagan exception</p></td></tr></tbody></table><p>Javachilar uchun eng ko‘p ishlatiladiganlari:</p><pre><code class="language-text">400
401
403
404
409
500</code></pre><hr><h2>22. <code>400</code> va <code>404</code> farqi</h2><h3><code>400 Bad Request</code></h3><p>Client noto‘g‘ri ma’lumot yubordi.</p><p>Misollar:</p><pre><code class="language-text">email format xato
age -5
price 0 dan kichik
JSON invalid</code></pre><h3><code>404 Not Found</code></h3><p>Client so‘ragan narsa topilmadi.</p><p>Misollar:</p><pre><code class="language-text">/api/users/99 → bunday user yo‘q
/api/products/123 → bunday product yo‘q</code></pre><hr><h2>23. <code>403</code> va <code>401</code> farqi</h2><h3><code>401 Unauthorized</code></h3><p>User hali authentication qilmagan.</p><p>Masalan:</p><pre><code class="language-text">Token yo‘q
Token noto‘g‘ri
Token muddati tugagan</code></pre><h3><code>403 Forbidden</code></h3><p>User login qilgan, lekin ruxsati yo‘q.</p><p>Masalan:</p><pre><code class="language-text">Oddiy user admin endpointga kirmoqchi</code></pre><hr><h2>24. Product API’da exception handling misoli</h2><h3>ProductService</h3><pre><code class="language-java">@Service
public class ProductService {

    private final ProductRepository productRepository;
    private final CategoryRepository categoryRepository;
    private final ProductMapper productMapper;

    public ProductService(ProductRepository productRepository,
                          CategoryRepository categoryRepository,
                          ProductMapper productMapper) {
        this.productRepository = productRepository;
        this.categoryRepository = categoryRepository;
        this.productMapper = productMapper;
    }

    @Transactional(readOnly = true)
    public ProductResponse getById(Long id) {
        Product product = productRepository.findById(id)
                .orElseThrow(() -&gt; new NotFoundException(&quot;Product topilmadi&quot;));

        return productMapper.toResponse(product);
    }

    @Transactional
    public ProductResponse create(ProductCreateRequest request) {
        if (productRepository.existsByName(request.name())) {
            throw new BadRequestException(&quot;Product name allaqachon mavjud&quot;);
        }

        Category category = categoryRepository.findById(request.categoryId())
                .orElseThrow(() -&gt; new NotFoundException(&quot;Category topilmadi&quot;));

        Product product = productMapper.toEntity(request, category);
        Product saved = productRepository.save(product);

        return productMapper.toResponse(saved);
    }

    @Transactional
    public ProductResponse update(Long id, ProductUpdateRequest request) {
        Product product = productRepository.findById(id)
                .orElseThrow(() -&gt; new NotFoundException(&quot;Product topilmadi&quot;));

        if (request.name() != null &amp;&amp; productRepository.existsByName(request.name())) {
            throw new BadRequestException(&quot;Product name allaqachon mavjud&quot;);
        }

        if (request.categoryId() != null) {
            Category category = categoryRepository.findById(request.categoryId())
                    .orElseThrow(() -&gt; new NotFoundException(&quot;Category topilmadi&quot;));

            product.setCategory(category);
        }

        productMapper.updateEntity(product, request);

        return productMapper.toResponse(product);
    }

    @Transactional
    public void delete(Long id) {
        if (!productRepository.existsById(id)) {
            throw new NotFoundException(&quot;Product topilmadi&quot;);
        }

        productRepository.deleteById(id);
    }
}</code></pre><hr><h2>25. Exceptionlarni package qilish</h2><p>Package-by-feature bo‘lsa:</p><pre><code class="language-text">src/main/java/com/example/demo
 ├── product
 │    ├── ProductController.java
 │    ├── ProductService.java
 │    └── ...
 ├── category
 │    └── ...
 └── common
      ├── exception
      │    ├── BadRequestException.java
      │    ├── NotFoundException.java
      │    └── GlobalExceptionHandler.java
      └── response
           └── ErrorResponse.java</code></pre><p><code>common</code> ichida butun projectga umumiy classlar turadi.</p><hr><h2>26. Eng ko‘p qilinadigan xatolar</h2><h3>Xato 1: Controller’da try-catch yozish</h3><p>Yomon:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public ResponseEntity&lt;?&gt; getById(@PathVariable Long id) {
    try {
        return ResponseEntity.ok(productService.getById(id));
    } catch (Exception ex) {
        return ResponseEntity.badRequest().body(ex.getMessage());
    }
}</code></pre><p>Bu yomon, chunki har bir controller’da bir xil try-catch ko‘payib ketadi.</p><p>Yaxshi:</p><pre><code class="language-java">@GetMapping(&quot;/{id}&quot;)
public ProductResponse getById(@PathVariable Long id) {
    return productService.getById(id);
}</code></pre><p>Exceptionni <code>GlobalExceptionHandler</code> ushlaydi.</p><hr><h3>Xato 2: Har joyda <code>RuntimeException</code></h3><p>Yomon:</p><pre><code class="language-java">throw new RuntimeException(&quot;Product not found&quot;);</code></pre><p>Yaxshi:</p><pre><code class="language-java">throw new NotFoundException(&quot;Product topilmadi&quot;);</code></pre><hr><h3>Xato 3: Hamma xatoga <code>500</code> qaytarish</h3><p>Yomon:</p><pre><code class="language-json">{
  &quot;status&quot;: 500,
  &quot;message&quot;: &quot;User not found&quot;
}</code></pre><p><code>User not found</code> - server xatosi emas. To‘g‘ri status:</p><pre><code class="language-text">404 Not Found</code></pre><hr><h3>Xato 4: Clientga stack trace qaytarish</h3><p>Yomon:</p><pre><code class="language-json">{
  &quot;message&quot;: &quot;could not execute statement; SQL [insert into users...]&quot;
}</code></pre><p>Bu xavfli. Ichki texnik ma’lumot chiqib ketadi.</p><p>Yaxshi:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;INTERNAL_SERVER_ERROR&quot;,
  &quot;message&quot;: &quot;Serverda kutilmagan xato yuz berdi&quot;
}</code></pre><hr><h3>Xato 5: Error response format har xil bo‘lishi</h3><p>Yomon:</p><pre><code class="language-json">&quot;User not found&quot;</code></pre><p>Boshqa endpointda:</p><pre><code class="language-json">{
  &quot;error&quot;: &quot;Email exists&quot;
}</code></pre><p>Yana boshqasida:</p><pre><code class="language-json">{
  &quot;message&quot;: &quot;Validation failed&quot;,
  &quot;errors&quot;: [...]
}</code></pre><p>Yaxshi: hammasi bir xil formatda:</p><pre><code class="language-json">{
  &quot;code&quot;: &quot;...&quot;,
  &quot;message&quot;: &quot;...&quot;,
  &quot;status&quot;: 400,
  &quot;path&quot;: &quot;...&quot;,
  &quot;timestamp&quot;: &quot;...&quot;,
  &quot;fields&quot;: {}
}</code></pre><hr><h2>27. Javachi uchun qisqa qoida</h2><pre><code class="language-text">Service exception tashlaydi.
Controller try-catch qilmaydi.
GlobalExceptionHandler exceptionni ushlaydi.
Clientga bir xil ErrorResponse qaytadi.
Logga texnik xato yoziladi.
Clientga xavfsiz xabar qaytariladi.</code></pre><hr><h2>28. Amaliy vazifa</h2><p>Oldingi <code>Product CRUD API</code> loyihasiga exception handling qo‘shing.</p><h3>Classlar</h3><pre><code class="language-text">common.exception.NotFoundException
common.exception.BadRequestException
common.exception.GlobalExceptionHandler
common.response.ErrorResponse</code></pre><h3>ErrorResponse</h3><pre><code class="language-java">public record ErrorResponse(
        String code,
        String message,
        int status,
        String path,
        LocalDateTime timestamp,
        Map&lt;String, String&gt; fields
) {
}</code></pre><h3>Handlerlar</h3><p>Quyidagilarni ushlang:</p><pre><code class="language-text">NotFoundException → 404
BadRequestException → 400
MethodArgumentNotValidException → 400
ConstraintViolationException → 400
MethodArgumentTypeMismatchException → 400
HttpMessageNotReadableException → 400
Exception → 500</code></pre><h3>ProductService’da ishlating</h3><pre><code class="language-text">Product topilmasa → NotFoundException
Category topilmasa → NotFoundException
Product name duplicate bo‘lsa → BadRequestException</code></pre><h3>Kutilgan response</h3><pre><code class="language-json">{
  &quot;code&quot;: &quot;NOT_FOUND&quot;,
  &quot;message&quot;: &quot;Product topilmadi&quot;,
  &quot;status&quot;: 404,
  &quot;path&quot;: &quot;/api/products/99&quot;,
  &quot;timestamp&quot;: &quot;2026-05-21T15:55:00&quot;,
  &quot;fields&quot;: null
}</code></pre><hr><h2>Xulosa</h2><p><strong>Exception handling</strong> - professional REST API yozishning majburiy qismi.</p><p>Eng muhimlari:</p><pre><code class="language-text">@RestControllerAdvice → global error handler
@ExceptionHandler → exception turini ushlaydi
Custom exception → xato turini aniq qiladi
ErrorResponse → clientga bir xil format
MethodArgumentNotValidException → @Valid xatolari
ConstraintViolationException → @RequestParam / @PathVariable validation
ProblemDetail → Spring 6 standart error modeli</code></pre><p>Eng asosiy flow:</p><pre><code class="language-text">Service’da xato yuz berdi
        ↓
Custom exception tashlandi
        ↓
GlobalExceptionHandler ushladi
        ↓
ErrorResponse qaytdi
        ↓
Client tushunarli xato oldi</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Database Migrations]]></title>
            <link>https://blog.jtscorp.uz/database-migrations</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/database-migrations</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Database migration</strong> - database strukturasini tartibli, versiyalangan va nazoratli o‘zgartirish usuli.</p><p>Bu bo‘lim ichida quyidagilar bor: <strong>Flyway setup</strong>, <code>V1__init.sql</code> convention, <strong>Liquibase alternative</strong>, migration versioning strategy, <code>spring.jpa.hibernate.ddl-auto</code> va nima uchun production’da ehtiyot bo‘lish kerakligi.</p><hr><h2>1. Database migration nima?</h2><p>Backend projectda database doim o‘zgaradi:</p><pre><code class="language-text">users jadvali qo‘shiladi
products jadvali qo‘shiladi
email column unique bo‘ladi
price column qo‘shiladi
index qo‘shiladi
foreign key qo‘shiladi</code></pre><p>Agar bularni qo‘lda database ichida o‘zgartirsak, muammo chiqadi:</p><pre><code class="language-text">Developer A database’da column qo‘shdi
Developer B bilmay qoldi
Production database boshqacha
Local database boshqacha
Test database boshqacha</code></pre><p>Shu muammoni <strong>migration tool</strong> hal qiladi.</p><p>Migration - database o‘zgarishlarini faylga yozib, Git orqali saqlash.</p><p>Masalan:</p><pre><code class="language-sql">CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price NUMERIC(19, 2) NOT NULL
);</code></pre><p>Bu SQL fayl project ichida turadi.</p><hr><h2>2. Migration nima uchun kerak?</h2><p>Migration ishlatilsa:</p><pre><code class="language-text">Database tarixi saqlanadi
Har bir o‘zgarish versiya bilan yuradi
Local, test, production database bir xil bo‘ladi
Team bilan ishlash osonlashadi
Deployment avtomatlashtiriladi
Rollback/recovery tushunarli bo‘ladi</code></pre><p>Oddiy qilib:</p><pre><code class="language-text">Code Git’da yuradi.
Database structure ham Git’da yurishi kerak.</code></pre><hr><h2>3. Migration bo‘lmasa nima bo‘ladi?</h2><p>Tasavvur qiling, siz <code>Product</code> entity qo‘shdingiz:</p><pre><code class="language-java">@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private BigDecimal price;
}</code></pre><p>Local’da ishladi. Chunki siz <code>ddl-auto=update</code> qilib qo‘ygansiz.</p><p>Lekin serverda error chiqdi:</p><pre><code class="language-text">relation &quot;products&quot; does not exist</code></pre><p>Nega?</p><p>Chunki production database’da <code>products</code> table yo‘q.</p><p>Migration bo‘lsa, bu table yaratish SQL fayl sifatida projectga qo‘shilgan bo‘lardi.</p><hr><h2>4. <code>spring.jpa.hibernate.ddl-auto</code> nima?</h2><p>Spring Boot’da Hibernate database tablelarni avtomatik yaratishi yoki o‘zgartirishi mumkin.</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: update</code></pre><p>Eng ko‘p uchraydigan qiymatlar:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Qiymat</p></th><th rowspan="1" colspan="1"><p>Ma’nosi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>none</code></p></td><td rowspan="1" colspan="1"><p>Hibernate schema’ga tegmaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>validate</code></p></td><td rowspan="1" colspan="1"><p>Entity va database mosligini tekshiradi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>update</code></p></td><td rowspan="1" colspan="1"><p>Entityga qarab database’ni update qilishga urinadi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>create</code></p></td><td rowspan="1" colspan="1"><p>Har startda schema yaratadi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>create-drop</code></p></td><td rowspan="1" colspan="1"><p>Startda yaratadi, stopda o‘chiradi</p></td></tr></tbody></table><hr><h2>5. Nega <code>ddl-auto=update</code> production’da yomon?</h2><p><code>ddl-auto=update</code> qulay ko‘rinadi, lekin production’da xavfli.</p><p>Sabablari:</p><pre><code class="language-text">Database o‘zgarishlari Git’da ko‘rinmaydi
Hibernate nima SQL ishlatishini to‘liq nazorat qilmaysiz
Column rename noto‘g‘ri tushunilishi mumkin
Data yo‘qolish xavfi bor
Index, constraint, default value kabi narsalar aniq boshqarilmaydi
Teamda kim nima o‘zgartirgani bilinmaydi</code></pre><p>Masalan siz field nomini o‘zgartirdingiz:</p><pre><code class="language-java">private String fullName;</code></pre><p>keyin:</p><pre><code class="language-java">private String name;</code></pre><p>Hibernate buni rename deb tushunmasligi mumkin. U yangi <code>name</code> column yaratib qo‘yishi mumkin. Eski <code>full_name</code> column qolib ketadi.</p><p>Production uchun yaxshiroq:</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: validate</code></pre><p>yoki:</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: none</code></pre><p>Database o‘zgarishlarini esa Flyway yoki Liquibase boshqaradi.</p><hr><h2>6. Flyway nima?</h2><p><strong>Flyway</strong> - database migration tool.</p><p>U project ichidagi SQL migration fayllarni o‘qiydi va database’ga navbat bilan qo‘llaydi.</p><p>Spring Boot bilan juda oson ishlaydi.</p><p>Flow:</p><pre><code class="language-text">Application start bo‘ladi
        ↓
Flyway migration fayllarni tekshiradi
        ↓
Qaysi migrationlar hali ishlamaganini aniqlaydi
        ↓
Ularni database’da bajaradi
        ↓
Application normal ishga tushadi</code></pre><hr><h2>7. Flyway dependency</h2><h3>Maven</h3><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.flywaydb&lt;/groupId&gt;
    &lt;artifactId&gt;flyway-core&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>PostgreSQL ishlatsangiz, ko‘pincha buni ham qo‘shiladi:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.flywaydb&lt;/groupId&gt;
    &lt;artifactId&gt;flyway-database-postgresql&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><h3>Gradle</h3><pre><code class="language-gradle">implementation &#039;org.flywaydb:flyway-core&#039;
implementation &#039;org.flywaydb:flyway-database-postgresql&#039;</code></pre><hr><h2>8. Flyway migration fayllari qayerda turadi?</h2><p>Default joy:</p><pre><code class="language-text">src/main/resources/db/migration</code></pre><p>Masalan:</p><pre><code class="language-text">src/main/resources/db/migration
 ├── V1__init.sql
 ├── V2__create_products_table.sql
 ├── V3__add_category_to_products.sql
 └── V4__create_users_table.sql</code></pre><p>Flyway application start bo‘lganda shu papkani o‘qiydi.</p><hr><h2>9. Flyway fayl nomlash qoidasi</h2><p>Flyway fayllar odatda shunday nomlanadi:</p><pre><code class="language-text">V1__init.sql</code></pre><p>Format:</p><pre><code class="language-text">V&lt;version&gt;__&lt;description&gt;.sql</code></pre><p>Muhim: <code>V1</code> dan keyin <strong>ikkita underscore</strong> bo‘ladi:</p><pre><code class="language-text">V1__init.sql</code></pre><p>Noto‘g‘ri:</p><pre><code class="language-text">V1_init.sql</code></pre><p>To‘g‘ri:</p><pre><code class="language-text">V1__init.sql</code></pre><p>Misollar:</p><pre><code class="language-text">V1__init.sql
V2__create_products.sql
V3__add_price_to_products.sql
V4__create_orders.sql</code></pre><hr><h2>10. Birinchi migration: <code>V1__init.sql</code></h2><p>Masalan product/category project uchun:</p><pre><code class="language-sql">CREATE TABLE categories (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL UNIQUE
);

CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price NUMERIC(19, 2) NOT NULL,
    quantity INTEGER NOT NULL DEFAULT 0,
    category_id BIGINT NOT NULL,

    CONSTRAINT fk_products_category
        FOREIGN KEY (category_id)
        REFERENCES categories(id)
);</code></pre><p>Bu fayl:</p><pre><code class="language-text">src/main/resources/db/migration/V1__init.sql</code></pre><p>ichida bo‘ladi.</p><p>Application ishga tushganda Flyway shu SQL’ni database’da bajaradi.</p><hr><h2>11. Flyway database’da nimani saqlaydi?</h2><p>Flyway database ichida maxsus table yaratadi:</p><pre><code class="language-text">flyway_schema_history</code></pre><p>Bu table ichida qaysi migrationlar bajarilgani saqlanadi.</p><p>Taxminan:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>installed_rank</p></th><th rowspan="1" colspan="1"><p>version</p></th><th rowspan="1" colspan="1"><p>description</p></th><th rowspan="1" colspan="1"><p>script</p></th><th rowspan="1" colspan="1"><p>success</p></th></tr><tr><td rowspan="1" colspan="1"><p>1</p></td><td rowspan="1" colspan="1"><p>1</p></td><td rowspan="1" colspan="1"><p>init</p></td><td rowspan="1" colspan="1"><p>V1__init.sql</p></td><td rowspan="1" colspan="1"><p>true</p></td></tr><tr><td rowspan="1" colspan="1"><p>2</p></td><td rowspan="1" colspan="1"><p>2</p></td><td rowspan="1" colspan="1"><p>create products</p></td><td rowspan="1" colspan="1"><p>V2__create_products.sql</p></td><td rowspan="1" colspan="1"><p>true</p></td></tr></tbody></table><p>Shuning uchun Flyway bir migrationni qayta-qayta ishlatmaydi.</p><p>Agar <code>V1__init.sql</code> allaqachon bajarilgan bo‘lsa, keyingi startda uni skip qiladi.</p><hr><h2>12. Yangi o‘zgarish qo‘shish</h2><p>Masalan <code>products</code> table’ga <code>description</code> column qo‘shish kerak.</p><p>Yomon yo‘l:</p><pre><code class="language-sql">ALTER TABLE products ADD COLUMN description TEXT;</code></pre><p>Buni database’da qo‘lda bajarib qo‘yish.</p><p>To‘g‘ri yo‘l:</p><p>Yangi migration fayl yaratiladi:</p><pre><code class="language-text">V2__add_description_to_products.sql</code></pre><p>Ichida:</p><pre><code class="language-sql">ALTER TABLE products
ADD COLUMN description TEXT;</code></pre><p>Endi Git’da ham ko‘rinadi:</p><pre><code class="language-text">V1__init.sql
V2__add_description_to_products.sql</code></pre><p>Teamdagi boshqa developer projectni pull qilsa, application start bo‘lganda Flyway uning local database’iga ham <code>description</code> column qo‘shadi.</p><hr><h2>13. Migration faylni o‘zgartirish mumkinmi?</h2><p>Muhim qoida:</p><pre><code class="language-text">Bajarilgan migration faylni o‘zgartirmang.</code></pre><p>Masalan <code>V1__init.sql</code> production’da ishlagan bo‘lsa, keyin uni edit qilish yomon.</p><p>Nega?</p><p>Flyway migration fayllarning checksum’ini saqlaydi. Agar ishlagan migration faylni o‘zgartirsangiz, Flyway error beradi.</p><p>Sababi: database tarixi buzilmasligi kerak.</p><p>To‘g‘ri yo‘l:</p><pre><code class="language-text">Eski migrationni o‘zgartirmang.
Yangi migration yarating.</code></pre><p>Masalan:</p><pre><code class="language-text">V3__alter_products_name_length.sql</code></pre><pre><code class="language-sql">ALTER TABLE products
ALTER COLUMN name TYPE VARCHAR(150);</code></pre><hr><h2>14. Migration versioning strategy</h2><p>Kichik projectlarda oddiy ketma-ket raqam yetadi:</p><pre><code class="language-text">V1__init.sql
V2__create_products.sql
V3__add_description.sql</code></pre><p>Lekin team katta bo‘lsa, conflict chiqishi mumkin.</p><p>Masalan ikki developer bir vaqtda yaratdi:</p><pre><code class="language-text">Developer A: V5__add_orders.sql
Developer B: V5__add_payments.sql</code></pre><p>Conflict.</p><p>Bunday holatda timestamp ishlatish mumkin:</p><pre><code class="language-text">V202605211030__create_orders.sql
V202605211045__create_payments.sql</code></pre><p>Format:</p><pre><code class="language-text">VYYYYMMDDHHMM__description.sql</code></pre><p>Masalan:</p><pre><code class="language-text">V202605211030__create_users_table.sql
V202605211045__add_index_to_users_email.sql</code></pre><p>Bu conflict ehtimolini kamaytiradi.</p><hr><h2>15. Flyway config</h2><p><code>application.yml</code>:</p><pre><code class="language-yaml">spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/shop_db
    username: postgres
    password: postgres

  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true

  flyway:
    enabled: true
    locations: classpath:db/migration</code></pre><p>Bu yerda:</p><pre><code class="language-text">ddl-auto: validate</code></pre><p>Hibernate schema yaratmaydi, faqat entity va database mosligini tekshiradi.</p><pre><code class="language-text">flyway.enabled: true</code></pre><p>Flyway migrationlarni ishlatadi.</p><hr><h2>16. Local development uchun nima qilish kerak?</h2><p>Local’da boshlanish uchun ba’zida:</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: update</code></pre><p>ishlatiladi.</p><p>Lekin boshlang&#039;ich bosqichidayoq yaxshi odat:</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: validate</code></pre><p>va Flyway migration yozish.</p><p>Development uchun:</p><pre><code class="language-text">Entity yozdingiz
Migration SQL yozdingiz
Application start qildingiz
Flyway migrationni bajardi
Hibernate validate qildi</code></pre><p>Bu professional workflow.</p><hr><h2>17. Entity va migration mos bo‘lishi kerak</h2><p>Agar entity:</p><pre><code class="language-java">@Entity
@Table(name = &quot;products&quot;)
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String name;

    @Column(nullable = false)
    private BigDecimal price;
}</code></pre><p>bo‘lsa, migration ham mos bo‘lishi kerak:</p><pre><code class="language-sql">CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price NUMERIC(19, 2) NOT NULL
);</code></pre><p>Agar entity’da <code>name</code> bor, lekin database’da <code>product_name</code> bo‘lsa, <code>ddl-auto=validate</code> error beradi.</p><p>Bu yaxshi. Chunki xatoni application startida bilib olasiz.</p><hr><h2>18. Index qo‘shish</h2><p>Database performance uchun index kerak bo‘ladi.</p><p>Masalan email bo‘yicha ko‘p qidiramiz:</p><pre><code class="language-java">Optional&lt;AppUser&gt; findByUsername(String username);</code></pre><p>Migration:</p><pre><code class="language-sql">CREATE UNIQUE INDEX ux_users_username
ON users(username);</code></pre><p>Yoki table ichida:</p><pre><code class="language-sql">CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    username VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    role VARCHAR(50) NOT NULL,
    enabled BOOLEAN NOT NULL DEFAULT true
);</code></pre><p>Agar keyin qo‘shilsa:</p><pre><code class="language-text">V3__add_unique_index_to_users_username.sql</code></pre><pre><code class="language-sql">CREATE UNIQUE INDEX ux_users_username
ON users(username);</code></pre><hr><h2>19. Foreign key qo‘shish</h2><p>Masalan <code>products.category_id</code> <code>categories.id</code>ga bog‘langan.</p><pre><code class="language-sql">ALTER TABLE products
ADD CONSTRAINT fk_products_category
FOREIGN KEY (category_id)
REFERENCES categories(id);</code></pre><p>Bu database darajasida himoya qiladi.</p><p>Ya’ni product mavjud bo‘lmagan category’ga bog‘lanib qolmaydi.</p><hr><h2>20. Default value qo‘shish</h2><p>Masalan <code>enabled</code> default <code>true</code> bo‘lsin:</p><pre><code class="language-sql">ALTER TABLE users
ADD COLUMN enabled BOOLEAN NOT NULL DEFAULT true;</code></pre><p>Agar table ichida yaratilsa:</p><pre><code class="language-sql">enabled BOOLEAN NOT NULL DEFAULT true</code></pre><p>Bu foydali, chunki eski data uchun ham qiymat bo‘ladi.</p><hr><h2>21. NOT NULL column qo‘shishda ehtiyot bo‘lish</h2><p>Agar table’da oldindan data bo‘lsa, bunday migration xato berishi mumkin:</p><pre><code class="language-sql">ALTER TABLE products
ADD COLUMN status VARCHAR(30) NOT NULL;</code></pre><p>Nega?</p><p>Chunki eski rows uchun <code>status</code> qiymati yo‘q.</p><p>Yaxshiroq:</p><pre><code class="language-sql">ALTER TABLE products
ADD COLUMN status VARCHAR(30) NOT NULL DEFAULT &#039;ACTIVE&#039;;</code></pre><p>Yoki ikki bosqichda:</p><pre><code class="language-sql">ALTER TABLE products
ADD COLUMN status VARCHAR(30);

UPDATE products
SET status = &#039;ACTIVE&#039;
WHERE status IS NULL;

ALTER TABLE products
ALTER COLUMN status SET NOT NULL;</code></pre><p>Bu production’da xavfsizroq.</p><hr><h2>22. Data migration</h2><p>Migration faqat table yaratish emas. Data o‘zgartirish ham bo‘lishi mumkin.</p><p>Masalan eski productlarda <code>quantity</code> null bo‘lsa:</p><pre><code class="language-sql">UPDATE products
SET quantity = 0
WHERE quantity IS NULL;</code></pre><p>Keyin constraint:</p><pre><code class="language-sql">ALTER TABLE products
ALTER COLUMN quantity SET NOT NULL;</code></pre><hr><h2>23. Seed data qo‘shish</h2><p>Local yoki test uchun boshlang‘ich data kerak bo‘lishi mumkin.</p><p>Masalan:</p><pre><code class="language-text">V2__insert_initial_categories.sql</code></pre><pre><code class="language-sql">INSERT INTO categories (name)
VALUES
    (&#039;Electronics&#039;),
    (&#039;Food&#039;),
    (&#039;Clothes&#039;);</code></pre><p>Lekin production seed data’da ehtiyot bo‘ling. Har bir insert idempotent bo‘lgani yaxshi.</p><p>PostgreSQL’da:</p><pre><code class="language-sql">INSERT INTO categories (name)
VALUES (&#039;Electronics&#039;)
ON CONFLICT (name) DO NOTHING;</code></pre><hr><h2>24. Repeatable migration</h2><p>Flyway’da oddiy versioned migrationlardan tashqari repeatable migration ham bor.</p><p>Nomlash:</p><pre><code class="language-text">R__create_views.sql</code></pre><p><code>R__</code> bilan boshlanadi.</p><p>U checksum o‘zgarsa qayta ishlaydi.</p><p>Ko‘proq view, function, procedure kabi obyektlar uchun ishlatiladi.</p><p>Boshlang&#039;ich bosqichida avval <code>V1</code>, <code>V2</code>, <code>V3</code> migrationlarni yaxshi tushunish yetarli.</p><hr><h2>25. Liquibase nima?</h2><p><strong>Liquibase</strong> - Flywayga alternativ migration tool.</p><p>Flyway ko‘pincha SQL fayllar bilan sodda ishlaydi.</p><p>Liquibase esa migrationlarni bir nechta formatda yozishga imkon beradi:</p><pre><code class="language-text">XML
YAML
JSON
SQL</code></pre><p>Masalan Liquibase YAML:</p><pre><code class="language-yaml">databaseChangeLog:
  - changeSet:
      id: 1
      author: jahon
      changes:
        - createTable:
            tableName: categories
            columns:
              - column:
                  name: id
                  type: BIGSERIAL
                  constraints:
                    primaryKey: true
              - column:
                  name: name
                  type: VARCHAR(100)
                  constraints:
                    nullable: false
                    unique: true</code></pre><hr><h3>Flyway vs Liquibase</h3><table><tbody><tr><th rowspan="1" colspan="1"><p>Tool</p></th><th rowspan="1" colspan="1"><p>Qachon qulay</p></th></tr><tr><td rowspan="1" colspan="1"><p>Flyway</p></td><td rowspan="1" colspan="1"><p>Oddiy, SQL-first, tushunish oson</p></td></tr><tr><td rowspan="1" colspan="1"><p>Liquibase</p></td><td rowspan="1" colspan="1"><p>Katta enterprise, ko‘p DB support, rollback metadata kerak bo‘lsa</p></td></tr></tbody></table><p>Java developer uchun tavsiya:</p><pre><code class="language-text">Avval Flyway o‘rganing.
Keyin Liquibase nima ekanini bilib qo‘ying.</code></pre><hr><h2>26. Product API uchun migrationlar</h2><p>Oldingi loyihamizga mos migrationlar.</p><h3><code>V1__create_categories_table.sql</code></h3><pre><code class="language-sql">CREATE TABLE categories (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL UNIQUE
);</code></pre><h3><code>V2__create_products_table.sql</code></h3><pre><code class="language-sql">CREATE TABLE products (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price NUMERIC(19, 2) NOT NULL,
    quantity INTEGER NOT NULL DEFAULT 0,
    category_id BIGINT NOT NULL,

    CONSTRAINT fk_products_category
        FOREIGN KEY (category_id)
        REFERENCES categories(id)
);</code></pre><h3><code>V3__insert_initial_categories.sql</code></h3><pre><code class="language-sql">INSERT INTO categories (name)
VALUES
    (&#039;Electronics&#039;),
    (&#039;Food&#039;),
    (&#039;Clothes&#039;);</code></pre><h3><code>V4__add_description_to_products.sql</code></h3><pre><code class="language-sql">ALTER TABLE products
ADD COLUMN description TEXT;</code></pre><hr><h2>27. Security darsidan keyingi users migration</h2><p>Oldingi Spring Security mavzusida <code>AppUser</code> entity bor edi.</p><p>Migration:</p><pre><code class="language-text">V5__create_users_table.sql</code></pre><pre><code class="language-sql">CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    username VARCHAR(100) NOT NULL,
    password VARCHAR(255) NOT NULL,
    role VARCHAR(50) NOT NULL,
    enabled BOOLEAN NOT NULL DEFAULT true
);

CREATE UNIQUE INDEX ux_users_username
ON users(username);</code></pre><p>Entity:</p><pre><code class="language-java">@Entity
@Table(name = &quot;users&quot;)
public class AppUser {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true, length = 100)
    private String username;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false, length = 50)
    private String role;

    @Column(nullable = false)
    private boolean enabled = true;
}</code></pre><hr><h2>28. Migration bilan <code>@Entity</code> annotationlar munosabati</h2><p>Entity’dagi annotationlar:</p><pre><code class="language-java">@Column(nullable = false, length = 100)</code></pre><p>database’ni yaratib bermasligi kerak, agar Flyway ishlatayotgan bo‘lsangiz.</p><p>Ular ko‘proq:</p><pre><code class="language-text">Hibernate validate qilishiga yordam beradi
Kod o‘qilganda constraintni ko‘rsatadi
JPA mappingni aniqlaydi</code></pre><p>Lekin haqiqiy schema:</p><pre><code class="language-sql">CREATE TABLE ...
ALTER TABLE ...</code></pre><p>migration fayllarda bo‘ladi.</p><hr><h2>29. Flyway error: migration o‘zgargan bo‘lsa</h2><p>Agar oldin bajarilgan migrationni edit qilsangiz, Flyway checksum error berishi mumkin.</p><p>Masalan:</p><pre><code class="language-text">Validate failed: Migration checksum mismatch</code></pre><p>Bunday holatda production’da migrationni o‘zgartirmang. Yangi migration yarating.</p><p>Local developmentda, agar database muhim bo‘lmasa, local DB’ni tozalab qaytadan migration qilish mumkin.</p><p>Masalan Docker database bo‘lsa:</p><pre><code class="language-bash">docker compose down -v
docker compose up -d</code></pre><p>Lekin production’da bunday qilinmaydi.</p><hr><h2>30. Migration yozishda eng yaxshi amaliyotlar</h2><h3>1. Har bir o‘zgarish alohida fayl</h3><p>Yomon:</p><pre><code class="language-text">V2__many_changes.sql</code></pre><p>Ichida hamma narsa aralash.</p><p>Yaxshiroq:</p><pre><code class="language-text">V2__create_products_table.sql
V3__add_product_description.sql
V4__add_product_indexes.sql</code></pre><hr><h3>2. Bajarilgan migrationni edit qilmang</h3><pre><code class="language-text">Eski migration tarix.
Yangi o‘zgarish yangi migration.</code></pre><hr><h3>3. Production’da <code>ddl-auto=update</code> ishlatmang</h3><p>Yaxshi variant:</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: validate</code></pre><hr><h3>4. Constraintlarni database’da ham yozing</h3><p>Faqat DTO validation yetarli emas.</p><pre><code class="language-sql">name VARCHAR(100) NOT NULL
username VARCHAR(100) NOT NULL UNIQUE
price NUMERIC(19,2) NOT NULL</code></pre><hr><h3>5. Indexlarni unutmang</h3><p>Ko‘p qidiriladigan columnlarga index:</p><pre><code class="language-sql">CREATE INDEX ix_products_category_id
ON products(category_id);</code></pre><hr><h3>6. Migrationni local’da tekshirib keyin push qiling</h3><pre><code class="language-text">Application start bo‘ldimi?
Flyway migration successmi?
Hibernate validate successmi?
Repository querylar ishlayaptimi?</code></pre><hr><h2>31. Ko‘p uchraydigan xatolar</h2><h3>Xato 1: <code>ddl-auto=update</code>ga suyanish</h3><p>Yomon:</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: update</code></pre><p>Production uchun xavfli.</p><p>Yaxshiroq:</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: validate</code></pre><hr><h3>Xato 2: Migration fayl nomida bitta underscore</h3><p>Yomon:</p><pre><code class="language-text">V1_init.sql</code></pre><p>To‘g‘ri:</p><pre><code class="language-text">V1__init.sql</code></pre><hr><h3>Xato 3: Bajarilgan migrationni edit qilish</h3><p>Yomon:</p><pre><code class="language-text">V1__init.sql faylni production’dan keyin o‘zgartirish</code></pre><p>To‘g‘ri:</p><pre><code class="language-text">V2__add_column.sql yaratish</code></pre><hr><h3>Xato 4: NOT NULL columnni default value’siz qo‘shish</h3><p>Yomon:</p><pre><code class="language-sql">ALTER TABLE products
ADD COLUMN status VARCHAR(30) NOT NULL;</code></pre><p>Yaxshiroq:</p><pre><code class="language-sql">ALTER TABLE products
ADD COLUMN status VARCHAR(30) NOT NULL DEFAULT &#039;ACTIVE&#039;;</code></pre><hr><h3>Xato 5: Foreign key yozmaslik</h3><p>Yomon:</p><pre><code class="language-sql">category_id BIGINT NOT NULL</code></pre><p>Yaxshiroq:</p><pre><code class="language-sql">category_id BIGINT NOT NULL,
CONSTRAINT fk_products_category
    FOREIGN KEY (category_id)
    REFERENCES categories(id)</code></pre><hr><h2>32. Java developer uchun qisqa qoida</h2><pre><code class="language-text">Entity - Java modeli
Migration - database haqiqati
Flyway - migrationlarni tartib bilan ishlatadi
ddl-auto=validate - entity va database mosligini tekshiradi
ddl-auto=update - production’da xavfli
Bajarilgan migration - o‘zgartirilmaydi
Yangi o‘zgarish - yangi migration fayl</code></pre><hr><h2>33. Amaliy vazifa</h2><p>Oldingi <code>Product CRUD API</code> loyihasiga Flyway qo‘shing.</p><h3>1. Dependency qo‘shing</h3><pre><code class="language-text">flyway-core
flyway-database-postgresql</code></pre><h3>2. Papka yarating</h3><pre><code class="language-text">src/main/resources/db/migration</code></pre><h3>3. Migration fayllar</h3><pre><code class="language-text">V1__create_categories_table.sql
V2__create_products_table.sql
V3__create_users_table.sql
V4__insert_initial_categories.sql</code></pre><h3>4. <code>application.yml</code></h3><pre><code class="language-yaml">spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/shop_db
    username: postgres
    password: postgres

  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true

  flyway:
    enabled: true
    locations: classpath:db/migration</code></pre><h3>5. Tekshirish</h3><p>Application start bo‘lganda database’da quyidagilar bo‘lsin:</p><pre><code class="language-text">categories
products
users
flyway_schema_history</code></pre><hr><h2>Xulosa</h2><p><strong>Database migrations</strong> - real backend projectlarda majburiy ko‘nikma.</p><p>Java developer quyidagilarni bilishi kerak:</p><pre><code class="language-text">Flyway nima?
Migration fayllar qayerda turadi?
V1__init.sql nomlash qoidasi
flyway_schema_history nima?
Nega bajarilgan migration edit qilinmaydi?
ddl-auto=update production’da nega xavfli?
ddl-auto=validate nima qiladi?
Foreign key, unique, index, default value qanday yoziladi?
Liquibase Flywayga qanday alternativa?</code></pre><p>Eng muhim professional qoida:</p><pre><code class="language-text">Database o‘zgarishi qo‘lda emas, migration orqali qilinadi.</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Configuration & Profiles]]></title>
            <link>https://blog.jtscorp.uz/configuration-profiles</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/configuration-profiles</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p><strong>Configuration &amp; profiles</strong> - Spring Boot application’ni turli muhitlarda boshqarish uchun ishlatiladi: local, development, test, production.</p><p>Bu bo‘lim ichida quyidagilar bor: <code>@ConfigurationProperties</code>, Spring Profiles, <code>@Profile</code>, <code>spring.profiles.active</code>, <code>application-dev.yml</code>, <code>application-prod.yml</code>, environment variables override.</p><hr><h2>1. Configuration nima?</h2><p>Configuration - application sozlamalari.</p><p>Masalan:</p><pre><code class="language-text">server port
database URL
database username/password
JWT secret
file upload limit
logging level
external API URL
mail server config</code></pre><p>Bularni kod ichiga qattiq yozish yomon.</p><p>Yomon:</p><pre><code class="language-java">String apiUrl = &quot;https://api.example.com&quot;;
String dbPassword = &quot;12345&quot;;</code></pre><p>Yaxshi:</p><pre><code class="language-yaml">app:
  external-api-url: https://api.example.com</code></pre><p>Keyin Java kodda config’dan o‘qiladi.</p><hr><h2>2. <code>application.properties</code> va <code>application.yml</code></h2><p>Spring Boot’da asosiy config fayl odatda:</p><pre><code class="language-text">src/main/resources/application.properties</code></pre><p>yoki:</p><pre><code class="language-text">src/main/resources/application.yml</code></pre><p>bo‘ladi.</p><hr><h3><code>application.properties</code></h3><pre><code class="language-properties">server.port=8080

spring.datasource.url=jdbc:postgresql://localhost:5432/shop_db
spring.datasource.username=postgres
spring.datasource.password=postgres

spring.jpa.hibernate.ddl-auto=validate</code></pre><hr><h3><code>application.yml</code></h3><pre><code class="language-yaml">server:
  port: 8080

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/shop_db
    username: postgres
    password: postgres

  jpa:
    hibernate:
      ddl-auto: validate</code></pre><p>Ikkalasi ham ishlaydi. Lekin katta configlarda <code>yml</code> o‘qishga qulayroq.</p><hr><h2>3. Nega config kerak?</h2><p>Config orqali application’ni kodni o‘zgartirmasdan boshqaramiz.</p><p>Masalan local’da database:</p><pre><code class="language-yaml">spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/shop_db</code></pre><p>Production’da esa:</p><pre><code class="language-yaml">spring:
  datasource:
    url: jdbc:postgresql://prod-db:5432/shop_db</code></pre><p>Kod bir xil. Faqat config boshqa.</p><hr><h2>4. Profile nima?</h2><p><strong>Profile</strong> - application qaysi muhitda ishlayotganini bildiradigan nom.</p><p>Ko‘p ishlatiladigan profile’lar:</p><pre><code class="language-text">local
dev
test
staging
prod</code></pre><p>Misol:</p><pre><code class="language-text">local → kompyuteringizda ishlaydi
dev → development server
test → test environment
prod → real userlar ishlatadigan production</code></pre><hr><h2>5. Profile fayllar</h2><p>Spring Boot profile bo‘yicha alohida config fayllarni o‘qiy oladi.</p><pre><code class="language-text">src/main/resources
 ├── application.yml
 ├── application-local.yml
 ├── application-dev.yml
 ├── application-test.yml
 └── application-prod.yml</code></pre><p>Asosiy <code>application.yml</code>:</p><pre><code class="language-yaml">spring:
  application:
    name: shop-api

  profiles:
    active: local</code></pre><p><code>application-local.yml</code>:</p><pre><code class="language-yaml">server:
  port: 8080

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/shop_db
    username: postgres
    password: postgres

  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true</code></pre><p><code>application-prod.yml</code>:</p><pre><code class="language-yaml">server:
  port: 8080

spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false</code></pre><p>Bu yerda production config’da password qattiq yozilmagan. Environment variable’dan olinadi.</p><hr><h2>6. <code>spring.profiles.active</code></h2><p>Qaysi profile ishlashini belgilaydi.</p><pre><code class="language-yaml">spring:
  profiles:
    active: dev</code></pre><p>Yoki command line orqali:</p><pre><code class="language-bash">java -jar app.jar --spring.profiles.active=prod</code></pre><p>Yoki environment variable orqali:</p><pre><code class="language-bash">SPRING_PROFILES_ACTIVE=prod</code></pre><p>Real production’da ko‘pincha environment variable orqali beriladi.</p><hr><h2>7. Profile qanday ishlaydi?</h2><p>Agar active profile:</p><pre><code class="language-yaml">spring:
  profiles:
    active: dev</code></pre><p>bo‘lsa, Spring Boot quyidagilarni o‘qiydi:</p><pre><code class="language-text">application.yml
application-dev.yml</code></pre><p><code>application-dev.yml</code> ichidagi qiymatlar <code>application.yml</code>dagi umumiy qiymatlarni override qilishi mumkin.</p><p>Masalan:</p><p><code>application.yml</code>:</p><pre><code class="language-yaml">server:
  port: 8080</code></pre><p><code>application-dev.yml</code>:</p><pre><code class="language-yaml">server:
  port: 8081</code></pre><p>Natija:</p><pre><code class="language-text">server.port = 8081</code></pre><hr><h2>8. <code>@Value</code> bilan config o‘qish</h2><p>Eng oddiy usul - <code>@Value</code>.</p><p><code>application.yml</code>:</p><pre><code class="language-yaml">app:
  upload-dir: uploads</code></pre><p>Java:</p><pre><code class="language-java">import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class FileService {

    @Value(&quot;${app.upload-dir}&quot;)
    private String uploadDir;

    public void printUploadDir() {
        System.out.println(uploadDir);
    }
}</code></pre><p>Bu ishlaydi, lekin katta configlar uchun uncha qulay emas.</p><hr><h3><code>@Value</code> kamchiligi</h3><p>Agar config ko‘p bo‘lsa:</p><pre><code class="language-java">@Value(&quot;${app.jwt.secret}&quot;)
private String secret;

@Value(&quot;${app.jwt.expiration-minutes}&quot;)
private Long expirationMinutes;

@Value(&quot;${app.jwt.issuer}&quot;)
private String issuer;</code></pre><p>Kod tarqalib ketadi.</p><p>Shuning uchun <code>@ConfigurationProperties</code> yaxshiroq.</p><hr><h3>9. <code>@ConfigurationProperties</code> nima?</h3><p><code>@ConfigurationProperties</code> - config qiymatlarini bitta classga type-safe qilib yig‘ib beradi.</p><p>Masalan <code>application.yml</code>:</p><pre><code class="language-yaml">app:
  jwt:
    secret: my-secret-key
    expiration-minutes: 60
    issuer: shop-api</code></pre><p>Config class:</p><pre><code class="language-java">import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = &quot;app.jwt&quot;)
public record JwtProperties(
        String secret,
        Long expirationMinutes,
        String issuer
) {
}</code></pre><p>Lekin bu classni Spring ro‘yxatdan o‘tkazish kerak.</p><hr><h2>10. <code>@EnableConfigurationProperties</code></h2><pre><code class="language-java">import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class PropertiesConfig {
}</code></pre><p>Endi <code>JwtProperties</code> bean bo‘ladi.</p><p>Service’da ishlatish:</p><pre><code class="language-java">import org.springframework.stereotype.Service;

@Service
public class JwtService {

    private final JwtProperties jwtProperties;

    public JwtService(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
    }

    public void printConfig() {
        System.out.println(jwtProperties.issuer());
        System.out.println(jwtProperties.expirationMinutes());
    }
}</code></pre><hr><h2>11. Mutable class bilan <code>@ConfigurationProperties</code></h2><p>Record o‘rniga oddiy class ham ishlatish mumkin.</p><pre><code class="language-java">import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = &quot;app.file&quot;)
public class FileStorageProperties {

    private String uploadDir;
    private Long maxSizeMb;

    public String getUploadDir() {
        return uploadDir;
    }

    public void setUploadDir(String uploadDir) {
        this.uploadDir = uploadDir;
    }

    public Long getMaxSizeMb() {
        return maxSizeMb;
    }

    public void setMaxSizeMb(Long maxSizeMb) {
        this.maxSizeMb = maxSizeMb;
    }
}</code></pre><p><code>application.yml</code>:</p><pre><code class="language-yaml">app:
  file:
    upload-dir: uploads
    max-size-mb: 10</code></pre><hr><h2>12. <code>@ConfigurationProperties</code> validation</h2><p>Config ham noto‘g‘ri bo‘lishi mumkin.</p><p>Masalan JWT secret bo‘sh bo‘lsa, application start bo‘lmasin.</p><pre><code class="language-java">import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Validated
@ConfigurationProperties(prefix = &quot;app.jwt&quot;)
public record JwtProperties(

        @NotBlank(message = &quot;JWT secret majburiy&quot;)
        String secret,

        @Min(value = 1, message = &quot;Expiration kamida 1 minut bo‘lishi kerak&quot;)
        Long expirationMinutes,

        @NotBlank(message = &quot;Issuer majburiy&quot;)
        String issuer
) {
}</code></pre><p>Agar config noto‘g‘ri bo‘lsa, application start vaqtida xato beradi.</p><p>Bu yaxshi. Chunki production’da noto‘g‘ri config bilan application ishga tushib ketmaydi.</p><hr><h2>13. Environment variable nima?</h2><p>Environment variable - server yoki OS darajasida beriladigan qiymat.</p><p>Masalan Linux’da:</p><pre><code class="language-bash">export DB_URL=jdbc:postgresql://prod-db:5432/shop_db
export DB_USERNAME=shop_user
export DB_PASSWORD=super-secret</code></pre><p>Spring Boot config’da:</p><pre><code class="language-yaml">spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}</code></pre><p>Bu production uchun muhim.</p><hr><h2>14. Default value berish</h2><p>Agar environment variable yo‘q bo‘lsa, default qiymat berish mumkin.</p><pre><code class="language-yaml">server:
  port: ${SERVER_PORT:8080}</code></pre><p>Ma’nosi:</p><pre><code class="language-text">Agar SERVER_PORT bor bo‘lsa → shuni ol
Agar yo‘q bo‘lsa → 8080 ishlat</code></pre><p>Yana:</p><pre><code class="language-yaml">app:
  file:
    upload-dir: ${UPLOAD_DIR:uploads}</code></pre><hr><h2>15. Secretlarni config faylga yozmang</h2><p>Yomon:</p><pre><code class="language-yaml">spring:
  datasource:
    password: my-production-password

app:
  jwt:
    secret: very-secret-key</code></pre><p>Nega yomon?</p><pre><code class="language-text">Gitga tushib qoladi
Developerlar ko‘radi
Repository leak bo‘lsa, production secret ochiladi
Secretni almashtirish qiyinlashadi</code></pre><p>Yaxshi:</p><pre><code class="language-yaml">spring:
  datasource:
    password: ${DB_PASSWORD}

app:
  jwt:
    secret: ${JWT_SECRET}</code></pre><hr><h2>16. Local uchun <code>.env</code> ishlatish</h2><p>Local development’da <code>.env</code> yoki IDE environment variables ishlatish mumkin.</p><p>Masalan <code>.env</code>:</p><pre><code class="language-env">DB_URL=jdbc:postgresql://localhost:5432/shop_db
DB_USERNAME=postgres
DB_PASSWORD=postgres
JWT_SECRET=local-secret-key</code></pre><p>Lekin <code>.env</code> odatda Gitga qo‘shilmaydi.</p><p><code>.gitignore</code>:</p><pre><code class="language-gitignore">.env</code></pre><hr><h2>17. <code>@Profile</code> nima?</h2><p><code>@Profile</code> - bean faqat ma’lum profile’da ishlashini bildiradi.</p><p>Masalan local profile’da test data yaratamiz:</p><pre><code class="language-java">import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

@Component
@Profile(&quot;local&quot;)
public class LocalDataInitializer {

    public LocalDataInitializer() {
        System.out.println(&quot;Local data initializer ishladi&quot;);
    }
}</code></pre><p>Bu bean faqat:</p><pre><code class="language-text">spring.profiles.active=local</code></pre><p>bo‘lsa yaratiladi.</p><hr><h3>Prod’da boshqa bean</h3><pre><code class="language-java">@Component
@Profile(&quot;prod&quot;)
public class ProdStartupLogger {

    public ProdStartupLogger() {
        System.out.println(&quot;Production mode&quot;);
    }
}</code></pre><p>Bu faqat production profile’da ishlaydi.</p><hr><h2>18. <code>@Profile</code> bilan interface implementation tanlash</h2><p>Masalan payment service local’da fake ishlasin, prod’da real ishlasin.</p><p>Interface:</p><pre><code class="language-java">public interface PaymentGateway {
    void pay(BigDecimal amount);
}</code></pre><p>Local implementation:</p><pre><code class="language-java">@Service
@Profile(&quot;local&quot;)
public class FakePaymentGateway implements PaymentGateway {

    @Override
    public void pay(BigDecimal amount) {
        System.out.println(&quot;Fake payment: &quot; + amount);
    }
}</code></pre><p>Production implementation:</p><pre><code class="language-java">@Service
@Profile(&quot;prod&quot;)
public class RealPaymentGateway implements PaymentGateway {

    @Override
    public void pay(BigDecimal amount) {
        // real payment provider call
    }
}</code></pre><p>Service:</p><pre><code class="language-java">@Service
public class OrderService {

    private final PaymentGateway paymentGateway;

    public OrderService(PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway;
    }
}</code></pre><p>Agar active profile <code>local</code> bo‘lsa, <code>FakePaymentGateway</code> inject bo‘ladi.</p><p>Agar <code>prod</code> bo‘lsa, <code>RealPaymentGateway</code> inject bo‘ladi.</p><hr><h2>19. Profile expression</h2><p><code>@Profile</code> ichida expression ham yozish mumkin.</p><pre><code class="language-java">@Profile(&quot;!prod&quot;)</code></pre><p>Ma’nosi:</p><pre><code class="language-text">prod bo‘lmagan hamma profile’da ishlaydi</code></pre><p>Misol:</p><pre><code class="language-java">@Component
@Profile(&quot;!prod&quot;)
public class DebugLogger {
}</code></pre><p>Yoki:</p><pre><code class="language-java">@Profile(&quot;dev | local&quot;)</code></pre><p>Ma’nosi:</p><pre><code class="language-text">dev yoki local bo‘lsa ishlaydi</code></pre><hr><h2>20. Logging config</h2><p>Local’da SQL va debug log ko‘proq kerak.</p><p><code>application-local.yml</code>:</p><pre><code class="language-yaml">logging:
  level:
    root: INFO
    org.hibernate.SQL: DEBUG
    org.hibernate.orm.jdbc.bind: TRACE</code></pre><p>Production’da kamroq:</p><pre><code class="language-yaml">logging:
  level:
    root: INFO
    org.hibernate.SQL: WARN</code></pre><p>Yomon amaliyot:</p><pre><code class="language-yaml">logging:
  level:
    root: TRACE</code></pre><p>Production’da haddan tashqari ko‘p log performance va xavfsizlik muammo qiladi.</p><hr><h2>21. Profile bo‘yicha database config</h2><p><code>application-local.yml</code>:</p><pre><code class="language-yaml">spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/shop_db
    username: postgres
    password: postgres</code></pre><p><code>application-dev.yml</code>:</p><pre><code class="language-yaml">spring:
  datasource:
    url: jdbc:postgresql://dev-db:5432/shop_db
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}</code></pre><p><code>application-prod.yml</code>:</p><pre><code class="language-yaml">spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}</code></pre><p>Farq:</p><pre><code class="language-text">local → oddiy lokal config bo‘lishi mumkin
dev/prod → secretlar env orqali</code></pre><hr><h2>22. Product API uchun config misol</h2><p><code>application.yml</code>:</p><pre><code class="language-yaml">spring:
  application:
    name: shop-api

  profiles:
    active: local

app:
  pagination:
    default-page-size: 20
    max-page-size: 100</code></pre><p><code>application-local.yml</code>:</p><pre><code class="language-yaml">server:
  port: 8080

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/shop_db
    username: postgres
    password: postgres

  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true

  flyway:
    enabled: true
    locations: classpath:db/migration</code></pre><p><code>application-prod.yml</code>:</p><pre><code class="language-yaml">server:
  port: ${SERVER_PORT:8080}

spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

  flyway:
    enabled: true
    locations: classpath:db/migration

app:
  pagination:
    default-page-size: ${DEFAULT_PAGE_SIZE:20}
    max-page-size: ${MAX_PAGE_SIZE:100}</code></pre><hr><h2>23. Pagination config’ni classga chiqarish</h2><p><code>application.yml</code>:</p><pre><code class="language-yaml">app:
  pagination:
    default-page-size: 20
    max-page-size: 100</code></pre><p>Properties class:</p><pre><code class="language-java">import jakarta.validation.constraints.Min;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Validated
@ConfigurationProperties(prefix = &quot;app.pagination&quot;)
public record PaginationProperties(

        @Min(1)
        int defaultPageSize,

        @Min(1)
        int maxPageSize
) {
}</code></pre><p>Config registration:</p><pre><code class="language-java">@Configuration
@EnableConfigurationProperties(PaginationProperties.class)
public class AppPropertiesConfig {
}</code></pre><p>Service’da ishlatish:</p><pre><code class="language-java">@Service
public class ProductService {

    private final PaginationProperties paginationProperties;

    public ProductService(PaginationProperties paginationProperties) {
        this.paginationProperties = paginationProperties;
    }

    public Page&lt;ProductResponse&gt; getAll(Integer page, Integer size) {
        int finalPage = page == null ? 0 : page;
        int finalSize = size == null
                ? paginationProperties.defaultPageSize()
                : Math.min(size, paginationProperties.maxPageSize());

        Pageable pageable = PageRequest.of(finalPage, finalSize);

        // repository call
        return Page.empty(pageable);
    }
}</code></pre><hr><h2>24. Config priority: qaysi biri ustun?</h2><p>Spring Boot’da config manbalari ko‘p bo‘lishi mumkin:</p><pre><code class="language-text">application.yml
application-prod.yml
environment variables
command line arguments</code></pre><p>Amaliy jihatdan eslab qolish:</p><pre><code class="language-text">Command line argument ko‘pincha yuqoriroq priority
Environment variable ham file configni override qila oladi
Profile-specific file umumiy application.yml ustidan yozadi</code></pre><p>Masalan:</p><p><code>application.yml</code>:</p><pre><code class="language-yaml">server:
  port: 8080</code></pre><p>Run command:</p><pre><code class="language-bash">java -jar app.jar --server.port=9090</code></pre><p>Natija:</p><pre><code class="language-text">server.port = 9090</code></pre><hr><h2>25. Docker bilan profile ishlatish</h2><p>Docker run:</p><pre><code class="language-bash">docker run \
  -e SPRING_PROFILES_ACTIVE=prod \
  -e DB_URL=jdbc:postgresql://db:5432/shop_db \
  -e DB_USERNAME=shop_user \
  -e DB_PASSWORD=secret \
  -p 8080:8080 \
  shop-api:latest</code></pre><p>Docker Compose:</p><pre><code class="language-yaml">services:
  app:
    image: shop-api:latest
    ports:
      - &quot;8080:8080&quot;
    environment:
      SPRING_PROFILES_ACTIVE: prod
      DB_URL: jdbc:postgresql://db:5432/shop_db
      DB_USERNAME: shop_user
      DB_PASSWORD: secret</code></pre><hr><h2>26. Test profile</h2><p>Testlar uchun alohida profile foydali.</p><p><code>application-test.yml</code>:</p><pre><code class="language-yaml">spring:
  datasource:
    url: jdbc:h2:mem:testdb
    username: sa
    password:

  jpa:
    hibernate:
      ddl-auto: create-drop</code></pre><p>Test class:</p><pre><code class="language-java">@SpringBootTest
@ActiveProfiles(&quot;test&quot;)
class ProductServiceTest {
}</code></pre><p>Bu test paytida <code>application-test.yml</code> ishlatadi.</p><hr><h2>27. <code>@ActiveProfiles</code></h2><p><code>@ActiveProfiles</code> testlarda profile tanlash uchun ishlatiladi.</p><pre><code class="language-java">@SpringBootTest
@ActiveProfiles(&quot;test&quot;)
class ProductControllerTest {
}</code></pre><p>Bu test environment’ni production configdan ajratadi.</p><p>Muhim:</p><pre><code class="language-text">Test hech qachon production databasega ulanmasligi kerak.</code></pre><hr><h2>28. Configuration class nima?</h2><p>Ba’zan beanlarni alohida config classda e’lon qilamiz.</p><pre><code class="language-java">@Configuration
public class AppConfig {

    @Bean
    public Clock clock() {
        return Clock.systemUTC();
    }
}</code></pre><p>Endi <code>Clock</code> kerak bo‘lgan joyda inject qilinadi:</p><pre><code class="language-java">@Service
public class TimeService {

    private final Clock clock;

    public TimeService(Clock clock) {
        this.clock = clock;
    }
}</code></pre><p>Profile bilan:</p><pre><code class="language-java">@Configuration
@Profile(&quot;test&quot;)
public class TestConfig {

    @Bean
    public Clock fixedClock() {
        return Clock.fixed(
                Instant.parse(&quot;2026-01-01T00:00:00Z&quot;),
                ZoneOffset.UTC
        );
    }
}</code></pre><hr><h2>29. Ko‘p uchraydigan xatolar</h2><h3>Xato 1: Production secretni Gitga yozish</h3><p>Yomon:</p><pre><code class="language-yaml">spring:
  datasource:
    password: real-prod-password</code></pre><p>To‘g‘ri:</p><pre><code class="language-yaml">spring:
  datasource:
    password: ${DB_PASSWORD}</code></pre><hr><h3>Xato 2: <code>prod</code> profile’da <code>show-sql: true</code></h3><p>Yomon:</p><pre><code class="language-yaml">spring:
  jpa:
    show-sql: true</code></pre><p>Production’da keraksiz SQL loglar chiqadi.</p><hr><h3>Xato 3: <code>ddl-auto=update</code>ni prod’da qoldirish</h3><p>Yomon:</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: update</code></pre><p>Yaxshi:</p><pre><code class="language-yaml">spring:
  jpa:
    hibernate:
      ddl-auto: validate</code></pre><hr><h3>Xato 4: Hamma configni <code>@Value</code> bilan tarqatib yuborish</h3><p>Yomon:</p><pre><code class="language-java">@Value(&quot;${app.jwt.secret}&quot;)
private String secret;

@Value(&quot;${app.jwt.expiration-minutes}&quot;)
private Long expiration;</code></pre><p>Katta config uchun yaxshiroq:</p><pre><code class="language-java">@ConfigurationProperties(prefix = &quot;app.jwt&quot;)
public record JwtProperties(...) {}</code></pre><hr><h3>Xato 5: Active profile’ni kodga qattiq bog‘lash</h3><p>Yomon:</p><pre><code class="language-yaml">spring:
  profiles:
    active: prod</code></pre><p>Buni production jar ichida qoldirish xavfli. Serverda environment variable orqali berish yaxshiroq:</p><pre><code class="language-bash">SPRING_PROFILES_ACTIVE=prod</code></pre><hr><h2>30. Java developer uchun qisqa qoida</h2><pre><code class="language-text">application.yml → umumiy config
application-local.yml → lokal muhit
application-dev.yml → dev server
application-prod.yml → production
spring.profiles.active → qaysi profile ishlashini tanlaydi
@Profile → bean faqat ma’lum profile’da yaratiladi
@Value → oddiy bitta qiymat uchun
@ConfigurationProperties → katta/type-safe config uchun
Environment variables → secret va server-specific config uchun</code></pre><hr><h2>31. Amaliy vazifa</h2><p>Oldingi <code>Product CRUD API</code> loyihasiga profile va config qo‘shing.</p><h3>Fayllar</h3><pre><code class="language-text">src/main/resources/application.yml
src/main/resources/application-local.yml
src/main/resources/application-prod.yml</code></pre><h3><code>application.yml</code></h3><pre><code class="language-yaml">spring:
  application:
    name: shop-api

  profiles:
    active: local

app:
  pagination:
    default-page-size: 20
    max-page-size: 100

  jwt:
    secret: ${JWT_SECRET:local-secret}
    expiration-minutes: ${JWT_EXPIRATION_MINUTES:60}
    issuer: shop-api</code></pre><h3><code>application-local.yml</code></h3><pre><code class="language-yaml">server:
  port: 8080

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/shop_db
    username: postgres
    password: postgres

  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: true

  flyway:
    enabled: true</code></pre><h3><code>application-prod.yml</code></h3><pre><code class="language-yaml">server:
  port: ${SERVER_PORT:8080}

spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

  flyway:
    enabled: true</code></pre><h3>Properties classlar</h3><pre><code class="language-text">PaginationProperties
JwtProperties
AppPropertiesConfig</code></pre><h3>Talablar</h3><pre><code class="language-text">JWT secret @ConfigurationProperties orqali o‘qilsin
Pagination max size configdan olinsin
Prod secretlar environment variable orqali berilsin
Testlarda @ActiveProfiles(&quot;test&quot;) ishlatilsin</code></pre><hr><h2>Xulosa</h2><p><strong>Configuration &amp; profiles</strong> real Spring Boot application’ni professional boshqarish uchun kerak.</p><p>Eng muhim fikr:</p><pre><code class="language-text">Kod o‘zgarmaydi.
Config muhitga qarab o‘zgaradi.</code></pre><p>Java developer quyidagilarni bilishi kerak:</p><pre><code class="language-text">application.yml
application-{profile}.yml
spring.profiles.active
@Profile
@Value
@ConfigurationProperties
@EnableConfigurationProperties
environment variables
default values: ${VAR:default}
local/dev/prod config ajratish
secretlarni Gitga yozmaslik</code></pre>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Transaction Management Deep]]></title>
            <link>https://blog.jtscorp.uz/transaction-management-deep</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/transaction-management-deep</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p>Bu bo‘lim quyidagilarni qamrab oladi: <strong>Propagation levels</strong>, <strong>Isolation levels</strong>, <strong>Rollback rules</strong>, <strong>TransactionTemplate</strong>, va <code>@Transactional</code> proxy/self-call muammolari.</p><hr><h2>1. Transaction nima?</h2><p><strong>Transaction</strong> - bir nechta database operatsiyani bitta yaxlit ish sifatida bajarish mexanizmi.</p><p>Masalan pul o‘tkazish:</p><pre><code class="language-text">1. Ali hisobidan 100 000 so‘m ayirish
2. Vali hisobiga 100 000 so‘m qo‘shish</code></pre><p>Bu ikkala amal ham bajarilishi kerak. Bittasi bajarilib, ikkinchisi xato bo‘lsa, sistema buziladi.</p><p>Shuning uchun transaction kerak:</p><pre><code class="language-text">Hammasi bajarildi  -&gt; COMMIT
Xato bo‘ldi        -&gt; ROLLBACK</code></pre><hr><h2>2. Transactionning asosiy maqsadi: ACID</h2><p>Transaction odatda <strong>ACID</strong> prinsipiga tayanadi.</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Prinsip</p></th><th rowspan="1" colspan="1"><p>Ma’nosi</p></th></tr><tr><td rowspan="1" colspan="1"><p>Atomicity</p></td><td rowspan="1" colspan="1"><p>Hammasi bajariladi yoki hech biri bajarilmaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Consistency</p></td><td rowspan="1" colspan="1"><p>Database noto‘g‘ri holatga tushib qolmaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Isolation</p></td><td rowspan="1" colspan="1"><p>Bir transaction boshqasiga noto‘g‘ri ta’sir qilmaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Durability</p></td><td rowspan="1" colspan="1"><p>Commit bo‘lgan data saqlanib qoladi</p></td></tr></tbody></table><p>Masalan:</p><pre><code class="language-java">@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    accountRepository.decreaseBalance(fromId, amount);
    accountRepository.increaseBalance(toId, amount);
}</code></pre><p>Agar <code>increaseBalance()</code> xato bersa, <code>decreaseBalance()</code> ham rollback bo‘ladi.</p><hr><h2>3. Spring’da transaction qanday ishlaydi?</h2><p>Spring’da eng ko‘p ishlatiladigan usul:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    // DB operations
}</code></pre><p>Lekin ichkarida Spring taxminan shunday qiladi:</p><pre><code class="language-java">try {
    transactionManager.begin();

    createOrder();

    transactionManager.commit();
} catch (Exception e) {
    transactionManager.rollback();
    throw e;
}</code></pre><p>Siz faqat annotation yozasiz. Spring esa transaction ochish, commit qilish, rollback qilishni boshqaradi.</p><hr><h2>4. <code>@Transactional</code> qayerga qo‘yiladi?</h2><p>Eng yaxshi joy - <strong>service layer</strong>.</p><pre><code class="language-java">@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final PaymentRepository paymentRepository;

    public OrderService(
            OrderRepository orderRepository,
            PaymentRepository paymentRepository
    ) {
        this.orderRepository = orderRepository;
        this.paymentRepository = paymentRepository;
    }

    @Transactional
    public void createOrder(CreateOrderRequest request) {
        Order order = new Order();
        orderRepository.save(order);

        Payment payment = new Payment();
        payment.setOrder(order);
        paymentRepository.save(payment);
    }
}</code></pre><p>Nega controllerga emas?</p><pre><code class="language-java">@RestController
public class OrderController {

    @Transactional // yomon amaliyot
    @PostMapping(&quot;/orders&quot;)
    public void create() {
    }
}</code></pre><p>Controller HTTP bilan ishlashi kerak. Transaction esa biznes operatsiya chegarasida bo‘lishi kerak.</p><p>To‘g‘ri chegara:</p><pre><code class="language-text">Controller -&gt; Service transaction -&gt; Repository</code></pre><hr><h2>5. <code>@Transactional</code> class level vs method level</h2><h3>Method level</h3><pre><code class="language-java">@Service
public class UserService {

    @Transactional
    public void createUser() {
    }

    public User getUser() {
    }
}</code></pre><p>Faqat <code>createUser()</code> transaction bilan ishlaydi.</p><h3>Class level</h3><pre><code class="language-java">@Service
@Transactional
public class UserService {

    public void createUser() {
    }

    public void updateUser() {
    }

    public User getUser() {
    }
}</code></pre><p>Bu classdagi public methodlarning hammasiga transaction qo‘llanadi.</p><p>Lekin o‘qish methodlari uchun alohida belgilash yaxshi:</p><pre><code class="language-java">@Transactional(readOnly = true)
public User getUser(Long id) {
    return userRepository.findById(id).orElseThrow();
}</code></pre><hr><h2>6. <code>readOnly = true</code> nima qiladi?</h2><pre><code class="language-java">@Transactional(readOnly = true)
public List&lt;User&gt; getUsers() {
    return userRepository.findAll();
}</code></pre><p>Bu Spring va ORMga signal beradi:</p><blockquote><p>&quot;Bu transaction ichida data o‘zgarmaydi, faqat o‘qiladi.&quot;</p></blockquote><p>Foydasi:</p><ul><li><p>Hibernate dirty checkingni kamaytirishi mumkin;</p></li><li><p>ba’zi DB optimizatsiyalar ishlashi mumkin;</p></li><li><p>kod niyatini aniq ko‘rsatadi.</p></li></ul><p>Lekin bu har doim &quot;write qilishni fizik bloklaydi&quot; degani emas. Ba’zi holatda DB yoki ORM baribir yozishga ruxsat berishi mumkin. Shuning uchun <code>readOnly = true</code>ni xavfsizlik mexanizmi deb emas, optimizatsiya va niyat belgisi deb tushunish kerak.</p><hr><h2>7. Rollback qoidalari</h2><p>Spring’da default qoida:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Exception turi</p></th><th rowspan="1" colspan="1"><p>Rollback bo‘ladimi?</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>RuntimeException</code></p></td><td rowspan="1" colspan="1"><p>Ha</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>Error</code></p></td><td rowspan="1" colspan="1"><p>Ha</p></td></tr><tr><td rowspan="1" colspan="1"><p>Checked exception</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td></tr></tbody></table><h3>RuntimeException rollback qiladi</h3><pre><code class="language-java">@Transactional
public void createOrder() {
    orderRepository.save(new Order());

    throw new RuntimeException(&quot;Something went wrong&quot;);
}</code></pre><p>Bu holatda <code>save()</code> rollback bo‘ladi.</p><hr><h3>Checked exception default rollback qilmaydi</h3><pre><code class="language-java">@Transactional
public void createOrder() throws IOException {
    orderRepository.save(new Order());

    throw new IOException(&quot;File error&quot;);
}</code></pre><p>Bu holatda default bo‘yicha rollback bo‘lmasligi mumkin, chunki <code>IOException</code> - checked exception.</p><p>Rollback kerak bo‘lsa:</p><pre><code class="language-java">@Transactional(rollbackFor = IOException.class)
public void createOrder() throws IOException {
    orderRepository.save(new Order());

    throw new IOException(&quot;File error&quot;);
}</code></pre><hr><h2>8. <code>rollbackFor</code> va <code>noRollbackFor</code></h2><h4><code>rollbackFor</code></h4><pre><code class="language-java">@Transactional(rollbackFor = Exception.class)
public void importUsers() throws Exception {
    userRepository.save(new User());

    throw new Exception(&quot;Import failed&quot;);
}</code></pre><p>Bu checked exception uchun ham rollback qiladi.</p><h3><code>noRollbackFor</code></h3><p>Ba’zan exception bo‘lsa ham rollback qilmaslik kerak bo‘ladi.</p><pre><code class="language-java">@Transactional(noRollbackFor = NotificationException.class)
public void createOrder() {
    orderRepository.save(new Order());

    try {
        notificationService.sendSms();
    } catch (NotificationException e) {
        throw e;
    }
}</code></pre><p>Lekin bunday kodni ehtiyotkorlik bilan yozish kerak. Ko‘pincha notificationni transaction ichida qilmaslik yaxshiroq.</p><hr><h2>9. Eng katta xato: exceptionni yutib yuborish</h2><p>Yomon:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    try {
        orderRepository.save(new Order());
        paymentRepository.save(new Payment());
    } catch (Exception e) {
        log.error(&quot;Error&quot;, e);
    }
}</code></pre><p>Bu yerda exception tashqariga chiqmaydi. Spring transactionni muvaffaqiyatli deb o‘ylaydi va <strong>commit</strong> qiladi.</p><p>To‘g‘ri:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    try {
        orderRepository.save(new Order());
        paymentRepository.save(new Payment());
    } catch (Exception e) {
        log.error(&quot;Error&quot;, e);
        throw e;
    }
}</code></pre><p>Yoki custom exception:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    try {
        orderRepository.save(new Order());
        paymentRepository.save(new Payment());
    } catch (Exception e) {
        throw new OrderCreationException(&quot;Order creation failed&quot;, e);
    }
}</code></pre><hr><h2>10. Propagation nima?</h2><p><strong>Propagation</strong> - agar bir transaction ichida boshqa transactionli method chaqirilsa, Spring nima qilishi kerakligini bildiradi.</p><p>Masalan:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    saveOrder();
    saveAudit();
}</code></pre><p>Agar <code>saveAudit()</code> ham <code>@Transactional</code> bo‘lsa, u yangi transaction ochadimi yoki mavjudiga qo‘shiladimi?</p><p>Buni <strong>propagation</strong> belgilaydi.</p><hr><h2>11. <code>Propagation.REQUIRED</code></h2><p>Default propagation - <code>REQUIRED</code>.</p><pre><code class="language-java">@Transactional(propagation = Propagation.REQUIRED)
public void saveOrder() {
}</code></pre><p>Ma’nosi:</p><pre><code class="language-text">Agar transaction bor bo‘lsa -&gt; unga qo‘shil
Agar transaction yo‘q bo‘lsa -&gt; yangi och</code></pre><p>Bu eng ko‘p ishlatiladigan holat.</p><p>Misol:</p><pre><code class="language-java">@Service
public class OrderService {

    @Transactional
    public void createOrder() {
        orderRepository.save(new Order());
        auditService.saveAudit();
    }
}</code></pre><pre><code class="language-java">@Service
public class AuditService {

    @Transactional
    public void saveAudit() {
        auditRepository.save(new AuditLog());
    }
}</code></pre><p>Bu yerda <code>saveAudit()</code> default <code>REQUIRED</code>, demak <code>createOrder()</code> transactioniga qo‘shiladi.</p><p>Agar <code>createOrder()</code> rollback bo‘lsa, audit ham rollback bo‘ladi.</p><hr><h2>12. <code>Propagation.REQUIRES_NEW</code></h2><pre><code class="language-java">@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAudit() {
}</code></pre><p>Ma’nosi:</p><pre><code class="language-text">Har doim yangi transaction och
Agar eski transaction bo‘lsa, vaqtincha to‘xtat</code></pre><p>Real misol: order xato bo‘lsa ham audit saqlansin.</p><pre><code class="language-java">@Service
public class OrderService {

    @Transactional
    public void createOrder() {
        try {
            orderRepository.save(new Order());

            throw new RuntimeException(&quot;Order failed&quot;);
        } catch (Exception e) {
            auditService.saveAudit(&quot;ORDER_FAILED&quot;);
            throw e;
        }
    }
}</code></pre><pre><code class="language-java">@Service
public class AuditService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveAudit(String message) {
        auditRepository.save(new AuditLog(message));
    }
}</code></pre><p>Natija:</p><pre><code class="language-text">Order -&gt; rollback
Audit -&gt; commit</code></pre><p>Bu productionda juda foydali.</p><p>Lekin haddan tashqari ishlatilsa:</p><ul><li><p>connection pool bosimi oshadi;</p></li><li><p>transaction oqimi murakkablashadi;</p></li><li><p>debugging qiyinlashadi.</p></li></ul><hr><h2>13. <code>Propagation.NESTED</code></h2><pre><code class="language-java">@Transactional(propagation = Propagation.NESTED)
public void saveChildData() {
}</code></pre><p>Ma’nosi:</p><pre><code class="language-text">Mavjud transaction ichida savepoint yaratadi
Ichki qism rollback bo‘lsa, butun transaction emas, shu qism rollback bo‘lishi mumkin</code></pre><p>Tasavvur:</p><pre><code class="language-text">Outer transaction start
  save order
  savepoint
    save optional details
    error
  rollback to savepoint
  continue outer transaction
commit outer transaction</code></pre><p>Misol:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    orderRepository.save(new Order());

    try {
        optionalService.saveOptionalDetails();
    } catch (Exception e) {
        log.warn(&quot;Optional details failed&quot;);
    }
}</code></pre><pre><code class="language-java">@Transactional(propagation = Propagation.NESTED)
public void saveOptionalDetails() {
    optionalRepository.save(new OptionalDetails());

    throw new RuntimeException(&quot;Optional failed&quot;);
}</code></pre><p><code>NESTED</code> hamma transaction managerlarda bir xil ishlamasligi mumkin. Odatda JDBC savepoint qo‘llab-quvvatlashi kerak.</p><hr><h2>14. Boshqa propagation turlari</h2><table><tbody><tr><th rowspan="1" colspan="1"><p>Propagation</p></th><th rowspan="1" colspan="1"><p>Ma’nosi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>REQUIRED</code></p></td><td rowspan="1" colspan="1"><p>Bor transactionga qo‘shiladi, bo‘lmasa yangi ochadi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>REQUIRES_NEW</code></p></td><td rowspan="1" colspan="1"><p>Har doim yangi transaction ochadi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>NESTED</code></p></td><td rowspan="1" colspan="1"><p>Existing transaction ichida savepoint ishlatadi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>SUPPORTS</code></p></td><td rowspan="1" colspan="1"><p>Transaction bo‘lsa ishlatadi, bo‘lmasa transactionsiz ishlaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>NOT_SUPPORTED</code></p></td><td rowspan="1" colspan="1"><p>Transactionsiz ishlaydi, bor transactionni suspend qiladi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>MANDATORY</code></p></td><td rowspan="1" colspan="1"><p>Transaction bo‘lishi shart, bo‘lmasa xato</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>NEVER</code></p></td><td rowspan="1" colspan="1"><p>Transaction bo‘lmasligi kerak, bo‘lsa xato</p></td></tr></tbody></table><p>Eng ko‘p ishlatiladi:</p><pre><code class="language-text">REQUIRED
REQUIRES_NEW
NESTED</code></pre><hr><h2>15. Isolation nima?</h2><p><strong>Isolation</strong> - parallel transactionlar bir-birini qanday ko‘rishini belgilaydi.</p><p>Bir vaqtning o‘zida ikkita request keldi:</p><pre><code class="language-text">Transaction A: user balansini o‘qiyapti
Transaction B: shu balansni o‘zgartiryapti</code></pre><p>Savol:</p><blockquote><p>A transaction B hali commit qilmagan datani ko‘rishi mumkinmi?</p></blockquote><p>Buni isolation level hal qiladi.</p><hr><h2>16. Isolation muammolari</h2><h3>16.1 Dirty Read</h3><p>Bir transaction boshqa transactionning hali commit qilinmagan datasini o‘qiydi.</p><pre><code class="language-text">Transaction B: balance = 100 dan 50 qildi, lekin commit qilmadi
Transaction A: balance = 50 deb o‘qidi
Transaction B: rollback qildi</code></pre><p>A noto‘g‘ri data o‘qidi.</p><hr><h3>16.2 Non-repeatable Read</h3><p>Bir transaction bir xil rowni ikki marta o‘qiydi, lekin natija o‘zgarib qoladi.</p><pre><code class="language-text">Transaction A: balance = 100 o‘qidi
Transaction B: balance = 200 qilib commit qildi
Transaction A: yana o‘qidi -&gt; balance = 200</code></pre><p>Bitta transaction ichida bir xil row har xil chiqdi.</p><hr><h3>16.3 Phantom Read</h3><p>Bir transaction bir xil queryni ikki marta ishlatadi, lekin ikkinchi safar yangi rowlar paydo bo‘ladi.</p><pre><code class="language-text">Transaction A: SELECT * FROM orders WHERE status = &#039;NEW&#039; -&gt; 10 ta
Transaction B: yangi NEW order qo‘shdi, commit qildi
Transaction A: yana query qildi -&gt; 11 ta</code></pre><hr><h2>17. Isolation levellar</h2><table><tbody><tr><th rowspan="1" colspan="1"><p>Isolation</p></th><th rowspan="1" colspan="1"><p>Dirty Read</p></th><th rowspan="1" colspan="1"><p>Non-repeatable Read</p></th><th rowspan="1" colspan="1"><p>Phantom Read</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>READ_UNCOMMITTED</code></p></td><td rowspan="1" colspan="1"><p>Mumkin</p></td><td rowspan="1" colspan="1"><p>Mumkin</p></td><td rowspan="1" colspan="1"><p>Mumkin</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>READ_COMMITTED</code></p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td><td rowspan="1" colspan="1"><p>Mumkin</p></td><td rowspan="1" colspan="1"><p>Mumkin</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>REPEATABLE_READ</code></p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td><td rowspan="1" colspan="1"><p>DBga bog‘liq</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>SERIALIZABLE</code></p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td><td rowspan="1" colspan="1"><p>Yo‘q</p></td></tr></tbody></table><hr><h2>18. <code>READ_COMMITTED</code></h2><pre><code class="language-java">@Transactional(isolation = Isolation.READ_COMMITTED)
public void process() {
}</code></pre><p>Ma’nosi:</p><blockquote><p>Faqat commit qilingan datani o‘qiydi.</p></blockquote><p>Ko‘p database’larda default darajaga yaqin. Masalan, PostgreSQL default isolation - <code>READ COMMITTED</code>.</p><p>Bu odatda ko‘p business applicationlar uchun yetarli.</p><hr><h2>19. <code>REPEATABLE_READ</code></h2><pre><code class="language-java">@Transactional(isolation = Isolation.REPEATABLE_READ)
public void process() {
}</code></pre><p>Ma’nosi:</p><blockquote><p>Bitta transaction ichida o‘qilgan row qayta o‘qilganda o‘zgarmas bo‘lib ko‘rinadi.</p></blockquote><p>Bu inventory, balance, stock kabi joylarda foydali bo‘lishi mumkin.</p><p>Lekin har doim ham race conditionni to‘liq hal qilmaydi. Ba’zan explicit lock yoki optimistic locking kerak bo‘ladi.</p><hr><h2>20. <code>SERIALIZABLE</code></h2><pre><code class="language-java">@Transactional(isolation = Isolation.SERIALIZABLE)
public void process() {
}</code></pre><p>Eng kuchli isolation.</p><p>Ma’nosi:</p><blockquote><p>Transactionlar xuddi ketma-ket bajarilgandek natija beradi.</p></blockquote><p>Kamchiligi:</p><ul><li><p>sekinroq;</p></li><li><p>locklar ko‘payadi;</p></li><li><p>deadlock ehtimoli oshadi;</p></li><li><p>throughput pasayadi.</p></li></ul><p>Shuning uchun uni default qilib qo‘yish noto‘g‘ri.</p><hr><h2>21. Real muammo: lost update</h2><p>Ikki user bir vaqtda bitta mahsulot stockini kamaytiryapti.</p><pre><code class="language-text">Initial stock = 10

Transaction A: stock = 10 o‘qidi
Transaction B: stock = 10 o‘qidi

A: stock = 9 saqladi
B: stock = 9 saqladi</code></pre><p>Aslida stock 8 bo‘lishi kerak edi, lekin 9 bo‘lib qoldi.</p><p>Bu <strong>lost update</strong>.</p><p>Yechimlar:</p><ul><li><p>optimistic locking;</p></li><li><p>pessimistic locking;</p></li><li><p>atomic update query;</p></li><li><p>yuqoriroq isolation;</p></li><li><p>business constraintlar.</p></li></ul><hr><h2>22. Optimistic locking</h2><p>Entityga version qo‘shiladi:</p><pre><code class="language-java">@Entity
public class Product {

    @Id
    private Long id;

    private int stock;

    @Version
    private Long version;
}</code></pre><p>Agar ikki transaction bitta rowni update qilsa, bittasi muvaffaqiyatli bo‘ladi, ikkinchisi <code>OptimisticLockException</code> oladi.</p><p>Bu high-read, low-conflict systemlarda yaxshi.</p><hr><h2>23. Pessimistic locking</h2><p>Rowni lock qilib qo‘yish.</p><pre><code class="language-java">public interface ProductRepository extends JpaRepository&lt;Product, Long&gt; {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query(&quot;select p from Product p where p.id = :id&quot;)
    Optional&lt;Product&gt; findByIdForUpdate(Long id);
}</code></pre><p>Service:</p><pre><code class="language-java">@Transactional
public void decreaseStock(Long productId) {
    Product product = productRepository.findByIdForUpdate(productId)
            .orElseThrow();

    product.decreaseStock();
}</code></pre><p>Bu holatda boshqa transaction shu rowni yozish uchun kutadi.</p><p>Kamchiligi:</p><ul><li><p>lock kutish;</p></li><li><p>deadlock;</p></li><li><p>throughput pasayishi.</p></li></ul><hr><h2>24. Atomic update query</h2><p>Ko‘p holatda eng sodda va kuchli yechim:</p><pre><code class="language-java">@Modifying
@Query(&quot;&quot;&quot;
    update Product p
    set p.stock = p.stock - :amount
    where p.id = :id and p.stock &gt;= :amount
&quot;&quot;&quot;)
int decreaseStock(Long id, int amount);</code></pre><p>Service:</p><pre><code class="language-java">@Transactional
public void buy(Long productId, int amount) {
    int updated = productRepository.decreaseStock(productId, amount);

    if (updated == 0) {
        throw new IllegalStateException(&quot;Not enough stock&quot;);
    }
}</code></pre><p>Bu <code>read -&gt; modify -&gt; write</code> muammosini kamaytiradi.</p><hr><h2>25. Transaction va flush</h2><p>JPA/Hibernate’da <code>save()</code> chaqirilganda SQL darrov DBga ketmasligi mumkin.</p><pre><code class="language-java">@Transactional
public void createUser() {
    userRepository.save(new User(&quot;Ali&quot;));

    System.out.println(&quot;Saved&quot;);
}</code></pre><p>Hibernate SQLni odatda:</p><ul><li><p>flush paytida;</p></li><li><p>querydan oldin;</p></li><li><p>transaction commit oldidan;</p></li></ul><p>yuboradi.</p><p>Majburiy flush:</p><pre><code class="language-java">userRepository.saveAndFlush(user);</code></pre><p>Yoki:</p><pre><code class="language-java">entityManager.flush();</code></pre><p>Lekin <code>flush()</code> commit degani emas. Flush SQLni DBga yuboradi, transaction hali rollback bo‘lishi mumkin.</p><hr><h2>26. Transaction va Lazy loading</h2><pre><code class="language-java">@Transactional(readOnly = true)
public Order getOrder(Long id) {
    Order order = orderRepository.findById(id).orElseThrow();
    order.getItems().size();
    return order;
}</code></pre><p><code>items</code> LAZY bo‘lsa, transaction ochiq paytda yuklanadi.</p><p>Lekin transactiondan tashqarida:</p><pre><code class="language-java">Order order = orderService.getOrder(id);
order.getItems().size(); // LazyInitializationException</code></pre><p>Bu muammo chiqishi mumkin.</p><p>Yechimlar:</p><ul><li><p>DTOga map qilish transaction ichida;</p></li><li><p>fetch join;</p></li><li><p>entity graph;</p></li><li><p>queryni to‘g‘ri yozish;</p></li><li><p>Open Session in View’ga suyanmaslik.</p></li></ul><hr><h2>27. <code>@Transactional</code> va self-invocation</h2><p>Bu juda muhim.</p><pre><code class="language-java">@Service
public class UserService {

    public void register() {
        saveUser();
    }

    @Transactional
    public void saveUser() {
        userRepository.save(new User());
    }
}</code></pre><p>Bu yerda <code>register()</code> ichidan <code>saveUser()</code> chaqirilsa, transaction ishlamasligi mumkin.</p><p>Sabab:</p><pre><code class="language-text">this.saveUser()</code></pre><p>proxy orqali o‘tmaydi.</p><p>Tashqaridan chaqirilsa:</p><pre><code class="language-text">Controller -&gt; UserService Proxy -&gt; saveUser()</code></pre><p>ishlaydi.</p><p>Ichkaridan chaqirilsa:</p><pre><code class="language-text">UserService object -&gt; this.saveUser()</code></pre><p>proxy chetlab o‘tiladi.</p><hr><h2>28. Self-invocation yechimi</h2><p>Eng toza yechim - methodlarni boshqa servicega ajratish.</p><pre><code class="language-java">@Service
public class UserRegistrationService {

    private final UserPersistenceService userPersistenceService;

    public UserRegistrationService(UserPersistenceService userPersistenceService) {
        this.userPersistenceService = userPersistenceService;
    }

    public void register() {
        userPersistenceService.saveUser();
    }
}</code></pre><pre><code class="language-java">@Service
public class UserPersistenceService {

    @Transactional
    public void saveUser() {
        userRepository.save(new User());
    }
}</code></pre><p>Bu yerda chaqiruv proxy orqali o‘tadi.</p><hr><h2>29. Private methodda <code>@Transactional</code></h2><p>Ishlamaydi.</p><pre><code class="language-java">@Transactional
private void saveUser() {
}</code></pre><p>Spring AOP proxy private methodni intercept qila olmaydi.</p><p>Odatda transaction methodlari <code>public</code> bo‘lishi kerak.</p><hr><h2>30. Final method/class muammosi</h2><p>CGLIB proxy final methodni override qila olmaydi.</p><p>Yomon:</p><pre><code class="language-java">@Service
public final class PaymentService {

    @Transactional
    public void pay() {
    }
}</code></pre><p>Yoki:</p><pre><code class="language-java">@Service
public class PaymentService {

    @Transactional
    public final void pay() {
    }
}</code></pre><p>Bunday holatlarda transaction ishlamasligi yoki kutilmagan muammo berishi mumkin.</p><hr><h2>31. Multiple transaction manager</h2><p>Ba’zan projectda bir nechta database bo‘ladi.</p><pre><code class="language-java">@Transactional(transactionManager = &quot;orderTransactionManager&quot;)
public void createOrder() {
}</code></pre><p>Yoki:</p><pre><code class="language-java">@Transactional(transactionManager = &quot;paymentTransactionManager&quot;)
public void createPayment() {
}</code></pre><p>Agar noto‘g‘ri transaction manager tanlansa, transaction kutganingizdek ishlamaydi.</p><p>Distributed transaction esa alohida murakkab mavzu. Ko‘p microservice architecture’da 2PC o‘rniga:</p><ul><li><p>Saga;</p></li><li><p>transactional outbox;</p></li><li><p>idempotency;</p></li><li><p>event-driven consistency;</p></li></ul><p>ishlatiladi.</p><hr><h2>32. Programmatic transaction: <code>TransactionTemplate</code></h2><p>Ba’zan annotation yetarli bo‘lmaydi. Transaction chegarasini kod ichida aniq boshqarish kerak bo‘ladi.</p><pre><code class="language-java">@Service
public class OrderService {

    private final TransactionTemplate transactionTemplate;

    public OrderService(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    public void createOrder() {
        transactionTemplate.execute(status -&gt; {
            orderRepository.save(new Order());
            paymentRepository.save(new Payment());
            return null;
        });
    }
}</code></pre><p>Bu <strong>programmatic transaction</strong> deyiladi.</p><hr><h2>33. <code>TransactionTemplate</code> qachon kerak?</h2><p>Masalan, bitta method ichida:</p><pre><code class="language-text">1. DB transactionda order yaratish
2. Transactiondan tashqarida external API chaqirish
3. Yana alohida transactionda audit yozish</code></pre><p>Annotation bilan bu ko‘pincha noqulay.</p><pre><code class="language-java">public void processOrder() {
    Long orderId = transactionTemplate.execute(status -&gt; {
        Order order = orderRepository.save(new Order());
        return order.getId();
    });

    externalPaymentClient.charge(orderId);

    transactionTemplate.execute(status -&gt; {
        auditRepository.save(new AuditLog(&quot;PAYMENT_CHARGED&quot;));
        return null;
    });
}</code></pre><p>Bu yerda transaction chegaralari aniq ko‘rinib turibdi.</p><hr><h2>34. Transaction ichida external API chaqirish xatosi</h2><p>Yomon:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    orderRepository.save(new Order());

    paymentClient.charge(); // external API

    orderRepository.updateStatus();
}</code></pre><p>Nega yomon?</p><ul><li><p>DB connection uzoq vaqt band bo‘ladi;</p></li><li><p>external API sekin bo‘lsa transaction cho‘ziladi;</p></li><li><p>timeout bo‘lishi mumkin;</p></li><li><p>locklar uzoq ushlanadi;</p></li><li><p>rollback external API’ni ortga qaytara olmaydi.</p></li></ul><p>Yaxshiroq yondashuv:</p><pre><code class="language-text">1. Order CREATED holatda saqlanadi
2. Payment event yuboriladi
3. Payment natijasiga qarab status update bo‘ladi</code></pre><p>Yoki transactional outbox pattern.</p><hr><h2>35. Transactional outbox qisqacha</h2><p>Muammo:</p><pre><code class="language-text">DBga order saqlandi
Kafka/RabbitMQga event yuborish kerak</code></pre><p>Agar DB commit bo‘ldi, lekin event yuborilmadi - data mismatch.</p><p>Outbox yechim:</p><pre><code class="language-text">Bitta DB transaction ichida:
1. Order save
2. Outbox event save

Keyin alohida worker:
1. Outbox eventlarni o‘qiydi
2. Kafka/RabbitMQga yuboradi
3. sent deb belgilaydi</code></pre><p>Service:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    Order order = orderRepository.save(new Order());

    outboxRepository.save(new OutboxEvent(
            &quot;ORDER_CREATED&quot;,
            order.getId()
    ));
}</code></pre><p>Bu microservice architecture’da juda kerakli pattern.</p><hr><h2>36. <code>@Transactional</code> va async</h2><p>Ehtiyot bo‘lish kerak:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    orderRepository.save(new Order());

    asyncService.sendNotification();
}</code></pre><pre><code class="language-java">@Async
public void sendNotification() {
}</code></pre><p><code>@Async</code> boshqa threadda ishlaydi. Transaction context avtomatik ravishda boshqa threadga o‘tmaydi.</p><p>Shuning uchun async method ichida lazy entity, transaction context yoki sessionga suyanish xato.</p><p>Yaxshi yondashuv:</p><pre><code class="language-java">@Transactional
public void createOrder() {
    Order order = orderRepository.save(new Order());

    eventPublisher.publishEvent(new OrderCreatedEvent(order.getId()));
}</code></pre><p>Keyin transaction commit bo‘lgandan keyin event ishlatish:</p><pre><code class="language-java">@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(OrderCreatedEvent event) {
    notificationService.send(event.orderId());
}</code></pre><hr><h2>37. <code>@TransactionalEventListener</code></h2><p>Bu juda foydali.</p><pre><code class="language-java">@Transactional
public void createOrder() {
    Order order = orderRepository.save(new Order());

    applicationEventPublisher.publishEvent(
            new OrderCreatedEvent(order.getId())
    );
}</code></pre><p>Listener:</p><pre><code class="language-java">@Component
public class OrderEventListener {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void afterCommit(OrderCreatedEvent event) {
        notificationService.sendOrderCreated(event.orderId());
    }
}</code></pre><p>Bu listener faqat transaction commit bo‘lgandan keyin ishlaydi.</p><p>Fazalar:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Phase</p></th><th rowspan="1" colspan="1"><p>Qachon ishlaydi?</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>BEFORE_COMMIT</code></p></td><td rowspan="1" colspan="1"><p>Commitdan oldin</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>AFTER_COMMIT</code></p></td><td rowspan="1" colspan="1"><p>Commitdan keyin</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>AFTER_ROLLBACK</code></p></td><td rowspan="1" colspan="1"><p>Rollbackdan keyin</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>AFTER_COMPLETION</code></p></td><td rowspan="1" colspan="1"><p>Commit yoki rollbackdan keyin</p></td></tr></tbody></table><hr><h2>38. Transaction timeout</h2><pre><code class="language-java">@Transactional(timeout = 5)
public void process() {
    // 5 sekunddan oshmasligi kerak
}</code></pre><p>Agar transaction juda cho‘zilib ketsa, timeout bo‘lishi mumkin.</p><p>Bu ayniqsa:</p><ul><li><p>batch processing;</p></li><li><p>external dependency;</p></li><li><p>lock kutish;</p></li><li><p>katta querylar;</p></li></ul><p>uchun muhim.</p><hr><h2>39. Transaction ichida nima qilmaslik kerak?</h2><p>Transaction ichida quyidagilarni minimal qiling:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Qilmaslik kerak</p></th><th rowspan="1" colspan="1"><p>Sabab</p></th></tr><tr><td rowspan="1" colspan="1"><p>External API call</p></td><td rowspan="1" colspan="1"><p>Sekin, rollback qilib bo‘lmaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>File upload/download</p></td><td rowspan="1" colspan="1"><p>DB connection band bo‘ladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Email/SMS yuborish</p></td><td rowspan="1" colspan="1"><p>Commit bo‘lmasdan xabar ketib qolishi mumkin</p></td></tr><tr><td rowspan="1" colspan="1"><p>Katta loop ichida ko‘p save</p></td><td rowspan="1" colspan="1"><p>Batch kerak</p></td></tr><tr><td rowspan="1" colspan="1"><p>User input kutish</p></td><td rowspan="1" colspan="1"><p>Transaction juda cho‘ziladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Keraksiz querylar</p></td><td rowspan="1" colspan="1"><p>Lock va connection bosimi oshadi</p></td></tr></tbody></table><hr><h2>40. Best practice</h2><h3>1. Transactionni service layerda saqlang</h3><pre><code class="language-text">Controller -&gt; Service @Transactional -&gt; Repository</code></pre><h3>2. Transactionni qisqa tuting</h3><p>Faqat DB consistency uchun kerakli kod transaction ichida bo‘lsin.</p><h3>3. External API’ni transaction tashqarisiga chiqaring</h3><p>Ayniqsa payment, SMS, email, file storage.</p><h3>4. Exceptionni yutmang</h3><p>Rollback kerak bo‘lsa exception tashqariga chiqsin.</p><h3>5. <code>readOnly = true</code> ishlating</h3><p>Read-only query methodlarda niyatni aniq ko‘rsating.</p><h3>6. Self-invocationdan ehtiyot bo‘ling</h3><p><code>this.method()</code> transactionni chetlab o‘tishi mumkin.</p><h3>7. Propagationni tushunmasdan o‘zgartirmang</h3><p><code>REQUIRES_NEW</code> kuchli, lekin noto‘g‘ri ishlatilsa muammo ko‘paytiradi.</p><h3>8. Isolationni defaultdan oshirishdan oldin o‘ylang</h3><p>Ko‘pincha muammo isolation bilan emas, locking/modeling bilan hal qilinadi.</p><hr><h2>41. Interview savollar</h2><h3>Savol 1: <code>@Transactional</code> qanday ishlaydi?</h3><p>Javob:</p><blockquote><p>Spring <code>@Transactional</code> method atrofida proxy yaratadi. Method chaqirilganda proxy transaction ochadi, method muvaffaqiyatli tugasa commit qiladi, runtime exception bo‘lsa rollback qiladi.</p></blockquote><hr><h3>Savol 2: Checked exception rollback qiladimi?</h3><p>Javob:</p><blockquote><p>Default holatda yo‘q. Spring runtime exception va errorlarda rollback qiladi. Checked exception uchun <code>rollbackFor</code> berish kerak.</p></blockquote><pre><code class="language-java">@Transactional(rollbackFor = IOException.class)</code></pre><hr><h3>Savol 3: <code>REQUIRED</code> va <code>REQUIRES_NEW</code> farqi nima?</h3><p>Javob:</p><blockquote><p><code>REQUIRED</code> mavjud transactionga qo‘shiladi, yo‘q bo‘lsa yangi ochadi. <code>REQUIRES_NEW</code> esa har doim yangi transaction ochadi va mavjud transactionni vaqtincha suspend qiladi.</p></blockquote><hr><h3>Savol 4: Isolation level nima?</h3><p>Javob:</p><blockquote><p>Isolation parallel transactionlar bir-birining o‘zgarishlarini qanday ko‘rishini belgilaydi. Masalan dirty read, non-repeatable read, phantom read kabi muammolarni boshqaradi.</p></blockquote><hr><h3>Savol 5: Nega transaction ichida external API chaqirish yomon?</h3><p>Javob:</p><blockquote><p>Chunki transaction uzoq ochiq qoladi, DB connection va locklar band bo‘ladi. External API sekin yoki xato bo‘lishi mumkin. Bundan tashqari DB rollback bo‘lsa ham external API chaqiruvini rollback qilib bo‘lmaydi.</p></blockquote><hr><h3>Savol 6: Self-invocation transactionga qanday ta’sir qiladi?</h3><p>Javob:</p><blockquote><p>Agar <code>@Transactional</code> method shu class ichidan <code>this.method()</code> orqali chaqirilsa, chaqiruv Spring proxy orqali o‘tmaydi. Shuning uchun transaction ishlamasligi mumkin.</p></blockquote><hr><h2>42. Qisqa xulosa</h2><p>Transaction management - faqat <code>@Transactional</code> qo‘yish emas.</p><p>Developer quyidagilarni tushunishi kerak:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Mavzu</p></th><th rowspan="1" colspan="1"><p>Muhim xulosa</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>@Transactional</code></p></td><td rowspan="1" colspan="1"><p>Proxy orqali ishlaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Rollback</p></td><td rowspan="1" colspan="1"><p>Runtime exception rollback qiladi, checked exception default rollback qilmaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Propagation</p></td><td rowspan="1" colspan="1"><p>Ichma-ich transactionlar qanday ishlashini belgilaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Isolation</p></td><td rowspan="1" colspan="1"><p>Parallel transaction muammolarini boshqaradi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Locking</p></td><td rowspan="1" colspan="1"><p>Lost update kabi muammolar uchun kerak</p></td></tr><tr><td rowspan="1" colspan="1"><p>TransactionTemplate</p></td><td rowspan="1" colspan="1"><p>Transaction chegarasini kod bilan boshqaradi</p></td></tr><tr><td rowspan="1" colspan="1"><p>External API</p></td><td rowspan="1" colspan="1"><p>Transaction ichida chaqirish xavfli</p></td></tr><tr><td rowspan="1" colspan="1"><p>Self-invocation</p></td><td rowspan="1" colspan="1"><p>Proxy chetlab o‘tilsa transaction ishlamaydi</p></td></tr></tbody></table><p>Eng muhim gap:</p><blockquote><p>Transaction qisqa, aniq va service layer darajasida bo‘lishi kerak. Transaction ichida faqat database consistency uchun zarur ishlar bajarilishi kerak.</p></blockquote>]]></description>
            <author/>
        </item>
        <item>
            <title><![CDATA[Spring Data Advanced]]></title>
            <link>https://blog.jtscorp.uz/spring-data-advanced</link>
            <guid isPermaLink="true">https://blog.jtscorp.uz/spring-data-advanced</guid>
            <pubDate>Wed, 01 Jul 2026 19:00:00 +0000</pubDate>
            <description><![CDATA[<p>Bu bo‘limda oddiy <code>JpaRepository</code>dan yuqoriroq darajaga chiqamiz.  <strong>Spring Data advanced</strong> mavzusi quyidagilarni qamrab oladi: <strong>Specifications &amp; Criteria API</strong>, <strong>Projections</strong>, <strong>Auditing</strong>, <strong>Optimistic locking</strong>, <strong>Spring Data Envers</strong>, va <strong>QueryDSL integration</strong>.</p><hr><h2>1. Spring Data advanced nima?</h2><p>Boshlang&#039;ich darajada biz odatda shuni bilamiz:</p><pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
    List&lt;User&gt; findByName(String name);
}</code></pre><p>Bu yaxshi. Lekin real project kattalashganda oddiy <code>findBy...</code> yetmay qoladi.</p><p>Masalan:</p><pre><code class="language-text">Userlarni filter qilish kerak:
- name bo‘yicha
- phone bo‘yicha
- status bo‘yicha
- createdDate oralig‘i bo‘yicha
- role bo‘yicha
- city bo‘yicha
- faqat active userlar
- pagination bilan</code></pre><p>Buni faqat <code>findByNameAndStatusAndCreatedAtBetween...</code> qilib yozish yomonlashadi.</p><p>Shu joyda <strong>advanced Spring Data</strong> kerak bo‘ladi.</p><hr><h2>2. Oddiy derived query muammosi</h2><pre><code class="language-java">List&lt;User&gt; findByNameAndStatusAndCityAndCreatedAtBetween(
        String name,
        UserStatus status,
        String city,
        LocalDateTime from,
        LocalDateTime to
);</code></pre><p>Boshida ishlaydi. Keyin requirement o‘zgaradi:</p><pre><code class="language-text">name optional
status optional
city optional
date optional
role optional</code></pre><p>Endi muammo:</p><pre><code class="language-java">findByName(...)
findByStatus(...)
findByCity(...)
findByNameAndStatus(...)
findByNameAndCity(...)
findByStatusAndCity(...)
findByNameAndStatusAndCity(...)</code></pre><p>Bu maintain qilib bo‘lmaydigan kodga aylanadi.</p><p>Yechimlar:</p><ul><li><p><code>Specification</code></p></li><li><p>Criteria API</p></li><li><p>QueryDSL</p></li><li><p>custom repository</p></li><li><p>JPQL dynamic query</p></li></ul><hr><h2>3. Specification nima?</h2><p><strong>Specification</strong> - dynamic query yozish usuli.</p><p>Oddiy ma’noda:</p><blockquote><p>Specification - query shartlarini alohida-alohida qilib yig‘ish imkonini beradi.</p></blockquote><p>Masalan:</p><pre><code class="language-text">status = ACTIVE
AND city = Tashkent
AND age &gt; 18</code></pre><p>Bularni alohida methodlarga ajratib, keraklisini qo‘shib ketish mumkin.</p><hr><h2>4. Specification ishlatish uchun repository</h2><p>Entity:</p><pre><code class="language-java">@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String fullName;

    private String phone;

    @Enumerated(EnumType.STRING)
    private UserStatus status;

    private String city;

    private LocalDateTime createdAt;
}</code></pre><p>Enum:</p><pre><code class="language-java">public enum UserStatus {
    ACTIVE,
    BLOCKED,
    DELETED
}</code></pre><p>Repository:</p><pre><code class="language-java">public interface UserRepository extends
        JpaRepository&lt;User, Long&gt;,
        JpaSpecificationExecutor&lt;User&gt; {
}</code></pre><p>Muhim joy:</p><pre><code class="language-java">JpaSpecificationExecutor&lt;User&gt;</code></pre><p>Shu interface <code>Specification</code> bilan query qilish imkonini beradi.</p><hr><h2>5. Oddiy Specification yozish</h2><pre><code class="language-java">public class UserSpecifications {

    public static Specification&lt;User&gt; hasStatus(UserStatus status) {
        return (root, query, criteriaBuilder) -&gt;
                criteriaBuilder.equal(root.get(&quot;status&quot;), status);
    }

    public static Specification&lt;User&gt; hasCity(String city) {
        return (root, query, criteriaBuilder) -&gt;
                criteriaBuilder.equal(root.get(&quot;city&quot;), city);
    }

    public static Specification&lt;User&gt; nameContains(String name) {
        return (root, query, criteriaBuilder) -&gt;
                criteriaBuilder.like(
                        criteriaBuilder.lower(root.get(&quot;fullName&quot;)),
                        &quot;%&quot; + name.toLowerCase() + &quot;%&quot;
                );
    }
}</code></pre><p>Bu yerda:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Qism</p></th><th rowspan="1" colspan="1"><p>Ma’nosi</p></th></tr><tr><td rowspan="1" colspan="1"><p><code>root</code></p></td><td rowspan="1" colspan="1"><p>Entity jadvali, masalan <code>User</code></p></td></tr><tr><td rowspan="1" colspan="1"><p><code>query</code></p></td><td rowspan="1" colspan="1"><p>Query obyektining o‘zi</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>criteriaBuilder</code></p></td><td rowspan="1" colspan="1"><p>SQL shartlarini yasash uchun builder</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>root.get(&quot;status&quot;)</code></p></td><td rowspan="1" colspan="1"><p><code>status</code> column</p></td></tr><tr><td rowspan="1" colspan="1"><p><code>criteriaBuilder.equal(...)</code></p></td><td rowspan="1" colspan="1"><p><code>where status = ?</code></p></td></tr></tbody></table><hr><h2>6. Specification ishlatish</h2><pre><code class="language-java">Specification&lt;User&gt; spec = Specification
        .where(UserSpecifications.hasStatus(UserStatus.ACTIVE))
        .and(UserSpecifications.hasCity(&quot;Tashkent&quot;));</code></pre><p>Repository:</p><pre><code class="language-java">List&lt;User&gt; users = userRepository.findAll(spec);</code></pre><p>Taxminiy SQL:</p><pre><code class="language-sql">select *
from users
where status = &#039;ACTIVE&#039;
  and city = &#039;Tashkent&#039;;</code></pre><hr><h2>7. Optional filterlar bilan real search</h2><p>Request DTO:</p><pre><code class="language-java">public record UserSearchRequest(
        String name,
        UserStatus status,
        String city,
        LocalDateTime from,
        LocalDateTime to
) {
}</code></pre><p>Specification builder:</p><pre><code class="language-java">public class UserSpecifications {

    public static Specification&lt;User&gt; filter(UserSearchRequest request) {
        Specification&lt;User&gt; spec = Specification.where(null);

        if (request.name() != null &amp;&amp; !request.name().isBlank()) {
            spec = spec.and(nameContains(request.name()));
        }

        if (request.status() != null) {
            spec = spec.and(hasStatus(request.status()));
        }

        if (request.city() != null &amp;&amp; !request.city().isBlank()) {
            spec = spec.and(hasCity(request.city()));
        }

        if (request.from() != null) {
            spec = spec.and(createdAfter(request.from()));
        }

        if (request.to() != null) {
            spec = spec.and(createdBefore(request.to()));
        }

        return spec;
    }

    private static Specification&lt;User&gt; nameContains(String name) {
        return (root, query, cb) -&gt;
                cb.like(
                        cb.lower(root.get(&quot;fullName&quot;)),
                        &quot;%&quot; + name.toLowerCase() + &quot;%&quot;
                );
    }

    private static Specification&lt;User&gt; hasStatus(UserStatus status) {
        return (root, query, cb) -&gt;
                cb.equal(root.get(&quot;status&quot;), status);
    }

    private static Specification&lt;User&gt; hasCity(String city) {
        return (root, query, cb) -&gt;
                cb.equal(root.get(&quot;city&quot;), city);
    }

    private static Specification&lt;User&gt; createdAfter(LocalDateTime from) {
        return (root, query, cb) -&gt;
                cb.greaterThanOrEqualTo(root.get(&quot;createdAt&quot;), from);
    }

    private static Specification&lt;User&gt; createdBefore(LocalDateTime to) {
        return (root, query, cb) -&gt;
                cb.lessThanOrEqualTo(root.get(&quot;createdAt&quot;), to);
    }
}</code></pre><p>Service:</p><pre><code class="language-java">@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Transactional(readOnly = true)
    public Page&lt;User&gt; search(UserSearchRequest request, Pageable pageable) {
        Specification&lt;User&gt; spec = UserSpecifications.filter(request);
        return userRepository.findAll(spec, pageable);
    }
}</code></pre><p>Controller:</p><pre><code class="language-java">@GetMapping(&quot;/users&quot;)
public Page&lt;User&gt; search(
        UserSearchRequest request,
        Pageable pageable
) {
    return userService.search(request, pageable);
}</code></pre><hr><h2>8. Specification + pagination + sorting</h2><p>Spring Data buni tayyor beradi:</p><pre><code class="language-java">Pageable pageable = PageRequest.of(
        0,
        20,
        Sort.by(Sort.Direction.DESC, &quot;createdAt&quot;)
);

Page&lt;User&gt; page = userRepository.findAll(spec, pageable);</code></pre><p>REST API’da:</p><pre><code class="language-text">GET /users?status=ACTIVE&amp;city=Tashkent&amp;page=0&amp;size=20&amp;sort=createdAt,desc</code></pre><p>Bu real projectlarda juda ko‘p ishlatiladi.</p><hr><h2>9. Specification qachon yaxshi?</h2><p>Specification yaxshi:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Holat</p></th><th rowspan="1" colspan="1"><p>Sabab</p></th></tr><tr><td rowspan="1" colspan="1"><p>Ko‘p optional filterlar bor</p></td><td rowspan="1" colspan="1"><p>Queryni dynamic yig‘ish oson</p></td></tr><tr><td rowspan="1" colspan="1"><p>Admin panel search</p></td><td rowspan="1" colspan="1"><p>Filterlar soni ko‘p bo‘ladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>CRM/ERP sistemalar</p></td><td rowspan="1" colspan="1"><p>Search murakkab bo‘ladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Pagination kerak</p></td><td rowspan="1" colspan="1"><p><code>findAll(spec, pageable)</code> tayyor</p></td></tr><tr><td rowspan="1" colspan="1"><p>Reusable filterlar kerak</p></td><td rowspan="1" colspan="1"><p>Har bir shart method bo‘lib turadi</p></td></tr></tbody></table><p>Lekin juda murakkab report querylar uchun Specification noqulay bo‘lishi mumkin. Bunda native SQL, QueryDSL yoki custom repository yaxshiroq.</p><hr><h2>10. Criteria API nima?</h2><p>Specification ichida ishlatilayotgan narsa aslida <strong>JPA Criteria API</strong>.</p><p>Criteria API - SQL/JPQLni string bilan emas, Java obyektlari bilan qurish usuli.</p><p>Masalan JPQL:</p><pre><code class="language-java">@Query(&quot;select u from User u where u.status = :status&quot;)
List&lt;User&gt; findActive(UserStatus status);</code></pre><p>Criteria API bilan:</p><pre><code class="language-java">CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery&lt;User&gt; query = cb.createQuery(User.class);
Root&lt;User&gt; root = query.from(User.class);

query.select(root)
        .where(cb.equal(root.get(&quot;status&quot;), UserStatus.ACTIVE));

List&lt;User&gt; users = entityManager
        .createQuery(query)
        .getResultList();</code></pre><p>Criteria API juda verbose. Shu sabab ko‘pincha uni to‘g‘ridan-to‘g‘ri emas, <code>Specification</code> orqali ishlatamiz.</p><hr><h2>11. Projection nima?</h2><p><strong>Projection</strong> - entityning hammasini emas, faqat kerakli fieldlarni olish.</p><p>Masalan <code>User</code> entity katta bo‘lishi mumkin:</p><pre><code class="language-java">@Entity
public class User {
    private Long id;
    private String fullName;
    private String phone;
    private String passwordHash;
    private String passportNumber;
    private LocalDateTime createdAt;
}</code></pre><p>API’da esa faqat shu kerak:</p><pre><code class="language-json">{
  &quot;id&quot;: 1,
  &quot;fullName&quot;: &quot;Ali Valiyev&quot;
}</code></pre><p>Entityni to‘liq olib, keyin DTOga map qilish har doim ham optimal emas. Projection yordamida DBdan kerakli columnlargina olinadi.</p><hr><h2>12. Interface-based projection</h2><p>Projection interface:</p><pre><code class="language-java">public interface UserShortView {

    Long getId();

    String getFullName();
}</code></pre><p>Repository:</p><pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {

    List&lt;UserShortView&gt; findByStatus(UserStatus status);
}</code></pre><p>Ishlatish:</p><pre><code class="language-java">List&lt;UserShortView&gt; users =
        userRepository.findByStatus(UserStatus.ACTIVE);</code></pre><p>Natija faqat kerakli fieldlarni beradi.</p><hr><h2>13. DTO projection</h2><p>DTO:</p><pre><code class="language-java">public record UserShortDto(
        Long id,
        String fullName
) {
}</code></pre><p>Repository:</p><pre><code class="language-java">@Query(&quot;&quot;&quot;
    select new com.example.dto.UserShortDto(
        u.id,
        u.fullName
    )
    from User u
    where u.status = :status
&quot;&quot;&quot;)
List&lt;UserShortDto&gt; findShortUsersByStatus(UserStatus status);</code></pre><p>Bu aniqroq va API response uchun qulay.</p><hr><h2>14. Projection qachon kerak?</h2><p>Projection ishlating:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Holat</p></th><th rowspan="1" colspan="1"><p>Sabab</p></th></tr><tr><td rowspan="1" colspan="1"><p>List endpoint</p></td><td rowspan="1" colspan="1"><p>Hammasi kerak emas</p></td></tr><tr><td rowspan="1" colspan="1"><p>Mobile API</p></td><td rowspan="1" colspan="1"><p>Response yengil bo‘ladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Admin table</p></td><td rowspan="1" colspan="1"><p>Faqat table columnlar kerak</p></td></tr><tr><td rowspan="1" colspan="1"><p>Sensitive fieldlar bor</p></td><td rowspan="1" colspan="1"><p>Password/passport chiqib ketmaydi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Performance kerak</p></td><td rowspan="1" colspan="1"><p>Kamroq data olinadi</p></td></tr></tbody></table><p>Yomon amaliyot:</p><pre><code class="language-java">@GetMapping(&quot;/users&quot;)
public List&lt;User&gt; getUsers() {
    return userRepository.findAll();
}</code></pre><p>Nega yomon?</p><ul><li><p>password hash chiqib ketishi mumkin;</p></li><li><p>entity relationlar JSONda loop bo‘lishi mumkin;</p></li><li><p>lazy loading muammosi chiqadi;</p></li><li><p>response og‘irlashadi;</p></li><li><p>API entityga bog‘lanib qoladi.</p></li></ul><p>Yaxshi:</p><pre><code class="language-java">@GetMapping(&quot;/users&quot;)
public Page&lt;UserShortDto&gt; getUsers(Pageable pageable) {
    return userService.getUsers(pageable);
}</code></pre><hr><h2>15. Dynamic projection</h2><p>Spring Data’da method parametr orqali projection turini tanlash ham mumkin:</p><pre><code class="language-java">public interface UserRepository extends JpaRepository&lt;User, Long&gt; {

    &lt;T&gt; List&lt;T&gt; findByStatus(UserStatus status, Class&lt;T&gt; type);
}</code></pre><p>Projectionlar:</p><pre><code class="language-java">public interface UserShortView {
    Long getId();
    String getFullName();
}</code></pre><pre><code class="language-java">public interface UserPhoneView {
    Long getId();
    String getPhone();
}</code></pre><p>Ishlatish:</p><pre><code class="language-java">List&lt;UserShortView&gt; shortUsers =
        userRepository.findByStatus(
                UserStatus.ACTIVE,
                UserShortView.class
        );

List&lt;UserPhoneView&gt; phoneUsers =
        userRepository.findByStatus(
                UserStatus.ACTIVE,
                UserPhoneView.class
        );</code></pre><p>Bu kuchli, lekin haddan tashqari ishlatilsa kod tushunarsizlashadi.</p><hr><h2>16. Auditing nima?</h2><p><strong>Auditing</strong> - entity qachon yaratilgan, qachon o‘zgargan, kim yaratgan, kim o‘zgartirganini avtomatik saqlash.</p><p>Ko‘p entitylarda bular bo‘ladi:</p><pre><code class="language-text">createdAt
updatedAt
createdBy
updatedBy</code></pre><p>Har safar qo‘lda yozish yomon:</p><pre><code class="language-java">user.setCreatedAt(LocalDateTime.now());
user.setUpdatedAt(LocalDateTime.now());</code></pre><p>Spring Data Auditing buni avtomatik qiladi.</p><hr><h2>17. Auditing yoqish</h2><p>Main class yoki config:</p><pre><code class="language-java">@EnableJpaAuditing
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}</code></pre><p>Base entity:</p><pre><code class="language-java">@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}</code></pre><p>Entity:</p><pre><code class="language-java">@Entity
public class Product extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
}</code></pre><p>Endi <code>Product</code> save bo‘lganda <code>createdAt</code>, <code>updatedAt</code> avtomatik to‘ladi.</p><hr><h2>18. <code>createdBy</code> va <code>updatedBy</code></h2><p>Base entity:</p><pre><code class="language-java">@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    @CreatedBy
    @Column(updatable = false)
    private Long createdBy;

    @LastModifiedBy
    private Long updatedBy;
}</code></pre><p>AuditorAware:</p><pre><code class="language-java">@Component
public class SpringSecurityAuditorAware
        implements AuditorAware&lt;Long&gt; {

    @Override
    public Optional&lt;Long&gt; getCurrentAuditor() {
        Authentication authentication =
                SecurityContextHolder.getContext().getAuthentication();

        if (authentication == null ||
                !authentication.isAuthenticated()) {
            return Optional.empty();
        }

        CustomUserDetails userDetails =
                (CustomUserDetails) authentication.getPrincipal();

        return Optional.of(userDetails.getId());
    }
}</code></pre><p>Bu yerda current user ID security contextdan olinadi.</p><hr><h2>19. Auditing qachon foydali?</h2><p>Auditing kerak:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Holat</p></th><th rowspan="1" colspan="1"><p>Sabab</p></th></tr><tr><td rowspan="1" colspan="1"><p>Admin panel</p></td><td rowspan="1" colspan="1"><p>Kim o‘zgartirganini ko‘rish</p></td></tr><tr><td rowspan="1" colspan="1"><p>CRM/ERP</p></td><td rowspan="1" colspan="1"><p>Data history muhim</p></td></tr><tr><td rowspan="1" colspan="1"><p>Debugging</p></td><td rowspan="1" colspan="1"><p>Qachon o‘zgarganini bilish</p></td></tr><tr><td rowspan="1" colspan="1"><p>Security</p></td><td rowspan="1" colspan="1"><p>Sensitive actionlarni kuzatish</p></td></tr><tr><td rowspan="1" colspan="1"><p>Reporting</p></td><td rowspan="1" colspan="1"><p>Created date bo‘yicha filter</p></td></tr></tbody></table><p>Lekin auditing to‘liq history emas. U faqat oxirgi holatni saqlaydi:</p><pre><code class="language-text">createdAt = birinchi yaratilgan vaqt
updatedAt = oxirgi o‘zgargan vaqt</code></pre><p>Agar har bir o‘zgarish tarixini saqlash kerak bo‘lsa, Envers yoki audit log kerak.</p><hr><h2>20. Optimistic locking nima?</h2><p><strong>Optimistic locking</strong> - parallel update muammolarini hal qilish usuli.</p><p>Masalan ikkita admin bitta productni ochdi:</p><pre><code class="language-text">Admin A: product price = 100 ko‘rdi
Admin B: product price = 100 ko‘rdi

Admin A: price = 120 qilib saqladi
Admin B: price = 90 qilib saqladi</code></pre><p>Natijada Admin A o‘zgarishi yo‘qolib ketishi mumkin. Bu <strong>lost update</strong>.</p><p>Optimistic locking buni oldini oladi.</p><hr><h2>21. <code>@Version</code></h2><p>Entityga <code>version</code> qo‘shamiz:</p><pre><code class="language-java">@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private BigDecimal price;

    @Version
    private Long version;
}</code></pre><p>Endi Hibernate update qilganda taxminan shunday SQL yozadi:</p><pre><code class="language-sql">update product
set name = ?, price = ?, version = version + 1
where id = ? and version = ?;</code></pre><p>Agar boshqa transaction oldin update qilib versionni oshirib yuborgan bo‘lsa, update 0 rowga ta’sir qiladi va exception chiqadi.</p><hr><h2>22. Optimistic locking oqimi</h2><pre><code class="language-text">Product version = 1

Admin A productni oldi: version = 1
Admin B productni oldi: version = 1

Admin A save qildi:
where id = 1 and version = 1
success, version = 2

Admin B save qildi:
where id = 1 and version = 1
0 rows updated -&gt; OptimisticLockException</code></pre><p>Bu to‘g‘ri xatti-harakat. Sistemada &quot;oxirgi yozgan yutadi&quot; emas, &quot;conflict bor&quot; deyiladi.</p><hr><h2>23. Optimistic lock exceptionni handle qilish</h2><pre><code class="language-java">@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ObjectOptimisticLockingFailureException.class)
    public ResponseEntity&lt;?&gt; handleOptimisticLock(
            ObjectOptimisticLockingFailureException ex
    ) {
        return ResponseEntity.status(HttpStatus.CONFLICT)
                .body(Map.of(
                        &quot;error&quot;, &quot;DATA_CONFLICT&quot;,
                        &quot;message&quot;, &quot;Data was changed by another user. Please reload and try again.&quot;
                ));
    }
}</code></pre><p>HTTP status:</p><pre><code class="language-text">409 Conflict</code></pre><p>Bu API uchun to‘g‘riroq.</p><hr><h2>24. Optimistic vs pessimistic locking</h2><table><tbody><tr><th rowspan="1" colspan="1"><p>Lock turi</p></th><th rowspan="1" colspan="1"><p>Qanday ishlaydi?</p></th><th rowspan="1" colspan="1"><p>Qachon yaxshi?</p></th></tr><tr><td rowspan="1" colspan="1"><p>Optimistic</p></td><td rowspan="1" colspan="1"><p>Version tekshiradi</p></td><td rowspan="1" colspan="1"><p>Conflict kam bo‘lsa</p></td></tr><tr><td rowspan="1" colspan="1"><p>Pessimistic</p></td><td rowspan="1" colspan="1"><p>Row lock qiladi</p></td><td rowspan="1" colspan="1"><p>Conflict ko‘p bo‘lsa</p></td></tr><tr><td rowspan="1" colspan="1"><p>Atomic update</p></td><td rowspan="1" colspan="1"><p>Direct update query</p></td><td rowspan="1" colspan="1"><p>Stock/balance kabi joylarda</p></td></tr></tbody></table><p>Optimistic locking odatda yaxshi:</p><ul><li><p>admin panel;</p></li><li><p>product edit;</p></li><li><p>user profile edit;</p></li><li><p>document edit;</p></li><li><p>kam conflictli CRUD.</p></li></ul><p>Stock kamaytirish, balance update qilish kabi joylarda esa atomic update yoki pessimistic lock yaxshiroq bo‘lishi mumkin.</p><hr><h2>25. Spring Data Envers nima?</h2><p><strong>Hibernate Envers</strong> - entity o‘zgarish tarixini saqlash mexanizmi.</p><p>Auditing faqat buni beradi:</p><pre><code class="language-text">Product:
createdAt
updatedAt</code></pre><p>Envers esa har bir o‘zgarish tarixini saqlaydi:</p><pre><code class="language-text">Product version 1: price = 100
Product version 2: price = 120
Product version 3: price = 90</code></pre><p>Bu quyidagilar uchun kerak:</p><ul><li><p>audit history;</p></li><li><p>kim qachon nima o‘zgartirdi;</p></li><li><p>oldingi holatni ko‘rish;</p></li><li><p>regulatory requirement;</p></li><li><p>admin action tracking.</p></li></ul><hr><h2>26. Envers dependency</h2><p>Maven:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.data&lt;/groupId&gt;
    &lt;artifactId&gt;spring-data-envers&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>Ko‘pincha Hibernate Envers ham kerak bo‘ladi:</p><pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.hibernate.orm&lt;/groupId&gt;
    &lt;artifactId&gt;hibernate-envers&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><hr><h2>27. Entityda <code>@Audited</code></h2><pre><code class="language-java">@Entity
@Audited
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private BigDecimal price;
}</code></pre><p>Endi Envers audit table yaratadi:</p><pre><code class="language-text">product
product_aud
revinfo</code></pre><p><code>product_aud</code> ichida productning eski holatlari saqlanadi.</p><hr><h2>28. Envers repository</h2><pre><code class="language-java">public interface ProductRepository extends
        JpaRepository&lt;Product, Long&gt;,
        RevisionRepository&lt;Product, Long, Integer&gt; {
}</code></pre><p>Ishlatish:</p><pre><code class="language-java">Revisions&lt;Integer, Product&gt; revisions =
        productRepository.findRevisions(productId);</code></pre><p>Oxirgi revision:</p><pre><code class="language-java">Revision&lt;Integer, Product&gt; lastRevision =
        productRepository.findLastChangeRevision(productId)
                .orElseThrow();</code></pre><hr><h2>29. Envers qachon kerak emas?</h2><p>Enversni hamma entityga qo‘shish kerak emas.</p><p>Kerak bo‘lmaydi:</p><ul><li><p>lookup table;</p></li><li><p>juda katta log entitylar;</p></li><li><p>tez-tez o‘zgaradigan high-volume data;</p></li><li><p>vaqtinchalik data;</p></li><li><p>cache-like table.</p></li></ul><p>Sabab:</p><ul><li><p>audit table kattalashadi;</p></li><li><p>write overhead oshadi;</p></li><li><p>storage ko‘payadi;</p></li><li><p>querylar murakkablashadi.</p></li></ul><p>Faqat muhim entitylarga qo‘shing:</p><pre><code class="language-text">User
Role
Product
Contract
Order status
Payment setting
Tariff</code></pre><hr><h2>30. QueryDSL nima?</h2><p><strong>QueryDSL</strong> - type-safe query yozish uchun kutubxona.</p><p>Specificationda biz string ishlatdik:</p><pre><code class="language-java">root.get(&quot;fullName&quot;)</code></pre><p>Agar field nomi o‘zgarsa, compile-time error bermaydi. Runtime’da xato chiqadi.</p><p>QueryDSLda esa generated classlar ishlatiladi:</p><pre><code class="language-java">QUser user = QUser.user;

queryFactory
        .selectFrom(user)
        .where(user.fullName.containsIgnoreCase(&quot;ali&quot;))
        .fetch();</code></pre><p>Agar <code>fullName</code> field o‘chirilsa yoki nomi o‘zgarsa, compile error chiqadi. Bu katta projectlarda kuchli afzallik.</p><hr><h2>31. QueryDSL qachon yaxshi?</h2><p>QueryDSL yaxshi:</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Holat</p></th><th rowspan="1" colspan="1"><p>Sabab</p></th></tr><tr><td rowspan="1" colspan="1"><p>Murakkab dynamic query</p></td><td rowspan="1" colspan="1"><p>Readable bo‘ladi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Compile-time safety kerak</p></td><td rowspan="1" colspan="1"><p>Field nomi xatosi kamayadi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Joinlar ko‘p</p></td><td rowspan="1" colspan="1"><p>JPQL stringdan tozaroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Report querylar</p></td><td rowspan="1" colspan="1"><p>Select/projection oson</p></td></tr><tr><td rowspan="1" colspan="1"><p>Katta project</p></td><td rowspan="1" colspan="1"><p>Refactoring xavfsizroq</p></td></tr></tbody></table><p>Kamchiligi:</p><ul><li><p>setup murakkabroq;</p></li><li><p>Q-class generation kerak;</p></li><li><p>jamoa o‘rganishi kerak;</p></li><li><p>oddiy CRUD uchun ortiqcha.</p></li></ul><hr><h2>32. QueryDSL oddiy misol</h2><p>QueryDSL generated class:</p><pre><code class="language-java">QUser user = QUser.user;</code></pre><p>Custom repository:</p><pre><code class="language-java">@Repository
public class UserQueryRepository {

    private final JPAQueryFactory queryFactory;

    public UserQueryRepository(EntityManager entityManager) {
        this.queryFactory = new JPAQueryFactory(entityManager);
    }

    public List&lt;User&gt; search(String name, UserStatus status) {
        QUser user = QUser.user;

        BooleanBuilder builder = new BooleanBuilder();

        if (name != null &amp;&amp; !name.isBlank()) {
            builder.and(user.fullName.containsIgnoreCase(name));
        }

        if (status != null) {
            builder.and(user.status.eq(status));
        }

        return queryFactory
                .selectFrom(user)
                .where(builder)
                .orderBy(user.createdAt.desc())
                .fetch();
    }
}</code></pre><p>Bu Specificationga qaraganda o‘qilishi osonroq bo‘lishi mumkin.</p><hr><h2>33. QueryDSL DTO projection</h2><p>DTO:</p><pre><code class="language-java">public record UserListDto(
        Long id,
        String fullName,
        String phone
) {
}</code></pre><p>Query:</p><pre><code class="language-java">public List&lt;UserListDto&gt; findUsers() {
    QUser user = QUser.user;

    return queryFactory
            .select(Projections.constructor(
                    UserListDto.class,
                    user.id,
                    user.fullName,
                    user.phone
            ))
            .from(user)
            .where(user.status.eq(UserStatus.ACTIVE))
            .fetch();
}</code></pre><p>Bu report/list endpointlar uchun qulay.</p><hr><h2>34. Specification vs QueryDSL</h2><table><tbody><tr><th rowspan="1" colspan="1"><p>Mezон</p></th><th rowspan="1" colspan="1"><p>Specification</p></th><th rowspan="1" colspan="1"><p>QueryDSL</p></th></tr><tr><td rowspan="1" colspan="1"><p>Setup</p></td><td rowspan="1" colspan="1"><p>Oson</p></td><td rowspan="1" colspan="1"><p>Murakkabroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Type safety</p></td><td rowspan="1" colspan="1"><p>Pastroq, string fieldlar bor</p></td><td rowspan="1" colspan="1"><p>Yuqori</p></td></tr><tr><td rowspan="1" colspan="1"><p>Dynamic filter</p></td><td rowspan="1" colspan="1"><p>Yaxshi</p></td><td rowspan="1" colspan="1"><p>Juda yaxshi</p></td></tr><tr><td rowspan="1" colspan="1"><p>Readability</p></td><td rowspan="1" colspan="1"><p>O‘rtacha</p></td><td rowspan="1" colspan="1"><p>Ko‘pincha yaxshiroq</p></td></tr><tr><td rowspan="1" colspan="1"><p>Kichik project</p></td><td rowspan="1" colspan="1"><p>Yetarli</p></td><td rowspan="1" colspan="1"><p>Ortiqcha bo‘lishi mumkin</p></td></tr><tr><td rowspan="1" colspan="1"><p>Katta project</p></td><td rowspan="1" colspan="1"><p>Ba’zan noqulay</p></td><td rowspan="1" colspan="1"><p>Kuchliroq</p></td></tr></tbody></table><p>Amaliy tavsiya:</p><pre><code class="language-text">Oddiy admin filterlar -&gt; Specification
Murakkab query/report/search -&gt; QueryDSL
Juda performance-sensitive query -&gt; native SQL yoki jOOQ</code></pre><hr><h2>35. N+1 muammo</h2><p>Spring Data advanced mavzusida buni ham tushunish shart.</p><p>Entity:</p><pre><code class="language-java">@Entity
public class Order {

    @ManyToOne(fetch = FetchType.LAZY)
    private User user;
}</code></pre><p>Service:</p><pre><code class="language-java">List&lt;Order&gt; orders = orderRepository.findAll();

for (Order order : orders) {
    System.out.println(order.getUser().getFullName());
}</code></pre><p>Muammo:</p><pre><code class="language-text">1 query: ordersni olish
N query: har bir order uchun userni olish</code></pre><p>Agar 100 ta order bo‘lsa:</p><pre><code class="language-text">1 + 100 query</code></pre><p>Bu <strong>N+1 problem</strong>.</p><hr><h2>36. Fetch join</h2><p>Repository:</p><pre><code class="language-java">@Query(&quot;&quot;&quot;
    select o
    from Order o
    join fetch o.user
    where o.status = :status
&quot;&quot;&quot;)
List&lt;Order&gt; findByStatusWithUser(OrderStatus status);</code></pre><p>Bu order va userni bitta queryda olib keladi.</p><p>Taxminiy SQL:</p><pre><code class="language-sql">select o.*, u.*
from orders o
join users u on u.id = o.user_id
where o.status = ?;</code></pre><hr><h2>37. EntityGraph</h2><p><code>@EntityGraph</code> ham relationlarni oldindan yuklash uchun ishlatiladi.</p><pre><code class="language-java">@EntityGraph(attributePaths = {&quot;user&quot;, &quot;items&quot;})
List&lt;Order&gt; findByStatus(OrderStatus status);</code></pre><p>Bu method chaqirilganda <code>user</code> va <code>items</code> oldindan yuklanadi.</p><p>Lekin ehtiyot bo‘lish kerak. Ko‘p collectionlarni bir vaqtda fetch qilish katta result set va duplicate row muammolarini chiqarishi mumkin.</p><hr><h2>38. Bulk update</h2><p>Oddiy JPA entity update:</p><pre><code class="language-java">@Transactional
public void blockUsers() {
    List&lt;User&gt; users = userRepository.findByStatus(UserStatus.INACTIVE);

    for (User user : users) {
        user.setStatus(UserStatus.BLOCKED);
    }
}</code></pre><p>Agar 100 ming user bo‘lsa, bu og‘ir.</p><p>Bulk update:</p><pre><code class="language-java">@Modifying
@Query(&quot;&quot;&quot;
    update User u
    set u.status = :newStatus
    where u.status = :oldStatus
&quot;&quot;&quot;)
int updateStatus(
        UserStatus oldStatus,
        UserStatus newStatus
);</code></pre><p>Service:</p><pre><code class="language-java">@Transactional
public int blockInactiveUsers() {
    return userRepository.updateStatus(
            UserStatus.INACTIVE,
            UserStatus.BLOCKED
    );
}</code></pre><p>Muhim: bulk update persistence contextni avtomatik yangilamasligi mumkin. Shu sabab bulk operationdan keyin entity manager clear kerak bo‘lishi mumkin.</p><pre><code class="language-java">@Modifying(clearAutomatically = true, flushAutomatically = true)</code></pre><hr><h2>39. <code>@Modifying</code></h2><p><code>@Query</code> default holatda select query deb qaraladi.</p><p>Update/delete uchun:</p><pre><code class="language-java">@Modifying
@Query(&quot;delete from User u where u.status = :status&quot;)
int deleteByStatus(UserStatus status);</code></pre><p>Va service method transaction ichida bo‘lishi kerak:</p><pre><code class="language-java">@Transactional
public int deleteBlockedUsers() {
    return userRepository.deleteByStatus(UserStatus.BLOCKED);
}</code></pre><hr><h2>40. Advanced Spring Data best practice</h2><h3>1. Entityni API response sifatida qaytarmang</h3><p>Yomon:</p><pre><code class="language-java">return userRepository.findAll();</code></pre><p>Yaxshi:</p><pre><code class="language-java">return userRepository.findAllProjectedBy();</code></pre><p>yoki DTO mapping.</p><hr><h3>2. Optional filterlarda Specification ishlating</h3><p>Yomon:</p><pre><code class="language-java">findByNameAndStatusAndCityAndCreatedAtBetween(...)</code></pre><p>Yaxshi:</p><pre><code class="language-java">userRepository.findAll(UserSpecifications.filter(request), pageable);</code></pre><hr><h3>3. List endpointlarda projection ishlating</h3><p>Entity katta bo‘lsa, faqat kerakli fieldlarni oling.</p><hr><h3>4. Audit fieldlarni qo‘lda to‘ldirmang</h3><p><code>@CreatedDate</code>, <code>@LastModifiedDate</code>, <code>@CreatedBy</code>, <code>@LastModifiedBy</code> ishlating.</p><hr><h3>5. Parallel update uchun <code>@Version</code> qo‘shing</h3><p>Muhim entitylarda lost update oldini oladi.</p><hr><h3>6. N+1 muammosini testda tekshiring</h3><p>Ayniqsa:</p><pre><code class="language-java">order.getUser()
order.getItems()
user.getRoles()</code></pre><p>kabi relationlarda.</p><hr><h3>7. Bulk operationlarda persistence contextga ehtiyot bo‘ling</h3><p>Bulk query DBni o‘zgartiradi, lekin memorydagi entitylar eski holatda qolishi mumkin.</p><hr><h2>41. Real project uchun pattern</h2><p>Masalan admin panelda user search endpoint:</p><pre><code class="language-text">GET /admin/users?name=ali&amp;status=ACTIVE&amp;city=Tashkent&amp;page=0&amp;size=20</code></pre><p>Yaxshi arxitektura:</p><pre><code class="language-text">Controller
  ↓
UserSearchRequest
  ↓
Service
  ↓
Specification
  ↓
Repository
  ↓
Page&lt;UserListDto&gt;</code></pre><p>Service:</p><pre><code class="language-java">@Transactional(readOnly = true)
public Page&lt;UserListDto&gt; search(UserSearchRequest request, Pageable pageable) {
    Specification&lt;User&gt; spec = UserSpecifications.filter(request);

    return userRepository.findAll(spec, pageable)
            .map(userMapper::toListDto);
}</code></pre><p>Bu clean va maintainable.</p><p>Agar performance kerak bo‘lsa, projectionni repository darajasida qilish mumkin.</p><hr><h2>42. Interview savollar</h2><h3>Savol 1: Specification nima?</h3><p>Javob:</p><blockquote><p>Specification - Spring Data JPA’da dynamic query shartlarini composable qilib yozish usuli. U Criteria API ustiga qurilgan va optional filterlar ko‘p bo‘lgan search endpointlarda foydali.</p></blockquote><hr><h3>Savol 2: Projection nima uchun kerak?</h3><p>Javob:</p><blockquote><p>Projection entityning hammasini emas, faqat kerakli fieldlarni olish uchun ishlatiladi. Bu response hajmini kamaytiradi, sensitive fieldlar chiqib ketishining oldini oladi va performancega yordam beradi.</p></blockquote><hr><h3>Savol 3: Auditing nima?</h3><p>Javob:</p><blockquote><p>Auditing entity yaratilgan va o‘zgartirilgan vaqtni, shuningdek kim yaratgani yoki o‘zgartirganini avtomatik saqlash mexanizmi. Spring Data’da <code>@CreatedDate</code>, <code>@LastModifiedDate</code>, <code>@CreatedBy</code>, <code>@LastModifiedBy</code> ishlatiladi.</p></blockquote><hr><h3>Savol 4: Optimistic locking qanday ishlaydi?</h3><p>Javob:</p><blockquote><p>Entityga <code>@Version</code> field qo‘shiladi. Update paytida Hibernate eski versionni tekshiradi. Agar boshqa transaction oldin update qilgan bo‘lsa, version mos kelmaydi va optimistic lock exception chiqadi.</p></blockquote><hr><h3>Savol 5: Envers nima?</h3><p>Javob:</p><blockquote><p>Hibernate Envers entity o‘zgarish tarixini saqlaydi. Oddiy auditing oxirgi holatni saqlasa, Envers har bir revisionni alohida audit tablelarda saqlaydi.</p></blockquote><hr><h3>Savol 6: QueryDSL Specificationdan nimasi bilan farq qiladi?</h3><p>Javob:</p><blockquote><p>Specification Criteria API asosida ishlaydi va ba’zan string field nomlariga tayanadi. QueryDSL esa generated Q-classlar orqali type-safe query yozadi, shuning uchun refactoringda xatolar compile time’da chiqadi.</p></blockquote><hr><h3>Savol 7: N+1 muammo nima?</h3><p>Javob:</p><blockquote><p>N+1 - avval asosiy entitylar bitta query bilan olinib, keyin har bir entity relationi uchun alohida query ketishi. Masalan 100 order va har birining userini olish 1 + 100 queryga aylanadi. Fetch join yoki EntityGraph bilan hal qilinadi.</p></blockquote><hr><h2>43. Qisqa xulosa</h2><p>Spring Data advanced - katta projectlarda data access qatlamini professional boshqarish uchun kerak.</p><table><tbody><tr><th rowspan="1" colspan="1"><p>Mavzu</p></th><th rowspan="1" colspan="1"><p>Qachon kerak</p></th></tr><tr><td rowspan="1" colspan="1"><p>Specification</p></td><td rowspan="1" colspan="1"><p>Optional filterlar ko‘p bo‘lsa</p></td></tr><tr><td rowspan="1" colspan="1"><p>Criteria API</p></td><td rowspan="1" colspan="1"><p>Dynamic queryni pastroq darajada boshqarish kerak bo‘lsa</p></td></tr><tr><td rowspan="1" colspan="1"><p>Projection</p></td><td rowspan="1" colspan="1"><p>Faqat kerakli fieldlarni olish uchun</p></td></tr><tr><td rowspan="1" colspan="1"><p>Auditing</p></td><td rowspan="1" colspan="1"><p>createdAt/updatedAt/createdBy/updatedBy uchun</p></td></tr><tr><td rowspan="1" colspan="1"><p>Optimistic locking</p></td><td rowspan="1" colspan="1"><p>Parallel update conflictlarini oldini olish uchun</p></td></tr><tr><td rowspan="1" colspan="1"><p>Envers</p></td><td rowspan="1" colspan="1"><p>Entity history kerak bo‘lsa</p></td></tr><tr><td rowspan="1" colspan="1"><p>QueryDSL</p></td><td rowspan="1" colspan="1"><p>Type-safe va murakkab querylar uchun</p></td></tr><tr><td rowspan="1" colspan="1"><p>Fetch join / EntityGraph</p></td><td rowspan="1" colspan="1"><p>N+1 muammosini hal qilish uchun</p></td></tr><tr><td rowspan="1" colspan="1"><p>Bulk update</p></td><td rowspan="1" colspan="1"><p>Katta hajmdagi update/delete uchun</p></td></tr></tbody></table><p>Eng muhim fikr:</p><blockquote><p>Spring Data advanced - querylarni ko‘paytirish emas, balki data accessni nazoratli, xavfsiz va performancega mos qilishdir.</p></blockquote>]]></description>
            <author/>
        </item>
    </channel>
</rss>
