Rules Hub

Coding Rules Library

← Back to all rules
backend ruleStack: typescript
repository-patternarchitecturedryclean-codeapi-design

Avoid specialized repository methods for filter combinations

Do not create specialized repository methods for specific filter combinations; rely on chainable (fluent) methods and a single terminal execution method.

PR: Feat/FCK-1888 - Create new articles by tag endpoint #1291Created: Dec 8, 2025

Bad example

Old codets
1class ArticleRepository extends BaseRepository {
2 // BAD: Creates a specific method just to apply one filter
3 // Duplicates the fetching and mapping logic found in landingPageTeasers()
4 public async getTeasersByTag(tagName: string): Promise<Teaser[]> {
5 return (await this.byTagName(tagName).asArray())
6 .map(article => article.toTeaser());
7 }
8
9 public async landingPageTeasers(): Promise<Teaser[]> {
10 return (await this.asArray()).map(article => article.toTeaser());
11 }
12}

Explanation (EN)

This approach leads to method explosion (e.g., `getTeasersByCategory`, `getTeasersByTag`). It duplicates the terminal logic (fetching and mapping) in every specialized method, making the class harder to maintain.

Objašnjenje (HR)

Ovaj pristup dovodi do prevelikog broja metoda (npr. `getTeasersByCategory`, `getTeasersByTag`). Duplira logiku dohvaćanja i mapiranja u svakoj specifičnoj metodi, što otežava održavanje klase.

Good example

New codets
1class ArticleRepository extends BaseRepository {
2 // GOOD: Expose filters as chainable methods
3 public byTagName(tagName: string) {
4 this.addFilter('tagName', tagName);
5 return this;
6 }
7
8 // GOOD: Only one terminal method defines the execution and mapping
9 public async landingPageTeasers(): Promise<Teaser[]> {
10 // Respects whatever filters were applied via the chain
11 return (await this.asArray()).map(article => article.toTeaser());
12 }
13}
14
15// Usage: await repo.byTagName('news').landingPageTeasers();

Explanation (EN)

By using a fluent interface, filters are composable. The consumer applies filters (`byTagName`) and then calls the reusable terminal method (`landingPageTeasers`), avoiding duplication of the mapping logic.

Objašnjenje (HR)

Korištenjem fluent sučelja, filteri postaju složivi. Potrošač primjenjuje filtere (`byTagName`) i zatim poziva ponovno iskoristivu terminalnu metodu (`landingPageTeasers`), izbjegavajući dupliranje logike mapiranja.

Notes (EN)

In a Repository pattern, separate 'criteria building' (chainable methods that return `this`) from 'execution' (methods that return data).

Bilješke (HR)

U Repository obrascu, odvojite 'izgradnju kriterija' (lančane metode koje vraćaju `this`) od 'izvršavanja' (metode koje vraćaju podatke).