An API is the public communication interface through which one module, application, or external client uses another’s functionality. It can be ordinary classes and method calls inside a modular monolith or a web interface with request and response formats; it is not limited to HTTP. Its purpose is to let clients ask for outcomes without learning the implementation behind them. section 7.2
Treat an API as a boundary for controlling complexity over time. A strong boundary describes what a capability does in domain language, hides infrastructure and internal decisions, and keeps high-level business flows independent of low-level code. Clients can then change the part they need without understanding every implementation detail. section 4.1.1 section 4.1.4 section 6.1.1
Design a public API to be:
- Simple: require little client knowledge, setup, or ordered call sequences; keep complex business and technical work inside the module. section 7.2.1
- Stable: avoid exposing implementation details or changing promises casually, since clients depend on public contracts. section 4.1.1 section 7.2.1
- Backward-compatible when independently deployed clients cannot all update together; accept older request forms when practical and warn before unavoidable breaks. section 7.2.2
- Flexible through deliberate extension points when recurring client variations would otherwise force the owning team to change the module repeatedly. section 7.2.3
Hide database access behind a domain API
An EmployeeRepository can expose findById, findByLastName, and save without revealing Hibernate, Postgres, queries, or caching. A cache can later be added inside HibernateEmployeeRepository, leaving callers unchanged. This reduces the number of places affected by an infrastructure change, but the contract must also guard against indirect ORM behavior leaking through returned entities. section 6.1.4 section 6.3.2
A notification web API
A notification service can expose operations to create a notification, add participants, and remove participants. Its contract accepts a message, supported media, and dispatch times while keeping channel delivery and templates behind the service boundary. Small, specific operations can be simple while still supporting several notification channels. section 7.3.6 section 7.2.1
Listing 7.1 Notification API
interface NotificationAPI { #1
Notification createNotification(
String message,
List<Medium> supportedMedium,
List<DispatchTime> times);
void addParticipant(int notificationId, String participantEmail);
void removeParticipant(int notificationId, String participantEmail);
}
enum Medium { #2
EMAIL,
CHAT,
WHATSAPP;
}
enum DispatchTime { #3
RIGHT_NOW,
ONE_WEEK_BEFORE,
DAY_BEFORE,
ONE_HOUR_BEFORE;
}
Inspect the compact NotificationAPI listing to see a public contract expressed as three focused operations and constrained option types. section 7.3.6
Do not add an interface merely for elegance. Straightforward features may mix high-level description and implementation, with private methods preserving readability. Keep infrastructure and business code separate, however, and introduce an interface when multiple infrastructure implementations are expected, details are uncertain, or a fake is useful for testing. Never let a public API become an excuse for clients to depend on internal classes. section 4.1.3 section 6.1.1 section 7.3.2











































