Infrastructure is every external system or technical resource beyond an application's boundary: databases, web services, third-party APIs, file systems, mail servers, and similar dependencies. Infrastructure code is the glue that communicates with them; domain code contains business logic. Keep those responsibilities separate so external details do not spread through the design. section 1.2.5 section 1.4
The central rule is simple: do not mix business rules with SQL, HTTP calls, file access, or library-specific data structures. Put communication in thin, dedicated classes with no business logic, and expose domain-focused operations to the rest of the application. This localizes implementation changes and makes domain behavior easier to test with fakes or mocks. section 4.1.3 section 6.1
Repository boundary with localized caching
An EmployeeRepository can describe required operations such as finding or saving employees without exposing the database or persistence library. A Hibernate-backed implementation contains Postgres, Hibernate, and cache handling. Adding a cache to findByLastName then changes that implementation rather than every caller. The boundary protects callers from incidental mechanics, while still allowing the implementation to use a cache and database query efficiently. section 6.1.4
Listing 6.1 The EmployeeRepository interface
interface EmployeeRepository { #1
Employee findById(int id);
Set<Employee> findByLastName(String lastName);
void save(Employee employee);
// ...
}
The repository interface illustrates a domain-facing contract: callers request employee data without depending on a database or persistence framework. section 6.1.4
Separation is not a demand for total technology independence. Hide details from most code so upgrades, read replicas, authentication changes, or storage migrations affect a small area. But keep developers aware of meaningful constraints: a relational database may guide efficient design, and a remote call requires distributed-failure thinking. Abstracting every difference can be expensive and unnecessary. section 6.1.2 section 6.1.3
Choose the boundary pragmatically:
- Wrap third-party libraries and external data structures when letting them leak would couple many domain classes to unstable or low-level details. section 6.0 section 6.6
- Use valuable database, framework, or service capabilities when they improve correctness or efficiency; a perfectly generic abstraction can make the system slower, more complex, or more error-prone. section 1.2.5 section 6.2
- Refactor toward a stronger boundary as complexity grows, but do not add layers merely because a straightforward feature has a simple implementation. section 4.1.3
Infrastructure changes are often incremental rather than wholesale: caching selected queries, introducing a read replica, changing internal authentication, or moving large files to S3. Encapsulation makes these evolutions cheaper because the affected integration code has a defined home. The practical aim is controlled coupling, not eliminating coupling altogether. section 6.1.3 section 6.6







