Какие абстракции предполагает гексагональная архитектура?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Абстракции гексагональной архитектуры (Ports & Adapters)
Гексагональная архитектура, также известная как Ports & Adapters, предлагает строгий набор абстракций для отделения бизнес-логики приложения от внешних зависимостей (UI, базы данных, сторонние API). В центре внимания — ядро приложения (Domain Layer), которое остается чистым и независимым.
Ключевые абстракции
1. Доменный слой (Core)
Это ядро системы, содержащее бизнес-логику, сущности, правила и процессы. Он не должен знать о внешнем мире — никаких прямых ссылок на HTTP, базы данных или файловые системы.
// Пример сущности (Entity) в доменном слое
class Order {
private id: string;
private items: OrderItem[];
private status: OrderStatus;
constructor(id: string, items: OrderItem[]) {
this.id = id;
this.items = items;
this.status = OrderStatus.PENDING;
}
// Бизнес-метод
public calculateTotal(): number {
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
// Доменное правило
public canBeCancelled(): boolean {
return this.status === OrderStatus.PENDING;
}
}
2. Порт (Port)
Порт — это абстракция, представляющая контракт или интерфейс, через который ядро взаимодействует с внешним миром. Это "окно" из ядра наружу.
- Входящие порты (Input Ports) — определяют, как внешние компоненты (например, UI) могут взаимодействовать с ядром. Это интерфейсы для сервисов приложения.
- Исходящие порты (Output Ports) — определяют, как ядро может взаимодействовать с внешними системами (например, для сохранения данных). Это интерфейсы для репозиториев, клиентов API.
// Пример исходящего порта (интерфейс репозитория)
interface OrderRepository {
save(order: Order): Promise<void>;
findById(id: string): Promise<Order | null>;
findAll(): Promise<Order[]>;
}
// Пример входящего порта (интерфейс сервиса приложения)
interface OrderService {
createOrder(items: OrderItem[]): Promise<Order>;
cancelOrder(orderId: string): Promise<void>;
getOrderTotal(orderId: string): Promise<number>;
}
3. Адаптер (Adapter)
Адаптер — это конкретная реализация порта, которая соединяет ядро с конкретной внешней технологией или системой. Адаптер знает о деталях внешнего мира, но ядро его не знает.
- Входящие адаптеры (Input Adapters) — вызывают входящие порты. Например: контроллер REST API, компонент React/Vue, обработчик CLI команды.
- Исходящие адаптеры (Output Adapters) — реализуют исходящие порты. Например: репозиторий для MongoDB, клиент для внешнего платежного API, сервис для отправки email.
// Пример исходящего адаптера (реализация репозитория для MongoDB)
class MongoOrderRepository implements OrderRepository {
private collection: Collection;
constructor(db: Db) {
this.collection = db.collection('orders');
}
async save(order: Order): Promise<void> {
const doc = { _id: order.id, ...order.toJSON() };
await this.collection.insertOne(doc);
}
async findById(id: string): Promise<Order | null> {
const doc = await this.collection.findOne({ _id: id });
return doc ? Order.fromJSON(doc) : null;
}
}
// Пример входящего адаптера (контроллер Express.js)
class OrderController {
private orderService: OrderService;
constructor(orderService: OrderService) {
this.orderService = orderService;
}
async handleCreateOrder(req: Request, res: Response): Promise<void> {
const items = req.body.items;
const order = await this.orderService.createOrder(items);
res.status(201).json(order);
}
}
Как абстракции работают вместе
- Ядро определяет бизнес**-интерфейсы (Порты)**.
- Внешний мир предоставляет конкретные Адаптеры, реализующие эти интерфейсы.
- Инъекция зависимостей (DI) связывает их: адаптер внедряется в ядро через его порт, но ядро не знает о конкретной реализации.
Преимущества такого разделения абстракций
- Тестируемость: Ядро можно полностью протестировать в изоляции с использованием mock-адаптеров.
- Свобода замены технологий: Чтобы перейти от MongoDB к PostgreSQL, нужно лишь создать новый адаптер
PostgresOrderRepository, реализующий тот жеOrderRepository. Ядро не меняется. - Защита от "распространения знаний": Детали базы данных или HTTP не проникают в бизнес-логику.
- Фокусировка на бизнес-правилах: Разработчики ядра могут работать, не погружаясь в детали инфраструктуры.
В итоге, гексагональная архитектура предлагает мощные абстракции — Порты и Адаптеры — которые создают четкую, поддерживаемую границу между чистым доменом и изменчивым внешним окружением, делая приложение устойчивым к изменениям технологического стека.