CodePark/ questions / Repository, unit of work & data patterns
START PRACTICINGPRACTICE
Backend question · Software design & patterns › Repository, unit of work & data patterns

A repository interface keeps growing methods like findActiveUsersCreatedAfterWithRoleInRegion(...). How can the Specification pattern help, and what are its limits?

The Specification pattern puts one business rule in its own object. That object has a method like isSatisfiedBy(candidate). Eric Evans and Martin Fowler described this pattern.

You can combine specifications with and, or and not. For example, isActive().and(createdAfter(date)).and(inRegion(r)) replaces a separate repository method for every combination. Each rule also gets a name in the business language. You can unit test it on in-memory objects.

To use it for queries, the repository accepts a specification, as in users.findMatching(spec). The repository then turns it into a database query. You need one translator per storage technology. For example, a translator turns each specification into a where clause for the query builder.

Limits:

  • The translation layer adds complexity.
  • A specification that works in memory may not translate into efficient SQL.
  • For read-heavy queries built for one screen, a separate query service or read model is often simpler. It skips the domain repository.

Use specifications when the criteria are real business rules that you reuse in several places.

  • A specification is a business rule as an object with an isSatisfiedBy method.
  • You can combine specifications with and, or and not.
  • It gives named, testable business rules instead of a growing list of finder methods.
  • It needs translation into database queries, which adds complexity.
  • Queries for one screen may fit better in a separate query service.