🔒 Sealed Classes: Архитектурный фейсконтроль
В ООП всегда была проблема с наследованием. Либо ваш класс открыт для всех («Наследуйся кто хочет!»), либо он закрыт наглухо (
Но что, если я хочу, чтобы мой класс
Раньше это решалось костылями (пакетная видимость, скрытые конструкторы). В Java 17 появился официальный механизм: Sealed Classes.
🚧 Как это работает?
Вы помечаете класс (или интерфейс) ключевым словом
Теперь, если Вася из соседнего отдела попытается написать
🚦 Правило трех дорог
Наследники запечатанного класса обязаны выбрать свою судьбу. Они должны иметь один из трех модификаторов:
1.
2.
3.
🧩 Главная фишка: Комбо со Switch
Зачем это нужно, кроме запретов? Ради безопасности.
Когда вы используете
Вам больше не нужно писать ветку
Если завтра вы добавите в
Это спасает от багов, когда в бизнес-логику добавили новый тип, а обработчики обновить забыли.
🔥 Итог
Sealed Classes + Records + Switch Pattern Matching = 💎
Это "Святая Троица" современной Java. Используйте запечатанные классы для моделирования доменной области, где количество вариантов конечно и известно заранее (статусы заказа, типы платежей, виды пользователей).
#Java17 #SealedClasses #Architecture #CleanCode
📲 Мы в MAX
👉@BookJava
В ООП всегда была проблема с наследованием. Либо ваш класс открыт для всех («Наследуйся кто хочет!»), либо он закрыт наглухо (
final).Но что, если я хочу, чтобы мой класс
Shape (Фигура) могли наследовать только Circle и Square, и больше никто?Раньше это решалось костылями (пакетная видимость, скрытые конструкторы). В Java 17 появился официальный механизм: Sealed Classes.
🚧 Как это работает?
Вы помечаете класс (или интерфейс) ключевым словом
sealed и после слова permits перечисляете тех, кому "можно".
// Родитель: Разрешает наследование ТОЛЬКО этим двум классам
public sealed interface Payment
permits CreditCard, Cash {
}
Теперь, если Вася из соседнего отдела попытается написать
class Crypto extends Payment, компилятор ударит его по рукам. 🚫🚦 Правило трех дорог
Наследники запечатанного класса обязаны выбрать свою судьбу. Они должны иметь один из трех модификаторов:
1.
final - Цепочка наследования обрывается. Дальше наследовать нельзя.
public final class Cash implements Payment { ... }
2.
sealed - Иерархия продолжается, но снова под контролем.
public sealed class CreditCard implements Payment permits Visa, MasterCard { ... }
3.
non-sealed - "Открываем шлюзы". Дальше от этого класса может наследоваться кто угодно (возврат к старому поведению Java).
public non-sealed class DebitCard implements Payment { ... }
🧩 Главная фишка: Комбо со Switch
Зачем это нужно, кроме запретов? Ради безопасности.
Когда вы используете
sealed иерархию в новом switch, компилятор знает все возможные варианты.Вам больше не нужно писать ветку
default!
// Метод обработки платежа
String process(Payment p) {
return switch (p) {
case CreditCard c -> "Processing card: " + c.getNumber();
case Cash c -> "Processing cash amount: " + c.getAmount();
// default НЕ НУЖЕН! Компилятор знает, что третьго не дано.
};
}
Если завтра вы добавите в
permits новый класс Crypto, код перестанет компилироваться, пока вы не обработаете этот новый кейс в свитче.Это спасает от багов, когда в бизнес-логику добавили новый тип, а обработчики обновить забыли.
🔥 Итог
Sealed Classes + Records + Switch Pattern Matching = 💎
Это "Святая Троица" современной Java. Используйте запечатанные классы для моделирования доменной области, где количество вариантов конечно и известно заранее (статусы заказа, типы платежей, виды пользователей).
#Java17 #SealedClasses #Architecture #CleanCode
👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤1
📨 Apache Kafka: Нервная система микросервисов
Представьте, что вы заказали пиццу.
🔴 REST подход: Вы стоите у прилавка и смотрите на повара, пока он не закончит. Вы не можете отойти. Если повар уснул - вы застряли.
🔴 Kafka подход: Вы бросаете чек в коробку "Заказы" и уходите по своим делам. Повар возьмет заказ, когда освободится. Когда пицца будет готова, он положит её на стол выдачи, и вы получите уведомление.
Kafka - это распределенный лог событий. Это не просто очередь, это история всего, что произошло в системе.
🧩 Основные понятия
1. Topic (Топик) - Это "папка" или канал, куда падают сообщения. Например:
2. Producer (Продюсер) - Тот, кто пишет сообщения в топик (например, сервис Заказов).
3. Consumer (Консьюмер) - Тот, кто читает сообщения (например, сервис Уведомлений или Склад).
4. Broker (Брокер) - Сервер Kafka, который хранит эти данные.
🚀 Главная фишка: Fire and Forget
Когда
Он просто кидает событие
Сервисы-подписчики (Consumers) разгребут эти сообщения в своем темпе. Если сервис SMS упал, он поднимется через час, прочитает топик с того места, где остановился, и дошлет все смски. Данные не пропадут.
💻 Spring Kafka: Как писать код?
В Spring Boot работа с Kafka максимально упрощена.
1. Настройка (
2. Продюсер (Отправляем сообщение)
Нам нужен бин
3. Консьюмер (Слушаем эфир)
Просто вешаем аннотацию
⚡ Kafka vs RabbitMQ (Коротко)
Частый холивар.
🔴 RabbitMQ - это классическая Очередь. Сообщение прочитали -> оно удалилось. Это "умный брокер, глупые потребители".
🔴 Kafka - это Лог. Сообщения хранятся на диске днями (или вечно). Их можно перечитывать заново (Replay). Это идеально для аналитики и восстановления данных.
🔥 Итог
Kafka позволяет микросервисам быть слабосвязанными (decoupled).
🔴 Сервис А не знает о существовании Сервиса Б.
🔴 Система выдерживает пиковые нагрузки (Kafka просто буферизирует сообщения, пока консьюмеры не разгребут их).
#Java #Kafka #Microservices #SpringCloud #Architecture
📲 Мы в MAX
👉@BookJava
Представьте, что вы заказали пиццу.
Kafka - это распределенный лог событий. Это не просто очередь, это история всего, что произошло в системе.
🧩 Основные понятия
1. Topic (Топик) - Это "папка" или канал, куда падают сообщения. Например:
orders-topic, email-topic.2. Producer (Продюсер) - Тот, кто пишет сообщения в топик (например, сервис Заказов).
3. Consumer (Консьюмер) - Тот, кто читает сообщения (например, сервис Уведомлений или Склад).
4. Broker (Брокер) - Сервер Kafka, который хранит эти данные.
🚀 Главная фишка: Fire and Forget
Когда
OrderService создает заказ, ему плевать, работает ли сейчас сервис отправки SMS или сервис начисления бонусов.Он просто кидает событие
OrderCreated в Kafka и мгновенно возвращает ответ пользователю "Заказ принят".Сервисы-подписчики (Consumers) разгребут эти сообщения в своем темпе. Если сервис SMS упал, он поднимется через час, прочитает топик с того места, где остановился, и дошлет все смски. Данные не пропадут.
💻 Spring Kafka: Как писать код?
В Spring Boot работа с Kafka максимально упрощена.
1. Настройка (
application.yaml)
spring:
kafka:
bootstrap-servers: localhost:9092 # Адрес брокера
consumer:
group-id: my-group # Важно для масштабирования
2. Продюсер (Отправляем сообщение)
Нам нужен бин
KafkaTemplate.
@Service
@RequiredArgsConstructor
public class OrderProducer {
private final KafkaTemplate<String, String> kafkaTemplate;
public void sendOrderEvent(String orderId) {
// Отправляем в топик "orders"
kafkaTemplate.send("orders", orderId);
System.out.println("Сообщение отправлено: " + orderId);
}
}
3. Консьюмер (Слушаем эфир)
Просто вешаем аннотацию
@KafkaListener.
@Service
public class NotificationConsumer {
@KafkaListener(topics = "orders", groupId = "notification_group")
public void listen(String orderId) {
System.out.println("Получено событие для заказа: " + orderId);
// Тут логика отправки email/sms
sendEmail(orderId);
}
}
⚡ Kafka vs RabbitMQ (Коротко)
Частый холивар.
🔥 Итог
Kafka позволяет микросервисам быть слабосвязанными (decoupled).
#Java #Kafka #Microservices #SpringCloud #Architecture
📲 Мы в MAX
👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤2🔥2
🏗 SOLID - Пять заповедей программиста
Почему один проект живет 10 лет и его легко дорабатывать, а другой через полгода превращается в "Legacy", к которому страшно подходить?
Разница в соблюдении принципов SOLID.
Это аббревиатура из 5 правил, сформулированных Робертом Мартином (Дядя Боб). Если вы нарушаете их - ваш код "гниет".
Давайте разберем каждую букву.
1️⃣ S - Single Responsibility Principle (Единственная ответственность)
⛔ Как делают новички (God Object):
Класс
1. Считает сумму заказа.
2. Сохраняет заказ в БД.
3. Отправляет email клиенту.
4. Генерирует PDF-чек.
Если бизнес попросит изменить формат чека — мы лезем в этот класс. Если поменяется логика БД - опять в него. Риск сломать отправку писем при правке базы данных огромен!
✅ Как надо:
Разбиваем на маленькие классы:
•
•
•
•
2️⃣ O - Open-Closed Principle (Открытость/Закрытость)
Это значит: Не меняйте старый рабочий код, чтобы добавить новую фичу.
⛔ Плохо:
У нас есть метод расчета доставки.
✅ Хорошо:
Используем полиморфизм.
3️⃣ L - Liskov Substitution Principle (Принцип подстановки Барбары Лисков)
Если у вас есть класс
Суть: Если код работает с базовым классом, он должен работать и с любым его наследником, не зная об этом и не ломаясь.
4️⃣ I - Interface Segregation Principle (Разделение интерфейсов)
⛔ Плохо:
Интерфейс
Мы создаем класс
Нам придется реализовать метод
✅ Хорошо:
Разбейте на
Человек имплементирует оба. Робот - только
5️⃣ D - Dependency Inversion Principle (Инверсия зависимостей)
Это то, что мы учили в Spring (DI).
Ваш
Тогда вы сможете легко подменить Postgres на MySQL или Mock-объект для тестов, не меняя ни строчки в Сервисе.
SOLID - это фильтр. Прежде чем закоммитить код, спросите себя:
1. Не делает ли мой класс слишком много? (S)
2. Придется ли мне переписывать этот класс, если добавятся новые условия? (O)
3. Не ломаю ли я поведение родителя? (L)
4. Не заставляю ли я других реализовывать ненужные методы? (I)
5. Завишу ли я от интерфейсов или от конкретных классов? (D)
#Architecture #SOLID #CleanCode #OODesign
📲 Мы в MAX
👉@BookJava
Почему один проект живет 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
👍13❤5🔥3
🏗 Структурные паттерны: Адаптер, Декоратор, Прокси
Когда вы пытаетесь соединить старую библиотеку с новым кодом или добавить логирование, не переписывая половину проекта - вы используете эти паттерны.
🔌 1. Adapter (Адаптер)
Суть: Делает несовместимые интерфейсы совместимыми.
Это как переходник для розетки. У вас вилка американская, а розетка европейская. Адаптер позволяет им работать вместе.
Где нужен: Когда есть старый класс (Legacy), который нельзя менять, но его нужно использовать в новом коде.
Пример: У нас есть система, которая понимает только
🎁 2. Decorator (Декоратор/Обертка)
Суть: Динамически добавляет объекту новые обязанности (функционал).
Это альтернатива наследованию. Вместо того чтобы создавать
Принцип: Матрешка. Каждый декоратор делает свою работу и вызывает следующий.
Пример:
Важно: В
🛡️ 3. Proxy (Заместитель)
Суть: Объект-прокладка, который контролирует доступ к другому объекту.
Клиент думает, что общается с реальным объектом, а на самом деле говорит с его заместителем.
Зачем?
1. Ленивая загрузка (Lazy Loading): Не грузить тяжелую картинку/БД, пока ее реально не попросят.
2. Безопасность: Проверить права доступа перед выполнением метода.
3. Логирование: Записать "Метод вызван" и передать вызов дальше.
Пример:
Spring Magic: Весь Spring держится на Прокси! Когда вы ставите
🔥 Итог
🔴 Adapter - меняет интерфейс объекта (чтобы подошел).
🔴 Decorator - меняет поведение объекта (добавляет фичи), не меняя интерфейс.
🔴 Proxy - контролирует доступ к объекту (ленивость, защита).
#DesignPatterns #Adapter #Decorator #Proxy #Architecture
📲 Мы в MAX
👉@BookJava
Когда вы пытаетесь соединить старую библиотеку с новым кодом или добавить логирование, не переписывая половину проекта - вы используете эти паттерны.
🔌 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 создает прокси вокруг вашего сервиса, открывает транзакцию, вызывает ваш метод, а потом закрывает транзакцию.🔥 Итог
#DesignPatterns #Adapter #Decorator #Proxy #Architecture
📲 Мы в 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) - Центр Вселенной
Здесь живет ваша Бизнес-логика.
🔴 Сущности (
🔴 Правила (
🔴 Правило: Здесь НЕТ фреймворков. Никакого Spring, никакого Hibernate, никакого SQL. Только чистая Java.
🔴 Этот слой ничего не знает о внешнем мире.
2. Порты (Ports / Use Cases) - Граница
Ядро определяет интерфейсы (Порты), которые ему нужны для работы.
🔴 Например:
🔴 Заметьте: интерфейс лежит внутри домена!
3. Адаптеры (Adapters / Infrastructure) - Внешний мир
Здесь живут детали реализации.
🔴
🔴
🔴 Здесь подключается Spring, Hibernate, Kafka и всё остальное.
🔄 Инверсия Зависимостей (DIP)
Следите за руками:
1. В слоеной архитектуре:
2. В чистой архитектуре:
Оба зависят от абстракции. БД стала просто плагином. Вы можете выкинуть 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
❌ Проблема Слоенки (Database Driven Design)
В классическом Spring-приложении зависимости идут сверху вниз:
1. Контроллер зависит от Сервиса.
2. Сервис зависит от Репозитория (Базы данных).
В чем подвох?
Ваша бизнес-логика (Сервис) намертво привязана к деталям хранения данных (БД).
✅ Решение: Clean / Hexagonal / Onion
Дядя Боб, Алистер Кокберн и другие умные дядьки придумали, как перевернуть игру.
Главная идея: Зависимости должны быть направлены ТОЛЬКО внутрь, к центру.
Представьте приложение как Луковицу.
1. Ядро (Core / Domain) - Центр Вселенной
Здесь живет ваша Бизнес-логика.
User, Order).User не может быть моложе 18 лет).2. Порты (Ports / Use Cases) - Граница
Ядро определяет интерфейсы (Порты), которые ему нужны для работы.
interface UserRepository (найти пользователя, сохранить пользователя).3. Адаптеры (Adapters / Infrastructure) - Внешний мир
Здесь живут детали реализации.
PostgresUserRepository реализует интерфейс UserRepository.RestController вызывает методы Ядра.🔄 Инверсия Зависимостей (DIP)
Следите за руками:
1. В слоеной архитектуре:
Service зависит от PostgresDao.2. В чистой архитектуре:
Service зависит от Интерфейса. А PostgresDao зависит от Интерфейса.Оба зависят от абстракции. БД стала просто плагином. Вы можете выкинуть Postgres и поставить заглушку (In-Memory Map) - и бизнес-логика даже не заметит подмены!
⚖️ Когда что использовать?
1. Layered (Controller-Service-Repo)
• ✅ Простые CRUD-приложения.
• ✅ Админки, прототипы.
• ✅ Когда логики почти нет, просто перекладываем данные.
2. Hexagonal (Ports & Adapters)
• ✅ Сложная бизнес-логика (Банкинг, Финтех, Логистика).
• ✅ Приложение живет долго (5+ лет).
• ✅ Нужно писать много Unit-тестов для ядра, не поднимая контекст Spring.
🔥 Итог
#Architecture #CleanArchitecture #Hexagonal #Spring #Java
📲 Мы в MAX
👉@BookJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
🧠 Domain Driven Design: Программируем на языке бизнеса
Главная проблема IT: Разработчики говорят на языке таблиц (
DDD (Предметно-ориентированное проектирование) - это попытка убрать переводчика.
🗣 1. Ubiquitous Language (Единый язык)
Это фундамент. Код должен звучать так же, как речь эксперта.
🔴 Плохо (CRUD-мышление):
• Бизнес: "Клиент сменил адрес доставки".
• Код:
🔴 Хорошо (DDD):
• Код:
Методы должны называться глаголами бизнеса, а не сеттерами.
📦 2. Bounded Context (Ограниченный контекст)
Самая большая ошибка новичка - создать один класс
🔴 Для Продаж: Товар - это цена, описание, картинки.
🔴 Для Склада: Товар - это вес, габариты, номер полки.
🔴 Для Бухгалтерии: Товар - это актив, амортизация, инвентарный номер.
В DDD мы не делаем монстра. Мы создаем разные модели для разных контекстов.
🔴
🔴
Эти модели могут даже иметь разные ID и общаться друг с другом только через события (Kafka).
🏗 3. Tactical DDD (Строительные блоки)
Как писать код внутри контекста?
A. Entity (Сущность)
Объект, у которого есть Identity (ID).
Если у двух людей одинаковое имя, это все равно два разных человека (разные ID).
🔴 Пример:
🔴 Они живут долго, меняют свое состояние, но остаются собой.
B. Value Object (Объект-значение)
Объект, который определяется только своими данными. У него нет ID. Он неизменяем (Immutable).
🔴 Пример:
🔴 Если у меня есть 100 рублей и у вас 100 рублей - это одни и те же 100 рублей. Нам не важно, какая именно это купюра.
🔴 Правило: Не используйте примитивы! Вместо
C. Aggregate (Агрегат)
Это кластер объектов, которые живут и умирают вместе.
🔴 Пример:
🔴 Aggregate Root (Корень): Это главный объект (
🔴 Правило: Извне можно обращаться только к Корню. Нельзя получить ссылку на
🩸 Anemic vs Rich Model (Анемичная vs Богатая модель)
❌ Анемичная (Стандартный Spring):
Класс — это просто мешок с геттерами и сеттерами. Вся логика лежит в
✅ Богатая (DDD):
Логика и валидация живут внутри Сущности. Сервис просто координирует работу.
🔥 Итог
DDD - это сложно, но необходимо для больших проектов.
1. Говорите на языке бизнеса (Ubiquitous Language).
2. Разделяйте модели (Bounded Contexts).
3. Используйте Value Objects вместо примитивов.
4. Прячьте логику внутрь Rich Domain Model.
#Architecture #DDD #DomainDrivenDesign #Java #Microservices
📲 Мы в MAX
👉@BookJava
Главная проблема IT: Разработчики говорят на языке таблиц (
INSERT, Foreign Key, DTO), а бизнес на языке денег и процессов («Провести проводку», «Списать остаток»).DDD (Предметно-ориентированное проектирование) - это попытка убрать переводчика.
🗣 1. Ubiquitous Language (Единый язык)
Это фундамент. Код должен звучать так же, как речь эксперта.
• Бизнес: "Клиент сменил адрес доставки".
• Код:
user.setAddress("New York"); userRepo.save(user);• Код:
user.relocateTo(new Address("New York"));Методы должны называться глаголами бизнеса, а не сеттерами.
📦 2. Bounded Context (Ограниченный контекст)
Самая большая ошибка новичка - создать один класс
Product на всё приложение.В DDD мы не делаем монстра. Мы создаем разные модели для разных контекстов.
SalesContext.ProductWarehouseContext.StockItemЭти модели могут даже иметь разные ID и общаться друг с другом только через события (Kafka).
🏗 3. Tactical DDD (Строительные блоки)
Как писать код внутри контекста?
A. Entity (Сущность)
Объект, у которого есть Identity (ID).
Если у двух людей одинаковое имя, это все равно два разных человека (разные ID).
User, Order.B. Value Object (Объект-значение)
Объект, который определяется только своими данными. У него нет ID. Он неизменяем (Immutable).
Money, Address, Color.String email используйте класс EmailAddress. Там можно спрятать валидацию.C. Aggregate (Агрегат)
Это кластер объектов, которые живут и умирают вместе.
Заказ (Order) + Позиции заказа (OrderItems).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
👍7❤1👎1