Архитектурные границы — это четко определенные разделы в приложении, которые инкапсулируют конкретную функциональность и минимизируют взаимозависимости между компонентами. В Node.js они особенно важны для поддержания масштабируемости и поддерживаемости кода.
// Типичная структура проекта
src/
├── presentation/ // Граница взаимодействия с внешним миром
├── application/ // Бизнес-логика (use cases)
├── domain/ // Ядро системы (сущности, value objects)
└── infrastructure/ // Граница работы с внешними сервисами и БД
Преимущества:
// Пример организации по принципу зависимостей
src/
├── interfaces/ // Адаптеры для внешнего мира
├── useCases/ // Бизнес-правила
├── domain/ // Сущности и интерфейсы репозиториев
└── infrastructure/ // Реализации репозиториев
Принцип: Зависимости направлены только внутрь, к ядру системы.
// Организация в виде независимых сервисов
services/
├── auth-service/ // Авторизация
├── order-service/ // Работа с заказами
└── payment-service/ // Платежи
Особенности:
// Явное определение границ через модули
// domain/User.js
class User {
constructor(id, name) {
this.id = id;
this.name = name;
}
}
module.exports = User;
// Четкие границы через внедрение зависимостей
class OrderService {
constructor(paymentGateway, notificationService) {
this.paymentGateway = paymentGateway;
this.notificationService = notificationService;
}
}
// Определение границы через абстракцию
// interfaces/IRepository.js
class IRepository {
save(entity) {
throw new Error('Not implemented');
}
}
// infrastructure/UserRepository.js
class UserRepository extends IRepository {
save(user) {
// Реализация для конкретной БД
}
}
// Границы через события
eventBus.on('ORDER_CREATED', async (order) => {
await inventoryService.reserveItems(order);
await paymentService.processPayment(order);
});
// domain/User.js
class User {
// Только бизнес-логика
}
// infrastructure/UserRepository.js
class UserRepository {
async save(user) {
// Преобразование User в DTO для БД
const userData = convertToPersistence(user);
await db.collection('users').insertOne(userData);
}
}
// presentation/controllers/UserController.js
class UserController {
constructor(userService) {
this.userService = userService;
}
async createUser(req, res) {
// Валидация входящих данных
const userDto = req.body;
const user = await this.userService.createUser(userDto);
// Преобразование в DTO для ответа
res.json(toUserResponseDto(user));
}
}
// Плохо: доменный слой зависит от инфраструктуры
const db = require('../infrastructure/db');
class User {
save() {
return db.save(this);
}
}
// Плохо: контроллер содержит бизнес-логику
app.post('/orders', async (req, res) => {
const order = req.body;
// Бизнес-правило в контроллере
if (order.total > 1000) {
order.discount = 0.1;
}
await Order.save(order);
});
// Плохо: прямой вызов между сервисами
class OrderService {
async createOrder() {
const user = await userService.getUser();
// ...
}
}
реализация четких архитектурных границ в Node.js требует дисциплины и правильного выбора паттернов. Используйте слоистую организацию, инверсию зависимостей и явные интерфейсы для создания поддерживаемых и масштабируемых приложений. Границы должны отражать бизнес-домены и минимизировать технические зависимости между компонентами.