Библиотека Java разработчика
10.5K subscribers
1.17K photos
594 videos
58 files
1.52K links
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate.


По всем вопросам @evgenycarter

РКН clck.ru/3KoGeP
Download Telegram
👮‍♂️ Spring Security: Фейсконтроль для вашего API

Spring Security - это не просто библиотека, это мощный фреймворк, который встает стеной между интернетом и вашим контроллером.

Его работа строится на концепции Filter Chain (Цепочка фильтров). Каждый запрос проходит через серию проверок: "Есть ли токен?", "Валиден ли он?", "Есть ли права?".

🔑 Authentication vs Authorization

Два слова, которые путают все джуниоры.

1. Authentication (Аутентификация): "Кто ты?"
🔴Ввод логина/пароля.
🔴Проверка отпечатка пальца.
🔴Ответ: 401 Unauthorized (если не знаем, кто это).


2. Authorization (Авторизация): "А что тебе можно?"
🔴Ты Вася (мы тебя узнали), но ты хочешь удалить базу данных. Тебе нельзя.
🔴Ответ: 403 Forbidden (знаем кто ты, но не пустим).



🎫 JWT (JSON Web Token) - Паспорт туриста

В микросервисах мы не храним состояние пользователя на сервере (Stateless). Вместо этого, при логине мы выдаем пользователю Токен.

JWT - это строка из трех частей, разделенных точками: Header.Payload.Signature.

🔴Payload: Полезные данные (User ID, Role, Email).
🔴Signature: Цифровая подпись сервера. Гарантирует, что хитрый хакер не поменял в токене роль USER на ADMIN.

Как это работает:

1. Клиент шлет Логин/Пароль -> Сервер проверяет и отдает JWT.

2. Клиент сохраняет JWT (обычно в LocalStorage браузера).

3. При каждом запросе клиент прикрепляет JWT в заголовок:
Authorization: Bearer <token>

4. Сервер видит токен, проверяет подпись и пускает (не ходя в базу данных!).

🛡 Настройка (Spring Boot 3.x)

Раньше мы наследовались от WebSecurityConfigurerAdapter. Забудьте, этот класс Deprecated.
Сейчас мы просто объявляем бин SecurityFilterChain.


@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable) // Для REST API отключаем
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // Никаких сессий!
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll() // Логин доступен всем
.requestMatchers("/admin/**").hasRole("ADMIN") // Админка только админам
.anyRequest().authenticated() // Всё остальное - только с токеном
)
// Добавляем наш кастомный фильтр для проверки JWT
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

return http.build();
}
}



🔓 Что такое OAuth2?

JWT - это когда вы сами выдаете токены.
OAuth2 - это когда токены выдает кто-то большой и доверенный (Google, Facebook, GitHub).

Кнопка "Войти через Google" - это OAuth2.

1. Вы перенаправляете юзера на Google.

2. Google спрашивает: "Разрешить приложению MyShop узнать ваш email?".

3. Google возвращает вам токен.

4. Вы верите этому токену, потому что доверяете Google.

В Spring Boot это настраивается буквально в 5 строк в application.yaml, но под капотом там огромная машина стандартов.


🔥 Итог: безопасность для бэкенда это:

1. Spring Security как движок.

2. JWT как пропуск.

3. Stateless режим (без сессий).

4. HTTPS (обязательно, иначе токен украдут).

#SpringSecurity #JWT #OAuth2 #Java #CyberSecurity

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍92
А вы справитесь с тестом по HighLoad?

Как пройти путь от разработчика до архитектора высоконагруженных систем для работы с крупными проектами?

Пройдите тест, проверьте свои знания для обучения на курсе «Highload Architect» от OTUS. А так же и получите скидку 🎁 до 15.02.2026 - подробности у менеджера.

➡️ Пройти Тест https://vk.cc/cU6fLb

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

❗️Практическое обучение проводится в прямом эфире — вебинары не являются предзаписанными.

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🏗 SOLID - Пять заповедей программиста

Почему один проект живет 10 лет и его легко дорабатывать, а другой через полгода превращается в "Legacy", к которому страшно подходить?
Разница в соблюдении принципов SOLID.

Это аббревиатура из 5 правил, сформулированных Робертом Мартином (Дядя Боб). Если вы нарушаете их - ваш код "гниет".

Давайте разберем каждую букву.

1️⃣ S - Single Responsibility Principle (Единственная ответственность)

"У класса должна быть только одна причина для изменения."


Как делают новички (God Object):
Класс OrderService делает всё:

1. Считает сумму заказа.
2. Сохраняет заказ в БД.
3. Отправляет email клиенту.
4. Генерирует PDF-чек.

Если бизнес попросит изменить формат чека — мы лезем в этот класс. Если поменяется логика БД - опять в него. Риск сломать отправку писем при правке базы данных огромен!

Как надо:
Разбиваем на маленькие классы:

OrderCalculator (считает).
OrderRepository (сохраняет).
EmailNotificationService (шлет письма).
PdfGenerator (печатает).

OrderService теперь просто дирижер, который вызывает эти компоненты.


2️⃣ O - Open-Closed Principle (Открытость/Закрытость)

"Программные сущности должны быть открыты для расширения, но закрыты для модификации."


Это значит: Не меняйте старый рабочий код, чтобы добавить новую фичу.

Плохо:
У нас есть метод расчета доставки.


if (deliveryType == "DHL") { ... }
else if (deliveryType == "Post") { ... }
// Пришла задача добавить FedEx? Придется лезть сюда и добавлять else if!



Хорошо:
Используем полиморфизм.


interface DeliveryStrategy { void deliver(); }
class DhlDelivery implements DeliveryStrategy { ... }
class PostDelivery implements DeliveryStrategy { ... }

// Нужен FedEx? Просто создаем НОВЫЙ класс, не трогая старые!
class FedExDelivery implements DeliveryStrategy { ... }




3️⃣ L - Liskov Substitution Principle (Принцип подстановки Барбары Лисков)

"Наследники должны без проблем заменять родителей."


Если у вас есть класс Bird с методом fly(), а вы создали наследника Penguin (Пингвин), который при вызове fly() бросает ошибку (потому что пингвины не летают) - вы нарушили LSP.

Суть: Если код работает с базовым классом, он должен работать и с любым его наследником, не зная об этом и не ломаясь.


4️⃣ I - Interface Segregation Principle (Разделение интерфейсов)

"Много маленьких интерфейсов лучше, чем один огромный."


Плохо:
Интерфейс Worker имеет методы work() и eat().
Мы создаем класс Robot. Роботы работают, но не едят.
Нам придется реализовать метод eat() и оставить его пустым или кинуть ошибку. Это мусор.

Хорошо:
Разбейте на Workable и Eatable.
Человек имплементирует оба. Робот - только Workable.


5️⃣ D - Dependency Inversion Principle (Инверсия зависимостей)

"Зависьте от абстракций, а не от конкретики."


Это то, что мы учили в Spring (DI).
Ваш Service не должен зависеть от PostgresRepository. Он должен зависеть от интерфейса Repository.
Тогда вы сможете легко подменить Postgres на MySQL или Mock-объект для тестов, не меняя ни строчки в Сервисе.


SOLID - это фильтр. Прежде чем закоммитить код, спросите себя:

1. Не делает ли мой класс слишком много? (S)
2. Придется ли мне переписывать этот класс, если добавятся новые условия? (O)
3. Не ломаю ли я поведение родителя? (L)
4. Не заставляю ли я других реализовывать ненужные методы? (I)
5. Завишу ли я от интерфейсов или от конкретных классов? (D)

#Architecture #SOLID #CleanCode #OODesign

📲 Мы в MAX

👉@BookJava
👍135🔥3
🏗 Порождающие паттерны: Как рождаются объекты?

Создать объект просто: User u = new User().
А если у объекта 20 полей? А если нам нужен только один экземпляр на всё приложение? А если мы не знаем заранее, какой именно класс нам нужен?
Тут на сцену выходят паттерны.

1️⃣ Singleton (Одиночка)

Суть: Гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа.

Где нужен: Логгеры, Конфигурация, Пул соединений с БД.

Как реализовать:

1. Скрываем конструктор (private).
2. Создаем статическое поле с экземпляром.
3. Возвращаем его через статический метод.


public class Database {
// Единственный экземпляр
private static Database instance;

private Database() {} // Никто не создаст объект извне

public static synchronized Database getInstance() {
if (instance == null) {
instance = new Database();
}
return instance;
}
}



⚠️ Важно: В Spring Boot все бины по умолчанию - синглтоны. Вам не нужно писать этот код руками, контейнер Spring сам следит, чтобы сервис был создан один раз.


2️⃣ Builder (Строитель)

Суть: Позволяет создавать сложные объекты пошагово. Спасает от «Телескопического конструктора» (когда у вас конструктор с 10 аргументами, и вы не помните, где там age, а где height).

Было (Ужас):
new User("Alex", null, true, "admin", 25, null);

Стало (Builder):


User user = User.builder()
.name("Alex")
.age(25)
.role("ADMIN")
.active(true)
.build();



🛠 Лайфхак:
В Java не нужно писать Билдер руками (это 50 строк кода). Просто поставьте аннотацию Lombok @Builder над классом.


3️⃣ Factory Method (Фабричный метод)

Суть: Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.
Это реализация принципа Open-Closed. Мы добавляем новые типы продуктов, не ломая существующий код.

Пример: У нас есть "Уведомления". Сегодня это Email, завтра SMS, послезавтра Push.


// 1. Интерфейс
interface Notification { void send(String msg); }

// 2. Реализации
class EmailNotification implements Notification { ... }
class SmsNotification implements Notification { ... }

// 3. Фабрика (Решает, что создать)
class NotificationFactory {
public static Notification create(String type) {
return switch (type) {
case "EMAIL" -> new EmailNotification();
case "SMS" -> new SmsNotification();
default -> throw new IllegalArgumentException("Unknown type");
};
}
}

// Клиентский код (не знает про классы Email/Sms, знает только интерфейс)
Notification notification = NotificationFactory.create("SMS");
notification.send("Hello!");



🔥 Итог

1. Singleton - когда нужен один объект на всю систему.
2. Builder - когда объект сложный и у него много параметров.
3. Factory - когда мы не знаем заранее, какой конкретно объект понадобится, или хотим скрыть логику выбора.

#DesignPatterns #GoF #Singleton #Builder #Factory #Java

📲 Мы в MAX

👉@BookJava
👍5🔥3
🔴 Завтра тестовое собеседование с Java-разработчиком

11 февраля(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир →
@shortcut_sh_bot

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31
🏗 Структурные паттерны: Адаптер, Декоратор, Прокси

Когда вы пытаетесь соединить старую библиотеку с новым кодом или добавить логирование, не переписывая половину проекта - вы используете эти паттерны.

🔌 1. Adapter (Адаптер)

Суть: Делает несовместимые интерфейсы совместимыми.
Это как переходник для розетки. У вас вилка американская, а розетка европейская. Адаптер позволяет им работать вместе.

Где нужен: Когда есть старый класс (Legacy), который нельзя менять, но его нужно использовать в новом коде.

Пример: У нас есть система, которая понимает только KM/H (километры), а внешняя библиотека выдает скорость в MPH (мили).


// 1. Наш интерфейс (чего мы ждем)
interface Movable { double getSpeed(); } // км/ч

// 2. Чужой класс (что есть)
class Bugatti {
double getSpeedMph() { return 268; }
}

// 3. Адаптер (Переводчик)
class MovableAdapter implements Movable {
private Bugatti bugatti;

public MovableAdapter(Bugatti bugatti) {
this.bugatti = bugatti;
}

@Override
public double getSpeed() {
return convertMphToKmph(bugatti.getSpeedMph());
}
}




🎁 2. Decorator (Декоратор/Обертка)

Суть: Динамически добавляет объекту новые обязанности (функционал).
Это альтернатива наследованию. Вместо того чтобы создавать CoffeeWithMilkAndSugar, мы берем Coffee и заворачиваем его в Milk, а потом в Sugar.

Принцип: Матрешка. Каждый декоратор делает свою работу и вызывает следующий.

Пример:


// Базовый кофе
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getCost()); // 10$

// Добавили молоко (Обернули)
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getCost()); // 12$

// Добавили сахар (Обернули еще раз)
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getCost()); // 13$



Важно: В java.io это используется повсюду: new BufferedReader(new FileReader(file)).


🛡️ 3. Proxy (Заместитель)

Суть: Объект-прокладка, который контролирует доступ к другому объекту.
Клиент думает, что общается с реальным объектом, а на самом деле говорит с его заместителем.

Зачем?

1. Ленивая загрузка (Lazy Loading): Не грузить тяжелую картинку/БД, пока ее реально не попросят.

2. Безопасность: Проверить права доступа перед выполнением метода.

3. Логирование: Записать "Метод вызван" и передать вызов дальше.

Пример:


interface Image { void display(); }

class RealImage implements Image {
public RealImage(String file) { loadFromDisk(file); } // Долгая операция!
public void display() { System.out.println("Displaying..."); }
}

class ProxyImage implements Image {
private RealImage realImage;
private String file;

public ProxyImage(String file) { this.file = file; }

@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(file); // Грузим только сейчас!
}
realImage.display();
}
}



Spring Magic: Весь Spring держится на Прокси! Когда вы ставите @Transactional, Spring создает прокси вокруг вашего сервиса, открывает транзакцию, вызывает ваш метод, а потом закрывает транзакцию.


🔥 Итог

🔴Adapter - меняет интерфейс объекта (чтобы подошел).

🔴Decorator - меняет поведение объекта (добавляет фичи), не меняя интерфейс.

🔴Proxy - контролирует доступ к объекту (ленивость, защита).

#DesignPatterns #Adapter #Decorator #Proxy #Architecture

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
🎥 Открытый урок «Class Data Sharing и его перспективы».

🗓 17 февраля в 20:00 МСК
🆓 Бесплатно. Урок в рамках старта курса
«Java Developer. Advanced».

Быстрый, лёгкий старт Java-сервисов — конкурентное преимущество. Разберём, чем поможет Class Data Sharing и где он уместен.


Что будет на вебинаре:
✔️ Назначение Class Data Sharing.
✔️ Поддержка и использование в Spring Boot.
✔️ Разница с Native Image для GraalVM.

В результате вебинара:
Сможете запустить Spring Boot-приложение с использованием CDS и понять базовую настройку. Получите представление, чем CDS отличается от Native Image (GraalVM).

Кому будет интересно:
Подойдёт Java-разработчикам и инженерам, которым важно ускорить старт сервисов и оптимизировать время запуска в проде.

🔗 Ссылка на регистрацию: https://vk.cc/cUiTYA

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 Поведенческие паттерны: Стратегия, Наблюдатель, Цепочка

Эти паттерны помогают избежать спагетти-кода, где один класс управляет всем миром через гигантские if-else.

🗺 1. Strategy (Стратегия)

Суть: Позволяет менять алгоритм поведения объекта прямо "на лету", во время выполнения программы.
Это убийца бесконечных if (type == "CARD") { ... } else if (type == "PAYPAL") { ... }.

Аналогия: Навигатор. Вы строите маршрут из точки А в точку Б. Стратегия - это способ передвижения:

🔴Пешком (один алгоритм).
🔴На машине (другой алгоритм).
🔴На автобусе (третий алгоритм).
Цель одна, пути реализации разные.

Код:


// Общий интерфейс
interface RouteStrategy {
void buildRoute(String a, String b);
}

// Конкретные стратегии
class RoadStrategy implements RouteStrategy { ... }
class WalkingStrategy implements RouteStrategy { ... }

// Контекст (Навигатор)
class Navigator {
private RouteStrategy strategy;

public void setStrategy(RouteStrategy strategy) {
this.strategy = strategy; // Меняем на лету!
}

public void buildRoute(String a, String b) {
strategy.buildRoute(a, b);
}
}




👀 2. Observer (Наблюдатель / Listener)

Суть: Один объект (Subject) меняет свое состояние, и все зависимые от него объекты (Observers) тут же узнают об этом.

Аналогия: YouTube-канал.

🔴Блогер (Subject) выпускает видео.
🔴Подписчики (Observers) получают уведомление.
🔴Если вы отписались - уведомления приходить перестанут.

Код: Это основа всех UI-фреймворков (кнопка нажата -> слушатель сработал) и даже архитектуры Kafka.


class NewsAgency {
private List<Channel> channels = new ArrayList<>();

public void subscribe(Channel channel) {
channels.add(channel);
}

public void broadcast(String news) {
for (Channel channel : channels) {
channel.update(news); // Уведомляем всех!
}
}
}




🔗 3. Chain of Responsibility (Цепочка обязанностей)

Суть: Запрос передается по цепочке обработчиков. Каждый обработчик решает: обработать запрос самому или передать следующему.

Аналогия: Техподдержка.

1. Сначала отвечает Чат-бот (Уровень 1). Не справился? -> Передает дальше.

2. Оператор колл-центра (Уровень 2). Не справился? -> Передает дальше.

3. Инженер (Уровень 3). Решает проблему.

Пример в Spring:
Spring Security работает именно так! Ваш HTTP-запрос проходит через цепочку фильтров:

🔴CorsFilter (проверяет домен) -> JwtFilter (проверяет токен) -> UsernamePasswordFilter (проверяет логин).
Если хоть один фильтр скажет "Нет", запрос дальше не пойдет.

Код:


abstract class SupportHandler {
protected SupportHandler next;

public void setNext(SupportHandler next) { this.next = next; }

public void handleRequest(String issue) {
if (canHandle(issue)) {
solve();
} else if (next != null) {
next.handleRequest(issue); // Передаем следующему
}
}
}




🔥 Итог

🔴Strategy - Выбираем алгоритм действия (Платим картой или кэшем?).
🔴Observer - Слушаем изменения (Вышло видео -> пришло уведомление).
🔴Chain of Responsibility - Передаем эстафету (Фильтр 1 -> Фильтр 2 -> Контроллер).

#DesignPatterns #Behavioral #Strategy #Observer #ChainOfResponsibility

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
🧅 Архитектура: От Слоев к Луковице

Проблема Слоенки (Database Driven Design)

В классическом Spring-приложении зависимости идут сверху вниз:

1. Контроллер зависит от Сервиса.
2. Сервис зависит от Репозитория (Базы данных).

В чем подвох?
Ваша бизнес-логика (Сервис) намертво привязана к деталям хранения данных (БД).

🔴Хотите поменять SQL на NoSQL? Переписывайте сервис.

🔴Хотите протестировать логику? Придется мокать базу данных.

🔴Главный грех: База данных диктует, как писать бизнес-логику. А должно быть наоборот!


Решение: Clean / Hexagonal / Onion

Дядя Боб, Алистер Кокберн и другие умные дядьки придумали, как перевернуть игру.
Главная идея: Зависимости должны быть направлены ТОЛЬКО внутрь, к центру.

Представьте приложение как Луковицу.

1. Ядро (Core / Domain) - Центр Вселенной

Здесь живет ваша Бизнес-логика.

🔴Сущности (User, Order).

🔴Правила (User не может быть моложе 18 лет).

🔴Правило: Здесь НЕТ фреймворков. Никакого Spring, никакого Hibernate, никакого SQL. Только чистая Java.

🔴Этот слой ничего не знает о внешнем мире.

2. Порты (Ports / Use Cases) - Граница

Ядро определяет интерфейсы (Порты), которые ему нужны для работы.

🔴Например: interface UserRepository (найти пользователя, сохранить пользователя).

🔴Заметьте: интерфейс лежит внутри домена!

3. Адаптеры (Adapters / Infrastructure) - Внешний мир

Здесь живут детали реализации.

🔴PostgresUserRepository реализует интерфейс UserRepository.

🔴RestController вызывает методы Ядра.

🔴Здесь подключается Spring, Hibernate, Kafka и всё остальное.

🔄 Инверсия Зависимостей (DIP)

Следите за руками:

1. В слоеной архитектуре: Service зависит от PostgresDao.
2. В чистой архитектуре: Service зависит от Интерфейса. А PostgresDao зависит от Интерфейса.

Оба зависят от абстракции. БД стала просто плагином. Вы можете выкинуть Postgres и поставить заглушку (In-Memory Map) - и бизнес-логика даже не заметит подмены!

⚖️ Когда что использовать?

1. Layered (Controller-Service-Repo)

Простые CRUD-приложения.
Админки, прототипы.
Когда логики почти нет, просто перекладываем данные.

2. Hexagonal (Ports & Adapters)

Сложная бизнес-логика (Банкинг, Финтех, Логистика).
Приложение живет долго (5+ лет).
Нужно писать много Unit-тестов для ядра, не поднимая контекст Spring.

🔥 Итог

🔴Layered: Быстро писать, сложно поддерживать. БД - главная.
🔴Clean: Дольше писать (много маппингов DTO <-> Entity), легко поддерживать. Логика - главная.

#Architecture #CleanArchitecture #Hexagonal #Spring #Java

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
🧠 Domain Driven Design: Программируем на языке бизнеса

Главная проблема IT: Разработчики говорят на языке таблиц (INSERT, Foreign Key, DTO), а бизнес на языке денег и процессов («Провести проводку», «Списать остаток»).

DDD (Предметно-ориентированное проектирование) - это попытка убрать переводчика.


🗣 1. Ubiquitous Language (Единый язык)

Это фундамент. Код должен звучать так же, как речь эксперта.

🔴Плохо (CRUD-мышление):
• Бизнес: "Клиент сменил адрес доставки".
• Код: user.setAddress("New York"); userRepo.save(user);


🔴Хорошо (DDD):
• Код: user.relocateTo(new Address("New York"));

Методы должны называться глаголами бизнеса, а не сеттерами.


📦 2. Bounded Context (Ограниченный контекст)

Самая большая ошибка новичка - создать один класс Product на всё приложение.

🔴Для Продаж: Товар - это цена, описание, картинки.
🔴Для Склада: Товар - это вес, габариты, номер полки.
🔴Для Бухгалтерии: Товар - это актив, амортизация, инвентарный номер.

В DDD мы не делаем монстра. Мы создаем разные модели для разных контекстов.

🔴SalesContext.Product
🔴WarehouseContext.StockItem

Эти модели могут даже иметь разные ID и общаться друг с другом только через события (Kafka).


🏗 3. Tactical DDD (Строительные блоки)

Как писать код внутри контекста?

A. Entity (Сущность)

Объект, у которого есть Identity (ID).
Если у двух людей одинаковое имя, это все равно два разных человека (разные ID).

🔴Пример: User, Order.
🔴Они живут долго, меняют свое состояние, но остаются собой.

B. Value Object (Объект-значение)

Объект, который определяется только своими данными. У него нет ID. Он неизменяем (Immutable).

🔴Пример: Money, Address, Color.
🔴Если у меня есть 100 рублей и у вас 100 рублей - это одни и те же 100 рублей. Нам не важно, какая именно это купюра.
🔴Правило: Не используйте примитивы! Вместо String email используйте класс EmailAddress. Там можно спрятать валидацию.

C. Aggregate (Агрегат)

Это кластер объектов, которые живут и умирают вместе.

🔴Пример: Заказ (Order) + Позиции заказа (OrderItems).
🔴Aggregate Root (Корень): Это главный объект (Order).
🔴Правило: Извне можно обращаться только к Корню. Нельзя получить ссылку на OrderItem и изменить его цену напрямую. Вы должны сказать: order.changeItemPrice(...). Это гарантирует целостность данных.


🩸 Anemic vs Rich Model (Анемичная vs Богатая модель)

Анемичная (Стандартный Spring):
Класс — это просто мешок с геттерами и сеттерами. Вся логика лежит в Service.


// Service
public void completeOrder(Long id) {
Order order = repo.findById(id);
if (order.getStatus() != PAID) throw ...
order.setStatus(COMPLETED); // Кто угодно может поменять статус!
repo.save(order);
}



Богатая (DDD):
Логика и валидация живут внутри Сущности. Сервис просто координирует работу.


// Entity
public void complete() {
if (this.status != PAID) throw new DomainException("Не оплачено!");
this.status = COMPLETED;
// Можно даже вернуть событие OrderCompletedEvent
}

// Service
public void completeOrder(Long id) {
Order order = repo.findById(id);
order.complete(); // Вся бизнес-логика внутри
repo.save(order);
}



🔥 Итог

DDD - это сложно, но необходимо для больших проектов.

1. Говорите на языке бизнеса (Ubiquitous Language).
2. Разделяйте модели (Bounded Contexts).
3. Используйте Value Objects вместо примитивов.
4. Прячьте логику внутрь Rich Domain Model.

#Architecture #DDD #DomainDrivenDesign #Java #Microservices

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍81👎1
❗️Микросервисы часто обещают масштабируемость, но на практике превращаются в сложную и хрупкую систему.

Причина почти всегда одна — неверная декомпозиция. Слишком мелкие сервисы, плотные связи, распределённые транзакции и постоянные проблемы с данными.

На открытом уроке:
- разберём, как правильно делить систему на сервисы - поговорим о границах микросервисов, ключевых шаблонах декомпозиции, подходах к взаимодействию и управлению согласованностью данных
- вы увидите, как принимать архитектурные решения не по шаблону, а с учётом бизнес-контекста, нагрузки и эволюции системы
- разберём типовые ошибки, из-за которых микросервисы теряют автономность и становятся сложнее монолита

📌 Встречаемся 18 февраля в преддверии старта курса «Highload Architect».

Зарегистрируйтесь, чтобы не пропустить: https://vk.cc/cUudMf

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🧹 Гигиена кода: Рефакторинг и Технический долг

Почему проекты умирают? Не из-за плохой идеи, а из-за того, что стоимость добавления новой фичи становится выше, чем прибыль от неё.
Разработчики тратят 90% времени на чтение и распутывание старого кода, и только 10% - на написание нового.

💳 1. Technical Debt (Технический долг)

Это метафора, придуманная Уордом Каннингемом.

🔴Суть: Вы берете "кредит" у качества кода, чтобы выпустить фичу быстрее ("Сделаем костыль, потом поправим").

🔴Проценты: Каждая минута, потраченная на борьбу с этим костылем в будущем.

🔴Банкротство: Момент, когда код настолько запутан, что проще всё переписать с нуля, чем добавить кнопку.

Главное правило: Долги надо возвращать. Выделяйте 20% времени в спринте на рефакторинг.

2. Code Smells (Запахи кода)

Как понять, что код "тухнет"? По запаху.
Это не баги (код работает), это признаки плохого дизайна.

Топ-5 самых вонючих мест:

1. God Object (Божественный объект): Класс Manager или Utils, который знает всё и делает всё. Нарушает SRP (Single Responsibility Principle).

2. Long Method (Длинный метод): Если метод не помещается на один экран монитора - это запах.

3. Magic Numbers (Магические числа):
Плохо: if (status == 7) ... (Что такое 7? Счастливое число?)
Хорошо: if (status == STATUS_ACTIVE) ...


4. Duplicated Code (Дублирование): Copy-Paste это зло. Если вы нашли ошибку в одном месте, вам придется искать её копии по всему проекту. Принцип DRY (Don't Repeat Yourself).

5. Feature Envy (Зависть к чужим данным): Метод класса А постоянно обращается к полям класса Б. Скорее всего, этот метод должен жить в классе Б.

🛠 3. Refactoring (Рефакторинг)

Это процесс изменения структуры кода без изменения его поведения.

Золотое правило Бойскаута:

"Оставь место стоянки чище, чем оно было до твоего прихода."


Зашли в файл поправить баг? Поправьте заодно название переменной или вынесите метод. Маленькие улучшения каждый день спасают проект.

💻 До и После

🤢 Было (Smelly Code):


public void process(Order o) {
// Magic Number + Long Method + Feature Envy
if (o.getItems().size() > 0 && o.getStatus() == 1) {
double total = 0;
for (Item i : o.getItems()) {
total += i.getPrice() * 1.2; // Что такое 1.2? НДС? Наценка?
}
System.out.println("Total: " + total); // Hardcoded output
}
}



😎 Стало (Clean Code):


private static final double TAX_RATE = 1.2;
private static final int STATUS_ACTIVE = 1;

public void printOrderTotal(Order order) {
if (isEligibleForProcessing(order)) {
double total = calculateTotal(order);
outputResult(total);
}
}

private boolean isEligibleForProcessing(Order order) {
return !order.isEmpty() && order.getStatus() == STATUS_ACTIVE;
}

private double calculateTotal(Order order) {
return order.getItems().stream()
.mapToDouble(item -> item.getPrice() * TAX_RATE)
.sum();
}



Код стал читаемым, как книга.

🔥 Итог

1. Технический долг неизбежен, но его нужно гасить.
2. Запахи кода (магические числа, дубликаты) подсказывают, где рефакторить.
3. Рефакторинг делаем только при наличии Тестов (иначе вы просто ломаете рабочий код).

#Refactoring #CleanCode #TechnicalDebt #CodeSmells #Java

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍42👏1
🚀 CI/CD: Роботы делают рутину за вас

Аббревиатура состоит из двух частей, и они решают разные проблемы.

🛠 1. CI (Continuous Integration / Непрерывная интеграция)

Суть: Разработчики постоянно (несколько раз в день) сливают свой код в общую ветку (например, main или develop).
Каждый раз, когда вы делаете git push, специальный сервер (GitLab CI, GitHub Actions, Jenkins) автоматически:

1. Скачивает ваш свежий код.
2. Собирает проект (mvn clean compile).
3. Запускает все Unit и Integration тесты.

Если хоть один тест упал - сборка помечается красным крестиком (Build Failed). Код не пройдет дальше.
Итог: Ваша главная ветка в Git всегда находится в рабочем состоянии.

📦 2. CD (Continuous Delivery & Deployment)

Здесь две буквы "D", и они немного отличаются:

🔴Continuous Delivery (Доставка): Код автоматически собирается в готовый артефакт (например, Docker-образ) и кладется в хранилище. Нажать кнопку "Опубликовать на Production" должен человек (например, тимлид).

🔴Continuous Deployment (Развертывание): Полная автоматизация. Прошли тесты? Собрался образ? Он сразу же автоматически загружается на боевой сервер и заменяет старую версию. (Так делает Amazon, выкатывая обновления тысячи раз в день).

⚙️ Анатомия Пайплайна (Pipeline)

Пайплайн - это скрипт, состоящий из шагов (Stages), которые выполняются строго друг за другом. Упал предыдущий - следующий не запустится.

Типичный пайплайн для Spring Boot + Docker:

1. Lint (Проверка стиля кода, нет ли неиспользуемых импортов).
2. Test (Запуск JUnit тестов).
3. Build (Сборка `app.jar`).
4. Dockerize (Сборка Docker-образа и отправка его в Docker Registry).
5. Deploy (Команда серверу: "Скачай новый образ и перезапустись").

💻 Как это выглядит в коде? (GitHub Actions)

Вам не нужно кликать мышкой в интерфейсах. Пайплайн описывается кодом (YAML) и лежит прямо в вашем репозитории (подход Infrastructure as Code).

Вот пример простого .github/workflows/build.yml для Java-проекта:


name: Spring Boot CI/CD

on:
push:
branches: [ "main" ] # Запускать только при пуше в main

jobs:
build-and-test:
runs-on: ubuntu-latest # Выделяем виртуальную машину Linux

steps:
- uses: actions/checkout@v3 # 1. Скачиваем код из Git

- name: Установка Java 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'

- name: Сборка и Тесты (Maven) # 2. Запускаем тесты и сборку
run: ./mvnw clean package

# Дальше могут быть шаги для сборки Docker и деплоя...



Стоит вам сделать git push, и GitHub сам поднимет сервер, выполнит эти команды и пришлет вам письмо, если что-то сломалось.

🆚 Что выбрать?

🔴Jenkins: "Дед" в мире CI/CD. Мощный, гибкий, но сложный в настройке (нужно поднимать свой сервер). Написан на Java.

🔴GitLab CI: Стандарт индустрии корпоративного сектора. Очень удобен, так как встроен прямо в репозиторий кода.

🔴GitHub Actions: Современный, быстрый, идеален для Open Source и проектов, уже живущих в GitHub.

🔥 Итог

CI/CD убивает фактор "человеческой ошибки". Вы перестаете бояться релизов. Деплой новой фичи превращается из стрессового события в пятницу вечером в скучную рутину: сделал пуш подождал 5 минут фича на проде.

#DevOps #CICD #Java #SpringBoot #GitHubActions

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
⚡️ Хотите освоить Backend-разработку с нуля, но не знаете, с чего начать? Java — это лучший старт!


Курс «Java-разработчик» от OTUS — это 15 месяцев обучения, которое откроет вам двери в мир Java-разработки. Программа включает живые лекции от опытных наставников и актуальные инструменты, такие как IntelliJ IDEA, Spring, Hibernate, Docker и Kubernetes. Мы разбираем всё: от синтаксиса Java до создания серверных приложений и работы с базами данных.


После курса вы сможете:
создавать серверные приложения на Java;
работать с реляционными и NoSQL базами данных;
осваивать и применять фреймворки Spring, Hibernate, Docker, Kafka, RabbitMQ;
претендовать на позицию Middle Java Developer в крупных компаниях.



Оставьте заявку и получите скидку на большое обучение «Java-разработчик»: https://vk.cc/cUAK9u


Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
👍1🤨1
☸️ Kubernetes: Оркестратор вашего зоопарка

Представьте, что у вас 50 микросервисов, каждый запущен в 3 экземплярах (для надежности). Итого 150 контейнеров.
Вдруг сервер №2 сгорает. 50 контейнеров умирают.
Без K8s: Админ просыпается в 3 ночи и руками запускает их на сервере №3.
С K8s: Вы спите. K8s замечает, что "кого-то не хватает", и сам перезапускает умершие контейнеры на живых серверах за секунды. Это называется Self-healing (Самовосстановление).

Чтобы понять K8s, нужно выучить всего три главных слова: Pod, Deployment, Service.

📦 1. Pod - Атом системы

В Docker мы запускаем Контейнеры. В Kubernetes мы запускаем Поды.

Pod - это минимальная единица. Это обертка вокруг одного (или нескольких) контейнеров.
• У Пода есть свой IP-адрес.
• Обычно: 1 Pod = 1 Контейнер с вашим app.jar.
• Редко: 1 Pod = Java App + Sidecar (например, агент логирования). Они живут вместе, как сиамские близнецы, и делят сеть.

👮‍♂️ 2. Deployment (Развертывание) - Начальник

Вы никогда не создаете Поды вручную. Потому что Поды смертны. Если Под умер - он умер.
Вместо этого вы создаете Deployment. Это инструкция для K8s:

"Я хочу, чтобы у меня ВСЕГДА было 3 копии моего приложения OrderService".


Deployment создает ReplicaSet, который следит за численностью.

• Если один Под упал K8s создает новый.
• Если нагрузка выросла Вы меняете цифру 3 на 10, и K8s мгновенно создает еще 7 копий.

🚦 3. Service (Сервис) - Единая точка входа

Поды рождаются и умирают. У них меняются IP-адреса.
Как фронтенду узнать, на какой IP слать запрос, если они меняются каждую минуту?

Тут выходит Service.
Это стабильный сетевой адрес (и DNS-имя), который не меняется никогда.

• Service работает как Load Balancer (Балансировщик).
• Он принимает запрос на http://order-service и пересылает его на один из живых Подов. Ему все равно, 3 их или 30.

📝 Как это выглядит в коде? (YAML)

В мире K8s мы не пишем команды, мы пишем Манифесты (YAML-файлы), описывающие Желаемое состояние.


# 1. Описываем Deployment (Что запускать?)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-java-app
spec:
replicas: 3 # Хочу 3 экземпляра!
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: java-app
image: my-docker-hub/app:v1 # Берем этот образ
ports:
- containerPort: 8080

---
# 2. Описываем Service (Как достучаться?)
apiVersion: v1
kind: Service
metadata:
name: my-backend-service
spec:
selector:
app: backend # Ищи Поды с меткой 'backend'
ports:
- protocol: TCP
port: 80 # Внешний порт
targetPort: 8080 # Порт контейнера



Вы скармливаете этот файл командой kubectl apply -f app.yaml, и магия случается.

🔥 Итог

1. Pod: Обертка над контейнером.
2. Deployment: Следит, чтобы нужное количество Подов всегда работало.
3. Service: Стабильный адрес, распределяющий запросы по Подам.

#Kubernetes #K8s #DevOps #Docker #Java

📲 Мы в MAX

👉@BookJava
👍51🔥1
📊 Мониторинг: Пульс вашего приложения

В мире мониторинга Java есть три главных игрока:

1. Spring Boot Actuator (Генерирует метрики).
2. Prometheus (Собирает и хранит их).
3. Grafana (Рисует красивые графики).

Давайте разберем, как они работают вместе.

1️⃣ Spring Boot Actuator & Micrometer

Сначала нужно научить приложение рассказывать о себе.
В Spring Boot это делается добавлением двух зависимостей: Actuator и Micrometer.

Micrometer это как SLF4J, только для метрик. Это фасад. Вы пишете код один раз, а Micrometer умеет отправлять эти данные хоть в Prometheus, хоть в Datadog, хоть в New Relic.

В pom.xml:


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>



В application.yaml:


management:
endpoints:
web:
exposure:
include: prometheus, health, info



Теперь, если вы перейдете по адресу /actuator/prometheus, вы увидите не JSON, а скучный текст:


# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{area="heap",id="G1 Eden Space",} 2.5165824E7
http_server_requests_seconds_count{uri="/users",status="200",} 452.0



Это и есть пища для Прометея.

2️⃣ Prometheus: Пылесос данных

Prometheus это Time Series Database (База данных временных рядов). Она хранит цифры с привязкой ко времени.

Его киллер-фича: Pull Model (Модель вытягивания).
В отличие от логов, которые приложение само отправляет (Push), Prometheus сам приходит к вашему приложению раз в 15 секунд и скачивает (Scrape) данные со страницы /actuator/prometheus.

Почему Pull лучше Push?
Если ваше приложение под дикой нагрузкой и умирает, оно не сможет отправить метрики. Но Prometheus придет, увидит, что ответа нет, и зафиксирует: "Сервис упал".

3️⃣ Grafana: Капитанский мостик

Prometheus хранит данные, но смотреть на них в текстовом виде больно.
Grafana подключается к Prometheus и превращает скучные цифры в космолет.

Вы можете создать дашборды для всего:

🔴JVM: Сколько памяти съедено? Как часто работает Garbage Collector?
🔴Tomcat: Сколько потоков занято?
🔴Бизнес-метрики: Сколько заказов оформлено за час? Какая выручка?

Alerting (Оповещения):
Самое важное - Графана умеет "кричать".
Вы настраиваете правило: "Если количество ошибок 500 превышает 1% в течение 5 минут - отправь сообщение в Telegram/Slack команде дежурных".

🛠 Кастомные метрики

Spring дает кучу метрик из коробки (CPU, Memory, HTTP requests). Но бизнесу нужны свои цифры.
Создать их легко через MeterRegistry.


@Service
public class OrderService {

private final Counter orderCounter;

public OrderService(MeterRegistry registry) {
// Создаем счетчик "orders.created"
this.orderCounter = registry.counter("orders.created");
}

public void createOrder(Order order) {
repo.save(order);
orderCounter.increment(); // +1 к метрике
}
}



Теперь в Grafana вы увидите график "Заказов в секунду".

🔥 Итог

1. Actuator открывает "дверь" (/actuator/prometheus).
2. Prometheus заходит в эту дверь каждые 15 секунд и забирает цифры.
3. Grafana рисует графики на основе этих цифр и будит вас ночью, если всё сломалось.

#DevOps #Monitoring #Prometheus #Grafana #SpringBoot

📲 Мы в MAX

👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥62
👩‍💻 Открытый урок «JDBC — ваш швейцарский нож для работы с данными»

🗓 26 февраля в 20:00 МСК

🆓 Бесплатно. Урок в рамках старта курса «Java Developer. Professional» от Otus.

Мастерство работы с базами данных: эффективные инструменты и лучшие практики для разработчиков.


О чём поговорим: 
Основы JDBC: что это такое, зачем нужно и как работает
Практические примеры выполнения сложных запросов
Работа с транзакциями и обработка ошибок в JDBC
Оптимизация производительности при работе с данными через JDBC


Кому будет интересно:

Вебинар будет полезен разработчикам, инженерам по базам данных и архитекторам ПО, стремящимся улучшить навыки работы с базами данных и оптимизировать взаимодействие с данными.


🔗 Ссылка на регистрацию: https://vk.cc/cUM6xz


Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576