Layered architecture

01.07.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Spring Boot | 12 daqiqa o'qish

Spring Boot’da tartibli, tushunarli, kengaytirish oson bo‘lgan backend yozish. Layered architecture, ya’ni qatlamli arxitektura. Bu mavzu ichida: Controller → Service → Repository, DTO vs Entity, mapping, @Transactional, package-by-layer va package-by-feature bor.


1. Layered Architecture nima?

Layered architecture - bu loyihani bir nechta qatlamlarga ajratib yozish usuli.

Oddiy qilib aytganda:

Client / Frontend / Mobile App
        ↓
Controller
        ↓
Service
        ↓
Repository
        ↓
Database

Har bir qatlamning o‘z vazifasi bor. Hammasini bitta joyga yozib yubormaymiz.


2. Nega Layered Architecture kerak?

Agar hamma kodni bitta Controller ichiga yozsak, loyiha tez buziladi.

Masalan:

@RestController
public class UserController {

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        // validation
        // business logic
        // database save
        // response qaytarish
    }
}

Bu yomon uslub. Chunki Controller ichida hamma narsa aralashib ketadi:

  • HTTP request qabul qilish

  • ma’lumot tekshirish

  • biznes qoida

  • database bilan ishlash

  • response qaytarish

Kichik loyiha bo‘lsa ham ishlaydi. Lekin real loyihada bu kodni tushunish, test qilish, o‘zgartirish qiyinlashadi.


3. Asosiy qatlamlar

3.1 Controller layer

Controller - tashqi dunyo bilan aloqa qiladigan qatlam.

Frontend, mobile app yoki Postman request yuboradi. Controller shu requestni qabul qiladi.

@RestController
@RequestMapping("/api/users")
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);
    }
}

Controller vazifasi:

  • request qabul qilish

  • request body olish

  • path variable olish

  • query param olish

  • service chaqirish

  • response qaytarish

Controller qilmasligi kerak:

  • databasega to‘g‘ridan-to‘g‘ri murojaat qilmasligi kerak

  • biznes logic yozmasligi kerak

  • murakkab hisob-kitob qilmasligi kerak

Yomon misol:

@PostMapping
public User create(@RequestBody User user) {
    if (user.getAge() < 18) {
        throw new RuntimeException("User must be adult");
    }

    return userRepository.save(user);
}

Bu noto‘g‘ri. Chunki controller ichida business logic va repository ishlatilgan.


3.2 Service layer

Service - biznes logic yoziladigan joy.

Masalan:

  • foydalanuvchi yaratish

  • buyurtma hisoblash

  • chegirma qo‘llash

  • balansni tekshirish

  • transaction boshqarish

  • bir nechta repository bilan ishlash

@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() < 18) {
            throw new IllegalArgumentException("User must be adult");
        }

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

        return userMapper.toResponse(savedUser);
    }
}

Service vazifasi:

  • business logic

  • tekshiruvlar

  • repository chaqirish

  • transaction boshqarish

  • entity va DTO o‘rtasida ishlash

Service qilmasligi kerak:

  • HTTP status code bilan ko‘p ishlamasligi kerak

  • @RequestBody, @PathVariable kabi web annotationlarga bog‘lanmasligi kerak

  • frontend formatiga haddan tashqari bog‘lanmasligi kerak


3.3 Repository layer

Repository - database bilan ishlaydigan qatlam.

Spring Data JPA’da odatda interface bo‘ladi:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    boolean existsByEmail(String email);

    Optional<User> findByEmail(String email);
}

Repository vazifasi:

  • databasega yozish

  • database’dan o‘qish

  • query bajarish

  • entity bilan ishlash

Repository qilmasligi kerak:

  • business logic yozmasligi kerak

  • DTO qaytarmasligi afzal

  • HTTP bilan aloqasi bo‘lmasligi kerak


4. Entity nima?

Entity - database jadvaliga mos keladigan Java class.

@Entity
@Table(name = "users")
public class User {

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

    private String fullName;

    private String email;

    private Integer age;

    // getters/setters
}

Bu class database bilan bog‘langan.

Masalan jadval:

users
-----
id
full_name
email
age

Entity asosan Repository va Service qatlamida ishlatiladi.


5. DTO nima?

DTO - Data Transfer Object.

Bu client bilan ma’lumot almashish uchun ishlatiladigan class.

Masalan request DTO:

public record UserCreateRequest(
        String fullName,
        String email,
        Integer age
) {
}

Response DTO:

public record UserResponse(
        Long id,
        String fullName,
        String email
) {
}

Bu yerda age response’da qaytmayapti. Chunki clientga hamma ma’lumotni berish shart emas.


6. DTO vs Entity - nima uchun ajratamiz?

Entity’ni to‘g‘ridan-to‘g‘ri API’da ishlatish yomon

Yomon misol:

@PostMapping
public User create(@RequestBody User user) {
    return userRepository.save(user);
}

Nega yomon?

Chunki User entity ichida databasega oid fieldlar bor. Masalan:

private String password;
private Boolean deleted;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;

Agar entity’ni response qilib qaytarsangiz, noxush holat bo‘lishi mumkin:

{
  "id": 1,
  "fullName": "Ali Valiyev",
  "email": "ali@mail.com",
  "password": "123456",
  "deleted": false
}

Bu xavfli.

Shuning uchun:

Request DTO → Service → Entity → Repository
Repository → Entity → Service → Response DTO

7. Mapping nima?

Mapping - DTO’dan Entity yasash yoki Entity’dan DTO yasash.

Masalan manual mapping:

@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()
        );
    }
}

Bu oddiy va tushunarli usul.

Katta loyihalarda MapStruct ishlatiladi.


8. To‘liq oqim qanday ishlaydi?

Masalan frontend request yuboradi:

POST /api/users
Content-Type: application/json

{
  "fullName": "Ali Valiyev",
  "email": "ali@mail.com",
  "age": 22
}

Spring Boot’da oqim:

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

Natija:

{
  "id": 1,
  "fullName": "Ali Valiyev",
  "email": "ali@mail.com"
}

9. @Transactional qayerga qo‘yiladi?

@Transactional odatda Service layerga qo‘yiladi.

Masalan:

@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(...));
    }
}

Nega service layer?

Chunki transaction odatda bitta repository emas, butun business operation uchun kerak bo‘ladi.

Masalan:

Order save bo‘ldi
Payment save bo‘lmadi

Bunday holatda hammasi rollback bo‘lishi kerak. Ya’ni order ham saqlanmasligi kerak.

Shu sabab @Transactional repositoryga emas, service methodga qo‘yiladi.


10. Package-by-layer

Bu eng oddiy struktura:

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

Bu boshlang'ich uchun yaxshi boshlanish.

Afzalligi:

  • tushunish oson

  • Spring Boot tutoriallarda ko‘p uchraydi

  • kichik loyihalar uchun qulay

Kamchiligi:

  • loyiha kattalashsa, fayllar juda ko‘payadi

  • user, order, product kodlari har xil papkalarga sochilib ketadi


11. Package-by-feature

Bu real loyihalarda ko‘proq qulay bo‘ladi.

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

Afzalligi:

  • bitta feature’ga oid kod bitta joyda turadi

  • katta loyihada qulay

  • domain bo‘yicha tartibli bo‘ladi

Java developer sifatida ikkalasini bilish kerak.

Amaliy maslahat:

Kichik o‘quv loyiha → package-by-layer
Real loyiha yoki katta loyiha → package-by-feature

12. To‘liq mini misol

Entity

@Entity
@Table(name = "users")
public class User {

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

    private String fullName;

    private String email;

    private Integer age;

    // getters/setters
}

DTO

public record UserCreateRequest(
        String fullName,
        String email,
        Integer age
) {
}
public record UserResponse(
        Long id,
        String fullName,
        String email
) {
}

Repository

public interface UserRepository extends JpaRepository<User, Long> {

    boolean existsByEmail(String email);
}

Mapper

@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()
        );
    }
}

Service

@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("Email already exists");
        }

        if (request.age() < 18) {
            throw new IllegalArgumentException("User must be adult");
        }

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

        return userMapper.toResponse(savedUser);
    }
}

Controller

@RestController
@RequestMapping("/api/users")
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);
    }
}

13. Eng ko‘p qilinadigan xatolar

Xato 1: Controller’dan repository chaqirish

@RestController
public class UserController {

    private final UserRepository userRepository;

    @PostMapping("/users")
    public User create(@RequestBody User user) {
        return userRepository.save(user);
    }
}

To‘g‘risi:

Controller → Service → Repository

Xato 2: Entity’ni response qilib qaytarish

@GetMapping("/{id}")
public User getById(@PathVariable Long id) {
    return userRepository.findById(id).orElseThrow();
}

To‘g‘risi:

@GetMapping("/{id}")
public UserResponse getById(@PathVariable Long id) {
    return userService.getById(id);
}

Xato 3: Service ichida HTTP bilan ishlash

Yomon:

public ResponseEntity<UserResponse> create(UserCreateRequest request) {
    ...
}

Service ResponseEntity qaytarmagani yaxshi. ResponseEntity - web/controller tomonga yaqin narsa.

Yaxshiroq:

public UserResponse create(UserCreateRequest request) {
    ...
}

14. Qisqa qoida

Esda saqlash uchun:

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

15. Javachi uchun vazifa

Bitta kichik loyiha yozing:

Product CRUD API

Kerakli endpointlar:

POST   /api/products
GET    /api/products
GET    /api/products/{id}
PUT    /api/products/{id}
DELETE /api/products/{id}

Struktura:

product
 ├── Product.java
 ├── ProductRepository.java
 ├── ProductService.java
 ├── ProductController.java
 ├── ProductCreateRequest.java
 ├── ProductUpdateRequest.java
 ├── ProductResponse.java
 └── ProductMapper.java

Product fieldlari:

id
name
price
quantity

Asosiy qoida:

Controller repository’ni chaqirmasin.
Entity response sifatida qaytmasin.
Business logic service ichida bo‘lsin.

Xulosa

Layered architecture - Spring Boot developer uchun eng muhim mavzulardan biri.

Bu arxitektura kodni tartibli qiladi:

Controller → Service → Repository → Database

DTO va Entity’ni ajratish esa xavfsizlik, tozalik va kelajakda kengaytirish uchun juda muhim.