Input ports
Use-case entry points such as commands, services, or application APIs the outside world can invoke.
Ports and Adapters, often called Hexagonal Architecture, is a way to keep the application's core logic independent from the delivery and infrastructure mechanisms around it. The core speaks through ports. Adapters implement those ports for web, database, messaging, or external APIs.
Commerce systems often change payment providers, shipping APIs, persistence strategies, or transport layers over time. Hexagonal architecture helps prevent those changes from reshaping the business core every time an infrastructure choice moves.
In a hexagonal design, the application core depends on business-facing interfaces, often called ports. Adapters live at the edges and implement those ports for real infrastructure. The key benefit is dependency direction: the core does not import frameworks or transport details directly.
This style is especially helpful when teams care about testability, infrastructure independence, and long-lived business logic. It is less useful when the system is small and the extra abstraction would be mostly ceremony.
Use-case entry points such as commands, services, or application APIs the outside world can invoke.
Business-facing contracts for persistence, payments, notifications, or external integrations.
Concrete implementations for HTTP, database, messaging, or third-party APIs.
OrderStore, PaymentPort, and NotificationPort. Around those sit adapters like a REST controller, a Postgres repository, a Stripe adapter, and an email adapter. The core points outward only through interfaces.
package org.javaomnibus.ecommerce.application;
public interface PaymentPort {
PaymentReceipt capture(String orderId, Money amount);
}
public interface OrderStore {
void save(Order order);
}
public final class PlaceOrderUseCase {
private final PaymentPort paymentPort;
private final OrderStore orderStore;
public PlaceOrderUseCase(PaymentPort paymentPort, OrderStore orderStore) {
this.paymentPort = paymentPort;
this.orderStore = orderStore;
}
public OrderReceipt place(PlaceOrderCommand command) {
Order order = Order.create(command.customerId(), command.lines());
paymentPort.capture(order.id(), order.total());
orderStore.save(order);
return OrderReceipt.from(order);
}
}