Rules Hub
Coding Rules Library
Encapsulate transformation logic in domain models
Avoid transforming or formatting entity data inside controllers or routes; encapsulate this logic as computed properties or methods within the entity class itself.
Bad example
| 1 | // Controller/Route handler |
| 2 | const getUsers = async () => { |
| 3 | const users = await UserRepository.findAll(); |
| 4 | |
| 5 | // BAD: Transformation logic leaks into the controller |
| 6 | return users.map(user => ({ |
| 7 | ...user, |
| 8 | fullName: `${user.firstName} ${user.lastName}`, |
| 9 | avatar: ScaleImage.getScaledUrl(user.avatarId, { width: 100 }) |
| 10 | })); |
| 11 | }; |
Explanation (EN)
The controller manually formats the avatar URL and full name using external helpers. This leads to anemic models and scattered logic; if another endpoint needs the same user data, you must duplicate this mapping logic.
Objašnjenje (HR)
Kontroler ručno formatira URL avatara i puno ime koristeći vanjske helpere. To dovodi do anemičnih modela i raspršene logike; ako drugi endpoint treba iste podatke o korisniku, morate duplicirati ovu logiku mapiranja.
Good example
| 1 | // Entity/Model class |
| 2 | class User extends BaseEntity { |
| 3 | get fullName(): string { |
| 4 | return `${this.firstName} ${this.lastName}`; |
| 5 | } |
| 6 |
|
| 7 | get avatarUrl(): string { |
| 8 | if (!this.avatarId) return ''; |
| 9 | // Logic is encapsulated here |
| 10 | return ScaleImage.getScaledUrl(this.avatarId, { width: 100 }); |
| 11 | } |
| 12 |
|
| 13 | toJSON() { |
| 14 | return { |
| 15 | ...this.data, |
| 16 | fullName: this.fullName, |
| 17 | avatar: this.avatarUrl, |
| 18 | }; |
| 19 | } |
| 20 | } |
| 21 |
|
| 22 | // Controller/Route handler |
| 23 | const getUsers = async () => { |
| 24 | const users = await UserRepository.findAll(); |
| 25 | // GOOD: Controller simply returns the rich entities |
| 26 | return users; |
| 27 | }; |
Explanation (EN)
The transformation logic is moved into the `User` entity class as getters. The controller remains clean, and the logic for generating the avatar URL is centralized in one place, ensuring consistency across the application.
Objašnjenje (HR)
Logika transformacije premještena je u `User` entitet kao getter. Kontroler ostaje čist, a logika za generiranje URL-a avatara centralizirana je na jednom mjestu, osiguravajući dosljednost u cijeloj aplikaciji.
Notes (EN)
If multiple subclasses share the same transformation logic (e.g., `Agenda` and `AgendaExternal`), implement the logic once in the abstract base class (`AgendaBase`) instead of duplicating it or handling it externally.
Bilješke (HR)
Ako više podklasa dijeli istu logiku transformacije (npr. `Agenda` i `AgendaExternal`), implementiraj logiku jednom u apstraktnoj baznoj klasi (`AgendaBase`) umjesto da je dupliciraš ili obrađuješ izvana.