JavaRush /Курси /Модуль 5. Spring /Короткий зміст попередніх лекцій

Короткий зміст попередніх лекцій

Модуль 5. Spring
Рівень 25 , Лекція 1
Відкрита

Перед тим як почати писати код, важливо визначити архітектурний стиль застосунку, його основні компоненти та їх взаємодію. Будь-який досвідчений програміст підтвердить, що погано спроєктована архітектура подібна до дому без фундаменту: може й стояти, але недовго.


1. Огляд архітектури

Вибір архітектурного стилю

Ми розробляємо застосунок у стилі монолітної архітектури. Так-так, ніякого хайпу з мікросервісами на цьому етапі. Чому? Бо наш застосунок буде відносно невеликим, і переходити на мікросервіси немає сенсу. Усе буде зібрано в одному проєкті, але ми розділимо його на логічні модулі.

Компоненти застосунку

Складові нашого застосунку можна розділити на кілька ключових блоків:

  1. REST API: будемо надавати інтерфейс взаємодії через HTTP-запити для зовнішніх користувачів (наприклад, фронтенду).
  2. Сервісний шар (бізнес-логіка): тут ми зосередимо основний функціонал застосунку, такий як обробка даних, валідація і управління транзакціями.
  3. База даних: для збереження інформації.
  4. Безпека: захистимо доступ до наших даних і функціоналу.

Ось приклад схеми компонента:


+-------------------+
|   REST API        |
|-------------------|
|  Контролери       |
+-------------------+
        |
        v
+-------------------+
| Сервісний шар     |
|-------------------|
|  Бізнес-логіка    |
+-------------------+
        |
        v
+-------------------+
|   Доступ до даних |
|-------------------|
|  JPA репозиторії  |
+-------------------+
        |
        v
+-------------------+
|  База даних       |
|-------------------|

2. Проєктування REST API

Спочатку нагадаємо, що REST API — це інтерфейс, який дозволяє взаємодіяти із застосунком через HTTP-запити. Основні принципи REST:

  1. Орієнтація на ресурси: усе в нашому застосунку розглядається як ресурс. Наприклад, користувач — це ресурс /users.
  2. HTTP-методи: для різних операцій використовуються різні методи:
    • GET для отримання даних.
    • POST для створення нових даних.
    • PUT для оновлення даних.
    • DELETE для видалення даних.
  3. HTTP статус-коди: Сервер повертає статус виконання операції. Наприклад:
    • 200 OK для успішної відповіді.
    • 201 Created для успішного створення ресурсу.
    • 404 Not Found для відсутнього ресурсу.

Проєктування endpoint-ів

Плануємо зробити базовий CRUD для користувачів. Ось передбачувані ендпоінти:

Метод URL Опис
GET /users Отримати список користувачів
GET /users/{id} Отримати користувача за ID
POST /users Створити нового користувача
PUT /users/{id} Оновити дані користувача
DELETE /users/{id} Видалити користувача

Ось як це виглядає в контролері:


@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping
    public List<User> getAllUsers() {
        // Повертає список усіх користувачів
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        // Отримує користувача за id
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        // Створює нового користувача
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        // Оновлює дані користувача
    }

    @DeleteMapping("/{id}")
    public ResponseEntity
    deleteUser(@PathVariable Long id) {
        // Видаляє користувача
    }
}

3. Безпека застосунку

Реалізація аутентифікації з використанням Spring Security

Ви вже знаєте, що Spring Security дає нам можливість додавати правила аутентифікації та авторизації.

1. Підключення залежності: У pom.xml додаємо:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. Створення конфігурації безпеки:


@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/users/**").authenticated()
            .anyRequest().permitAll()
            .and()
            .httpBasic(); // Використовуємо базову аутентифікацію
    }
}

Тут ми захищаємо ендпоінти /users/**, а інші лишаємо доступними для всіх.


4. Робота з базою даних

Проєктування бази даних

Для нашого застосунку потрібна таблиця users. Приклад структури (ER-діаграма):

Поле Тип даних Опис
id BIGINT Ідентифікатор
name VARCHAR Ім'я користувача
email VARCHAR Електронна пошта
password VARCHAR Пароль

Реалізація сутностей

Створюємо сутність User з використанням JPA-анотацій:


@Entity
public class User {

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

    @Column(nullable = false)
    private String name;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private String password;

    // Гетери і сетери
}

Репозиторій для роботи з базою

Використовуємо Spring Data JPA для роботи з даними:


@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

Тепер ми можемо виконувати CRUD-операції з даними користувачів через цей інтерфейс.

Конфігурація підключення

У application.properties налаштовуємо підключення до бази:


spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update

Взаємодія компонентів

Отже, як усе це пов'язано?

  1. REST-запит приходить у контролер.
  2. Контролер викликає відповідний метод із сервісного шару.
  3. Сервісний шар взаємодіє з репозиторіями і повертає результат.
  4. Контролер повертає відповідь клієнту.

Ось приклад ланцюжка:


@RestController
public class UserController {

    private final UserService userService;

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

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

@Service
public class UserService {

    private final UserRepository userRepository;

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

    public User getUserById(Long id) {
        return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
    }
}

На цьому етапі архітектура застосунку готова. Далі ми зануримось у декомпозицію задач проєкту, щоб зрозуміти, як краще організувати роботу над рештою модулів.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ