Что такое бизнес-логика?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Бизнес-логика
Это один из самых важных и часто неправильно понимаемых терминов в разработке. Правильное разделение бизнес-логики от технической реализации критично для качественной архитектуры.
Определение
Бизнес-логика - это правила и процессы, которые реализуют требования бизнеса. Это СУТЬ приложения, то, чего бизнес хочет достичь.
Технические детали - это ВЫ реализуете бизнес-логику (язык программирования, фреймворк, БД и т.д.)
Примеры из реальной жизни
Пример 1: Система скидок в интернет-магазине
Бизнес-логика:
- Покупатели с суммой заказа более 1000 рублей получают скидку 10%
- Постоянные клиенты (5+ покупок) получают дополнительно 5% скидку
- Скидки не суммируются с промокодами
- VIP клиенты получают скидку максимум 30%, даже если условия дают больше
// Бизнес-логика (независима от технологии)
function calculateDiscount(customer: Customer, orderAmount: number): number {
let discount = 0;
// Правило 1: скидка за сумму заказа
if (orderAmount > 1000) {
discount += 10;
}
// Правило 2: скидка за постоянных клиентов
if (customer.purchaseCount >= 5) {
discount += 5;
}
// Правило 3: максимум для VIP
if (customer.isVIP && discount > 30) {
discount = 30;
}
return discount;
}
// Техническая реализация (может изменяться)
function applyDiscount(price: number, discountPercent: number): number {
return price * (1 - discountPercent / 100);
}
// Использование в контроллере
router.post('/checkout', async (req, res) => {
const customer = await getCustomer(req.user.id);
const discount = calculateDiscount(customer, req.body.orderAmount);
const finalPrice = applyDiscount(req.body.orderAmount, discount);
// ...
});
Пример 2: Система заказов такси
Бизнес-логика:
- Водитель может принять заказ только если он online
- Заказ распределяется среди водителей в порядке:
- Ближайший водитель (в пределах 5км)
- Если нет - расширяем до 10км
- Если нет - отмена с возвратом денег клиенту
- Цена зависит от расстояния, времени суток и спроса
- Рейтинг водителя влияет на распределение (лучше рейтинг - выше приоритет)
// Бизнес-логика: алгоритм распределения заказа
function findDriverForOrder(order: Order, drivers: Driver[]): Driver | null {
// Фильтруем online водителей с рейтингом >= 4.0
const availableDrivers = drivers.filter(
d => d.isOnline && d.rating >= 4.0
);
// Ищем в радиусе 5км
let candidates = availableDrivers.filter(
d => getDistance(d.location, order.pickup) <= 5
);
if (candidates.length === 0) {
// Ищем в радиусе 10км
candidates = availableDrivers.filter(
d => getDistance(d.location, order.pickup) <= 10
);
}
if (candidates.length === 0) {
return null; // Нет водителей - отмена заказа
}
// Сортируем: сначала по рейтингу, потом по расстоянию
candidates.sort((a, b) => {
if (b.rating !== a.rating) return b.rating - a.rating;
const distA = getDistance(a.location, order.pickup);
const distB = getDistance(b.location, order.pickup);
return distA - distB;
});
return candidates[0]; // Выбираем лучшего
}
// Бизнес-логика: расчет стоимости
function calculatePrice(
distance: number,
timeOfDay: 'peak' | 'normal',
demandMultiplier: number
): number {
const BASE_RATE = 50; // рубли
const PER_KM = 10; // рубли за км
const PEAK_MULTIPLIER = 1.5;
let price = BASE_RATE + distance * PER_KM;
if (timeOfDay === 'peak') {
price *= PEAK_MULTIPLIER;
}
price *= demandMultiplier;
return Math.round(price);
}
// Техническая реализация: получение координат через API
function getDistance(from: Coordinates, to: Coordinates): number {
// Вызываем Google Maps API
return calculateHaversineDistance(from, to);
}
// REST API endpoint
router.post('/orders', async (req, res) => {
const order = createOrder(req.body);
const driver = findDriverForOrder(order, activeDrivers);
if (!driver) {
refundCustomer(order.customerId, order.amount);
return res.status(404).json({ error: 'No drivers available' });
}
assignOrder(order, driver);
notifyDriver(driver, order);
});
Слои архитектуры и расположение бизнес-логики
Domain Layer (Слой предметной области)
Это чистая бизнес-логика БЕЗ зависимостей от фреймворков.
// domain/services/OrderService.ts
export class OrderService {
// Чистая бизнес-логика, никаких импортов Express, React и т.д.
calculateTotal(
items: OrderItem[],
discountPercent: number,
taxRate: number
): number {
const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
const withDiscount = subtotal * (1 - discountPercent / 100);
const total = withDiscount * (1 + taxRate / 100);
return Math.round(total * 100) / 100;
}
canApplyPromoCode(customer: Customer, code: string): boolean {
if (customer.isBlocked) return false;
if (code.startsWith('VIP_') && !customer.isVIP) return false;
return true;
}
getNextDeliveryDate(currentDate: Date, deliveryDays: number): Date {
const next = new Date(currentDate);
// Пропускаем выходные
let count = 0;
while (count < deliveryDays) {
next.setDate(next.getDate() + 1);
const day = next.getDay();
if (day !== 0 && day !== 6) { // Не суббота и не воскресенье
count++;
}
}
return next;
}
}
Application Layer (Слой приложения)
Службы приложения, которые координируют бизнес-логику с технической реализацией.
// application/usecases/CreateOrderUseCase.ts
export class CreateOrderUseCase {
constructor(
private orderService: OrderService,
private orderRepository: OrderRepository,
private paymentGateway: PaymentGateway,
private notificationService: NotificationService
) {}
async execute(command: CreateOrderCommand): Promise<Order> {
// Validate
if (!command.items || command.items.length === 0) {
throw new ValidationError('Cart is empty');
}
// Применяем бизнес-логику (расчет стоимости)
const total = this.orderService.calculateTotal(
command.items,
command.discountPercent,
TAX_RATE
);
// Сохраняем в БД
const order = await this.orderRepository.create({
customerId: command.customerId,
items: command.items,
total,
status: 'pending'
});
// Обрабатываем платеж
try {
const payment = await this.paymentGateway.charge(total);
order.paymentId = payment.id;
order.status = 'paid';
await this.orderRepository.update(order);
} catch (error) {
order.status = 'failed';
await this.orderRepository.update(order);
throw error;
}
// Уведомляем клиента
await this.notificationService.sendOrderConfirmation(order);
return order;
}
}
Presentation Layer (Слой представления)
Управление UI, получение ввода пользователя, вызов use cases.
// presentation/pages/CheckoutPage.tsx
export function CheckoutPage() {
const [items, setItems] = useState<OrderItem[]>([]);
const [discount, setDiscount] = useState(0);
const createOrderUseCase = useCreateOrderUseCase();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
// Вызываем use case (бизнес-логика)
const order = await createOrderUseCase.execute({
items,
discountPercent: discount,
customerId: getCurrentUserId()
});
// Обновляем UI после успеха
showSuccess(`Order #${order.id} created`);
navigate(`/orders/${order.id}`);
} catch (error) {
showError(error.message);
}
};
return (
<form onSubmit={handleSubmit}>
<CartItems items={items} onChange={setItems} />
<DiscountInput value={discount} onChange={setDiscount} />
<button type="submit">Checkout</button>
</form>
);
}
Признаки, что бизнес-логика в неправильном месте
Плохо: бизнес-логика в компоненте
// Компонент смешивает UI и бизнес-логику - ПЛОХО
function ShoppingCart({ items }) {
const [total, setTotal] = useState(0);
useEffect(() => {
// Это бизнес-логика, не место здесь!
let sum = 0;
items.forEach(item => {
let discount = 0;
if (item.quantity > 10) discount = 0.1;
if (item.category === 'electronics') discount = 0.05;
const price = item.basePrice * (1 - discount);
const tax = price * 0.18;
sum += (price + tax) * item.quantity;
});
setTotal(sum);
}, [items]);
return <div>Total: {total}</div>;
}
Хорошо: бизнес-логика отделена
// business/services/CartService.ts
export class CartService {
calculateTotal(items: CartItem[]): number {
return items.reduce((sum, item) => {
const discount = this.getDiscount(item);
const price = item.basePrice * (1 - discount);
const tax = price * TAX_RATE;
return sum + (price + tax) * item.quantity;
}, 0);
}
private getDiscount(item: CartItem): number {
if (item.quantity > 10) return 0.1;
if (item.category === 'electronics') return 0.05;
return 0;
}
}
// presentation/components/ShoppingCart.tsx
function ShoppingCart({ items }) {
const cartService = useCartService();
const total = useMemo(
() => cartService.calculateTotal(items),
[items, cartService]
);
return <div>Total: {total}</div>;
}
Преимущества правильного разделения
1. Переиспользуемость
// OrderService можно использовать везде:
- REST API (Express/FastAPI)
- GraphQL API
- WebSocket handlers
- Очереди сообщений (RabbitMQ/Kafka)
- CLI команды
- Batch процессы
2. Тестируемость
// Тестируем бизнес-логику БЕЗ браузера, БЕЗ сервера
test('calculateTotal should apply discounts', () => {
const service = new OrderService();
const items = [{ price: 100, qty: 15, category: 'electronics' }];
const total = service.calculateTotal(items, 10, 18); // discount, tax
expect(total).toBe(expectedValue);
});
3. Понятность
// Код читается как описание бизнес-требований
function qualifyForLoan(applicant: Applicant): boolean {
// Заёмщик должен быть старше 21
if (applicant.age < 21) return false;
// Зарплата должна быть минимум в 5 раз выше платежа
if (applicant.salary < monthlyPayment * 5) return false;
// Кредитная история должна быть хорошей
if (applicant.creditScore < 700) return false;
return true;
}
4. Независимость от технологий
// Если нужно заменить Express на Fastify - бизнес-логика не изменится
// Если нужно заменить PostgreSQL на MongoDB - бизнес-логика не изменится
// Если нужно добавить GraphQL - бизнес-логика не изменится
Заключение
Бизнес-логика - это СУТЬ приложения, то ради чего вы его пишете. Она должна быть:
- Отделена от технических деталей
- Независима от фреймворков и БД
- Тестируема без браузера и сервера
- Понятна даже тому, кто не знает код
- Переиспользуема в разных контекстах
Правильное разделение бизнес-логики и технической реализации - это признак профессиональной архитектуры и основа масштабируемых приложений.