How do you compose multiple fragment interfaces into one repository, and why would you split behavior across several fragments?
answer
- Many fragments per repository (Spring Data 2.0+)
- One Impl per fragment
- Declaration order = precedence
- Fragments reusable across repositories
- Cohesion + independent unit testing
basics
~20 sA repository interface can extend several fragment interfaces at once. Each has its own Impl class. You split behavior so unrelated custom logic (e.g., search vs. reporting) stays in small, cohesive, independently testable units instead of one giant custom class.
solid answer
~40 sSince Spring Data 2.0, a single repository interface can extend multiple fragment interfaces, and each fragment gets its own <Fragment>Impl implementation. You just list them: `interface UserRepository extends JpaRepository<User, Long>, UserSearchFragment, UserExportFragment {}`, with `UserSearchFragmentImpl` and `UserExportFragmentImpl`. At bootstrap the infrastructure composes all fragments plus the base implementation into one proxy, so callers see a unified API. This matters for cohesion and reuse: each fragment is a small, single-responsibility unit that can be developed and unit-tested in isolation, and — importantly — a fragment interface can be shared across multiple repositories, letting you reuse a behavior (say, soft-delete or audit logic) without copy-paste. The declaration order of fragments also fixes method-resolution precedence, which matters when two fragments expose the same signature.
code
java · 26 lines// Two independent fragments
public interface UserSearchFragment {
List<User> search(String term);
}
public interface UserExportFragment {
byte[] exportCsv();
}
public class UserSearchFragmentImpl implements UserSearchFragment {
private final EntityManager em;
public UserSearchFragmentImpl(EntityManager em) { this.em = em; }
public List<User> search(String term) { /* Criteria query */ return List.of(); }
}
public class UserExportFragmentImpl implements UserExportFragment {
public byte[] exportCsv() { /* build CSV */ return new byte[0]; }
}
// One repository composes both + the base — order sets precedence
public interface UserRepository extends JpaRepository<User, Long>,
UserSearchFragment, UserExportFragment {
}
// The SAME fragment can be reused by another repository:
public interface OrderRepository extends JpaRepository<Order, Long>,
UserExportFragment { /* reuses UserExportFragmentImpl */ }go deeper
Know that a repository can extend more than one fragment.
Explain per-fragment Impl, reuse across repositories, and cohesion benefits.
Tie ordering to method-resolution precedence and discuss dependency isolation per fragment.
Use shared fragments as a cross-cutting reuse pattern (soft-delete/audit) and govern their generic design.
## From one 'custom' interface to many fragments Before Spring Data 2.0 the model allowed exactly **one** custom interface per repository (conventionally `XxxRepositoryCustom` + `XxxRepositoryCustomImpl`). Spring Data 2.0 generalized this into **fragments**: any number of interfaces, each with its own implementation, composed together. ## How you declare multiple fragments Extend all of them from the repository interface: ```java interface UserRepository extends JpaRepository<User, Long>, UserSearchFragment, UserExportFragment { } ``` Provide `UserSearchFragmentImpl implements UserSearchFragment` and `UserExportFragmentImpl implements UserExportFragment`. Each Impl follows the same `Impl` postfix rule and may declare its own constructor dependencies (one might need `EntityManager`, another a `RestClient`). At startup Spring Data builds a `RepositoryComposition` containing every fragment plus the generated base (`SimpleJpaRepository`) and backs the single repository proxy with it. ## Why split - **Single responsibility / cohesion** — a 400-line custom impl mixing search, exports, and bulk updates becomes three focused classes. - **Independent testing** — each Impl is a plain class with injected collaborators; you can unit-test it without the repository proxy. - **Reuse across repositories** — because a fragment is just an interface + impl, the **same fragment can be shared by many repositories**. A `SoftDeleteFragment` / `SoftDeleteFragmentImpl` can back both `UserRepository` and `OrderRepository`. (When shared, the impl typically works generically, e.g., via injected `EntityManager` and entity type.) - **Different dependency footprints** — keeps a fragment that needs an external client separate from pure-JPA fragments. ## Ordering and precedence The **order in which you list fragments** in the `extends` clause defines their priority. If two fragments declare the same method signature, the one listed **first wins**. Fragments always outrank the base implementation, which lets a fragment override a CRUD method like `save`. So ordering is not cosmetic — it is the tie-breaker for method resolution. ## Gotchas - Each fragment still needs its **own** correctly named Impl; you cannot put multiple fragments' logic in one Impl class and expect all to be discovered. - A shared fragment's Impl must live in a package covered by the repository scan base packages. - Don't overload one fragment with unrelated methods just to avoid an extra file — that defeats the purpose.
- Can two different repositories share the same fragment implementation?Yes. A fragment interface plus its Impl is independent of any one repository, so multiple repositories can extend the same fragment and reuse the behavior, provided the Impl is generic and lives in a scanned package.
- If two fragments declare the same method signature, which one runs?The fragment listed first in the repository's extends clause wins — declaration order defines precedence.
saying these in an interview costs you the question
- Claiming a repository can have only one custom interface (that was the pre-2.0 limit)
- Putting several fragments' logic into a single Impl class
- Thinking fragment order is irrelevant when signatures collide