When are decorators useful?

Decorators can be used to deal with cross-cutting concerns in your application—that is, concerns that are not directly related to application features but are instead present everywhere in the system and can't be isolated from the rest of the application.

Examples of cross-cutting concerns include logging, authorization, exception handling, transaction management, validation, caching, and more. All of these are secondary, but can't be taken out or isolated without loss.

Decorators help us to tackle such concerns cleanly. By annotating classes and their fields with decorators, and therefore adding metadata/behavior, you can add specific information/logic where needed, without necessarily cluttering your code.

Let's look at an example that demonstrates how badly cross-cutting concerns can pollute your code:

class BankService { 
    constructor() { } 
 
    public depositMoney(amount: number): void { 
        log(`Persisting account change - start: ${new Date()}`); 
        if(!isAuthenticated) { 
            log('Refused to deposit money'); 
            throw new Error('Only authenticated users can deposit 
money'); } if(!amount || amount <= 0) { throw new Error('The amount is not valid'); } try { doInTransaction(() => { // try to do something interesting commitTransaction(); }); } catch(error) { log(`Operation failed: ${error}`); rollbackTransaction(); } log(`Persisting account change - end: ${new Date()}`); } }

In the preceding example, the actual business logic only represents a fraction of the code. The rest is exclusively dealing with cross-cutting concerns.

The following is how the same code could be rewritten with the help of decorators:

import {Authenticated, Traced, Transactional, ValidNumber} from "./02-cross-cutting-utils"; 
 
class NewBankService { 
  constructor() { 
  } 
 
  @Traced 
  @Authenticated 
  @Transactional 
  public depositMoney(@ValidNumber({min: 0}) amount: number):
void { // try to do something interesting } } new NewBankService().depositMoney(500);

Decorators are now responsible for handling secondary concerns while the method itself fully focuses on business logic. This is much better for readability, reusability, and maintainability.