: The complete code examples and project repositories for the book are available for free on Core Concepts Covered
The architecture relies on a strict separation of concerns, divided into three distinct layers: the Domain, Ports, and Adapters. The Domain (The Center)
package com.example.myapp.domain; import com.example.myapp.domain.model.Order; import com.example.myapp.ports.inbound.CreateOrderUseCase; import com.example.myapp.ports.outbound.OrderRepositoryPort; import java.math.BigDecimal; import java.util.UUID; public class OrderService implements CreateOrderUseCase private final OrderRepositoryPort orderRepositoryPort; public OrderService(OrderRepositoryPort orderRepositoryPort) this.orderRepositoryPort = orderRepositoryPort; @Override public Order createOrder(BigDecimal amount) Order order = new Order(UUID.randomUUID(), amount); orderRepositoryPort.save(order); return order; Use code with caution. 4. Adapters (Outside) : The complete code examples and project repositories
You will need to map objects between layers (e.g., converting a Web Request DTO to a Domain Model, and a Domain Model to a Database JPA Entity). Do not cut corners by reusing your JPA Entity as your core Domain Model—this completely breaks the architectural isolation.
Downloading a free PDF of this book from unauthorized "warez" or file-sharing sites is illegal and violates the author's intellectual property rights. Adapters (Outside) You will need to map objects
If you want to dive deeper into this architectural paradigm, consider exploring in your test suites. ArchUnit runs automated tests against your code structure to ensure that no developer accidentally imports an infrastructure package into the core domain layer.
The service implements the driving port and interacts with the driven port. If you want to dive deeper into this
package com.example.myapp.domain.model; import java.math.BigDecimal; import java.util.UUID; public class Order private final UUID id; private final BigDecimal totalAmount; private boolean isPaid; public Order(UUID id, BigDecimal totalAmount) this.id = id; this.totalAmount = totalAmount; this.isPaid = false; public void markAsPaid() if (totalAmount.compareTo(BigDecimal.ZERO) <= 0) throw new IllegalStateException("Cannot pay for an order with zero amount"); this.isPaid = true; // Getters public UUID getId() return id; public BigDecimal getTotalAmount() return totalAmount; public boolean isPaid() return isPaid; Use code with caution. 2. The Ports (The Bridge)
If your codebase looks like a framework with polite domain classes tucked inside, flip it: start with business rules and ask every dependency, “Is this a port or an adapter?” That discipline changes how teams reason about change, scaling, and long-term maintenance.
For Java developers, this architectural style is not just an academic exercise; it brings tangible benefits to real-world projects:
Switching from PostgreSQL to MongoDB or changing the UI from REST to GraphQL is possible without touching the domain logic.