A port is an interface that defines a contract at the boundary between a domain model and an external system. In hexagonal architecture, ports keep business logic independent of implementation details such as databases, APIs, and user interfaces. Primary ports expose application use cases to outside callers; secondary ports state what the application requires from repositories or external services. section 3.3
Use a port when a controller or other external entry point would otherwise call application logic directly. The controller depends on the port; an adapter implements that port and translates the interaction to the application component. This extra boundary makes it possible to replace the adapter—for example, to support a command-line application instead of a REST API—without changing the core interaction contract. section 4.3.2
A practical port-and-adapter flow has three parts:
- Define an interface containing the operations the caller needs, rather than exposing a concrete manager directly. section 4.3.2
- Implement the interface in an adapter that wraps the application component and delegates each operation. section 4.3.2
- Update the controller to invoke the adapter, removing its direct references to the application manager. section 4.3.2
REST port for asset operations
An AssetRestPort declares read, create, update, delete, and get_assets operations for Asset objects. AssetRestAdapter implements that interface, receives an AssetManager in its constructor, and forwards each call to it. A FastAPI controller can then call the adapter rather than AssetManager directly, making the REST-facing boundary explicit. section 4.3.2
Listing 4.23 Complete source code for AssetRestPort
from itam.domain.asset import Asset
from abc import abstractmethod
class AssetRestPort:
@abstractmethod
def read(self, asset_id: int) -> Asset:
pass
@abstractmethod
def create(self, asset: Asset) -> None:
pass
@abstractmethod
def update(self, asset: Asset) -> None:
pass
@abstractmethod
def delete(self, asset_id: int) -> None:
pass
@abstractmethod
def get_assets(self):
pass
Inspect the abstract AssetRestPort contract before comparing it with its delegating adapter implementation. section 4.3.2
Ports add an abstraction layer, so apply them deliberately. Their value is separating the driving REST API from the driven business model and preserving the option to substitute external-facing adapters. Review generated interfaces and adapters to confirm that the contract exposes the intended operations and that delegation remains correct. section 4.3.2


