Implementing Repository Pattern
1. Implementing Repository Interface
Example: Domain repository contract
public interface CustomerRepository {
Optional<Customer> findById(CustomerId id);
Optional<Customer> findByEmail(Email email);
void save(Customer customer);
void delete(CustomerId id);
Page<Customer> search(CustomerFilter filter, Pageable pageable);
}
2. Implementing Generic Repository
Example: Spring Data style
public interface CrudRepository<T, ID> {
Optional<T> findById(ID id);
List<T> findAll();
T save(T entity);
void deleteById(ID id);
long count();
boolean existsById(ID id);
}
Warning: Avoid exposing generic CRUD on domain repositories — only what makes sense for the aggregate.
3. Implementing Specification Pattern
Example: Spring Data JPA Specification
public class OrderSpecs {
public static Specification<Order> byStatus(String s) {
return (root, q, cb) -> cb.equal(root.get("status"), s);
}
public static Specification<Order> placedAfter(Instant t) {
return (root, q, cb) -> cb.greaterThan(root.get("placedAt"), t);
}
}
repo.findAll(byStatus("PAID").and(placedAfter(yesterday)));
4. Implementing Unit of Work Pattern
| Element | Implementation |
| Spring | @Transactional ⇒ EntityManager session |
| Tracking | Hibernate dirty checking |
| Flush | Auto on commit; manual via em.flush() |
5. Implementing Query Object Pattern
| Form | Detail |
| Criteria builder | Type-safe, programmatic |
| QueryDSL | Fluent type-safe DSL |
| jOOQ | SQL-like fluent API |
| Plain DTO | Filter object passed to repo |
6. Implementing Data Access Layer Abstraction
| Goal | Mechanism |
| Hide ORM | Domain depends on interface |
| Swap impl | JPA → MongoDB without service changes |
| Test | In-memory fake repo |
7. Implementing Repository Methods
| Naming | Spring Data Convention |
| find by | findBy<Field> |
| multiple criteria | findByXAndY |
| ordering | findByXOrderByY[Asc|Desc] |
| existence | existsBy* |
| counting | countBy* |
| deleting | deleteBy* |
Example: Paged query
Page<Order> page = orderRepo.findByStatus("OPEN",
PageRequest.of(pageNum, size, Sort.by("placedAt").descending()));
9. Implementing Repository Exception Handling
| Scenario | Action |
| Not found | Return Optional.empty() |
| Constraint violation | Translate to domain exception |
| Connection error | Wrap as IntegrationException |
| Spring | @Repository auto-translates |
10. Implementing Custom Repository Implementations
Example: Spring Data custom impl
public interface OrderRepoCustom { List<OrderSummary> reportFor(Instant from); }
public class OrderRepoCustomImpl implements OrderRepoCustom {
@PersistenceContext private EntityManager em;
public List<OrderSummary> reportFor(Instant from) {
return em.createQuery("...", OrderSummary.class)
.setParameter("f", from).getResultList();
}
}
public interface OrderRepository extends JpaRepository<Order, Long>, OrderRepoCustom {}
11. Implementing Repository Factory
| Use Case | Detail |
| Multi-tenant | Per-tenant connection |
| Shard routing | Pick repo by key |
| Read replica | Route reads to replica |
12. Implementing Read vs Write Repository
Write Repository
- Loads aggregates
- Saves aggregates
- Tx boundary
- Returns domain types
Read Repository
- Optimized projections
- Returns DTOs
- Read-only tx
- May use replica