Один из недооценённых скиллов в разработке — это умение провести правильную границу между сервисами. Большинство «распределённых монолитов», которые мы встречаем в продакшене, рождаются именно здесь.
Разобрали тему — держи шпаргалку 👇
🔹 Что такое Service Boundary
Это контракт на три вещи:
— за что сервис отвечает;
— какими данными он владеет;
— как он общается с соседями.
Представь компанию: HR не лезет в финансы, а склад не занимается зарплатами. Так же должны работать твои сервисы.
🔹 4 принципа, которые работают
1. Business Capability First
Режь по бизнес-функциям, а не по техническим слоям. OrderService, PaymentService, UserService, потому что именно так бизнес думает о системе.
2. Single Responsibility
Один сервис — одна ответственность. Если в описании сервиса есть союз и, это уже тревожный звоночек 🚨
3. Data Ownership
У каждого сервиса своя БД. Без исключений. Shared database = shared pain.
4. Loose Coupling
Только API, никакого прямого доступа к чужим таблицам.
🔹 Классический антипаттерн
// ❌ Бизнес-логика смешана в одном сервисе
@Service
public class BadOrderService {
public String processOrderAndPayment(int id) {
String order = orderRepository.findById(id)
.orElse("Order not found");
String paymentStatus = "Payment Successful"; // 🚨 чужая ответственность
return order + " | " + paymentStatus;
}
}
Выглядит безобидно, пока не нужно масштабировать платежи отдельно, сменить платёжного провайдера или добавить retry-логику только для оплаты. Тут и начнутся реальные проблемы.
🔹 Советы из практики
→ Начни с монолита. Не дроби систему заранее. Сначала пойми домен, потом режь по швам.
→ Следи за chatty communication. Если сервис делает 10 вызовов к соседям на каждый запрос, граница явно проведена не там.
→ Изучи DDD. Bounded Context из Domain-Driven Design это лучший инструмент для поиска правильных границ. Инвестиция окупается быстро
══════ Навигация ══════
Вакансии • Задачи • Собесы
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍6🔥1👏1
С Java 21 virtual threads стали stable (JEP 444). Вместо тяжёлых OS-тредов — легковесные потоки, которыми управляет JVM. Код остаётся синхронным и читаемым, а рантайм под капотом делает M:N scheduling: когда виртуальный тред блокируется на I/O, он паркуется и отпускает carrier thread другому.
На практике это значит одно: Executors.newFixedThreadPool(200) с танцами вокруг размера пула заменяется на Executors.newVirtualThreadPerTaskExecutor(). Блокирующий вызов больше не сжигает OS-тред, пока ждёт ответа от базы.
🔹 Где работает
— веб-сервисы с большим числом конкурентных запросов;
— I/O-heavy бэкенды (база, сеть, файлы);
— замена реактивщины там, где она была вынужденной, а не осознанной.
🔹 Где не работает и не должно
— connection pool базы всё ещё bottleneck.
— CPU-bound задачи не получают выигрыша, т.к. carrier threads и так заняты вычислениями.
— synchronized блоки пинят виртуальный тред к carrier thread (починили в JDK 24). Если у вас hot path с synchronized, переходите на ReentrantLock.
— Внешние API с rate limit'ами никуда не денутся.
══════ Навигация ══════
Вакансии • Задачи • Собесы
#CoreJava
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤4🔥2