In Hexagonal Architecture (Ports & Adapters), what is a 'port', and what distinguishes a driving (primary) port from a driven (secondary) port?
answer
- driving = called BY adapter, INTO core
- driven = called BY core, OUT to adapter
- port = interface owned by core
- Cockburn 2005
- left side vs right side
basics
~20 sA port is an interface the core app defines. Driving ports are ways the outside world calls INTO the app (like an API). Driven ports are ways the app calls OUT to things it needs (like a database).
solid answer
~30 sA port is an interface owned by the application core that defines a contract, independent of any technology. Driving (primary) ports are entry points — interfaces the core exposes so outside actors (UI, REST controller, CLI, message consumer) can invoke its use cases. Driven (secondary) ports are exit points — interfaces the core defines for capabilities it needs from the outside world (persistence, email, payment gateway), which the core calls but does not implement. The key distinction is direction: driving ports are called BY adapters, driven ports are called BY the core and implemented BY adapters.
go deeper
Should know a port is an interface and roughly which direction driving vs driven point, even if hazy on which side 'owns' the interface.
Should correctly state that both port types are defined by the core, and give one clear own example of each without prompting.
Should be able to design a well-shaped port from a use case, spot a leaky port in review, and justify when the split is/isn't worth it.
Should reason about port granularity and boundary placement across a whole system, and set conventions that prevent port explosion or leakage across teams.
## What a port is Hexagonal Architecture, introduced by **Alistair Cockburn** in 2005, organizes an application around a central "application core" — the business logic and use cases — that is completely isolated from the technical details of how it's invoked or what it depends on. The mechanism for that isolation is the **port**: an interface, defined and owned by the core, that describes a capability in terms the business understands, with zero mention of any specific technology (no SQL, no HTTP, no message broker library). ## The two directions Ports split into two kinds based on the direction of the call. - **A driving port** (also called a primary or "left-side" port) is a contract the core exposes outward so something in the outside world can trigger it — think `PlaceOrderUseCase` with a method `placeOrder(command): OrderResult`. Adapters like a REST controller, a CLI command handler, or a Kafka consumer sit on the "driving" side; they translate an external request format into a call against this port, and the core's implementation of that port runs the actual business logic. - **A driven port** (secondary, "right-side" port) runs in the opposite direction: it's a contract the core defines because it needs something FROM the outside world — for example, an `OrderRepository` interface with `save(order)` and `findById(id)`, or a `PaymentGateway` interface with `charge(amount, method)`. The core calls methods on this interface, but the core does not implement it; a driven adapter — a JPA repository implementation, a Stripe API client — does, and is plugged in at the boundary. ## Why this exists The problem being solved is that in naive designs, business logic ends up directly calling framework and infrastructure code (a service class instantiating a `JdbcTemplate`, or calling `HttpServletRequest` directly), which means changing the database vendor, the web framework, or adding a second delivery mechanism forces changes deep inside the business logic. By making the core define its own contracts and never import a concrete infrastructure class, hexagonal architecture forces all dependency arrows to point **INTO** the core, regardless of whether the interaction is "driving" (core is called) or "driven" (core does the calling). This is what Cockburn meant by treating all technology adjacencies symmetrically — a UI and a database are both "just adapters" from the core's point of view, even though a UI drives the app and a database is driven by it. ## The trade-off The trade-off is **indirection and volume**: every capability the core needs gets an interface plus at least one concrete adapter, and every entry point gets an interface plus at least one adapter that calls it. For a small CRUD service with one UI and one database, this can roughly double the number of types for no immediate payoff — you're paying an abstraction tax before you have a second implementation to justify it. The benefit shows up when you actually need multiple adapters on either side, e.g.: - exposing the same `PlaceOrder` use case via both a REST API and an internal gRPC service — two driving adapters sharing one driving port and one core implementation; - swapping a real payment gateway for a test double or a different vendor — two driven adapters implementing one driven port — without touching business logic. ## Failure modes Failure modes show up when the port/adapter boundary is drawn in name only. 1. **A common one is a port interface that leaks adapter-specific concerns** — an `OrderRepository` port with a method like `findByJpqlQuery(String)` or `save()` returning a JPA entity — which means the "port" is really just the adapter's own interface relabeled, and swapping persistence technology still requires touching the core. 2. **Another is direction confusion**: teams sometimes have the core depend on a driving port's adapter (e.g., the core reaching back into a web-layer DTO), which reintroduces the exact coupling ports exist to prevent. 3. **A third is port explosion**: defining a separate driven port per external call site rather than per cohesive capability, which turns the core into an interface-heavy maze that's harder to navigate than the framework code it replaced. ## Putting it together A concrete illustration: an e-commerce checkout use case exposed both through a synchronous REST API and an asynchronous "abandoned cart" background job — both are driving adapters calling the same driving port (`CheckoutUseCase`). That use case, in turn, depends on driven ports for: - `InventoryPort` (checking stock) - `PaymentPort` (charging a card) - `OrderRepositoryPort` (persisting the order) In production these are backed by a real inventory microservice client, a Stripe adapter, and a Postgres repository, while in tests they're backed by in-memory fakes, letting the entire checkout business rule set be tested with no HTTP server, no real database, and no network call to Stripe — which is the testability payoff hexagonal design is chiefly valued for.
- If a driven port interface exposes a method like `findByRawSql(String query)`, what's wrong with it?It leaks a persistence-technology concern (SQL) into a contract that's supposed to be technology-agnostic, meaning the core's business logic is now implicitly coupled to a relational database and raw query strings. Swapping to a document store or an in-memory test double becomes awkward or impossible without redesigning the port. A well-designed port instead exposes intent, e.g., findActiveOrdersForCustomer(customerId).
- Can the same driving port have multiple adapters at once, and why would you do that?Yes — e.g., a REST controller and a gRPC handler can both call the same PlaceOrderUseCase port, translating their own request formats into the port's method call. You'd do this to expose one piece of business logic through multiple delivery mechanisms without duplicating or forking the logic itself.
- Where does the port interface physically live in code — core module or adapter module?In the application core (or a shared 'domain'/'application' package the core owns), never in the adapter. The core defines the contract because it's the one with the requirement; adapters depend on the core's port definition, not the other way around, which is what keeps the dependency arrow pointing inward.
Think of the core as a power outlet on a wall (the 'hexagon'). Driving ports are like the outlet's socket shape — anything plugged in (a lamp, a phone charger) can trigger power to flow. Driven ports are like a cord the outlet itself plugs into to draw electricity from the grid — the outlet defines what kind of plug it needs, but doesn't care whether the grid comes from a coal plant or a solar panel.
saying these in an interview costs you the question
- Says 'port' and 'adapter' are interchangeable terms with no directional distinction
- Puts persistence-specific types (e.g. JPA entities, SQL) directly in a driven port signature
- Thinks a driving port is something the core calls outward
- Can't name a concrete example of a driving vs a driven port
- Believes you need exactly one adapter per port for the pattern to be worthwhile