Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

207
Vistas
SOLID - Dependency Inversion Principle: Should utility/helper functions be an interface or can I use its concrete implementation?

Given this dummy code, which has an if statement with a verbose code (imagine this is a really verbose if statement), I decide to create a util function to make my code cleaner. When I choose to create a util function, should it be implemented as an interface or can I implement the concrete function instead? I may use this same function again in other class.

Dummy code:

export class UserRepository {
      constructor (private readonly database: DatabaseProtocol) {}
    
      async findUserByEmail (data: Record<string, any>): Promise<IUser | null> {
        const userCollection = this.database.collection('users')
    
        if (Object.keys(data).length === 0) return null
    
        const user = await userCollection.findOne({ email: data.email })
    
        return user
      }
    }

Concrete implementation:

 export class UserRepository {
      constructor (private readonly database: DatabaseProtocol) {}
    
      async findUserByEmail (data: Record<string, any>): Promise<IUser | null> {
        const userCollection = this.database.collection('users')
    
        if (isEmpty(data)) return null
    
        const user = await userCollection.findOne({ email: data.email })
    
        return user
      }
    }

Interface implementation:

export class UserRepository {
  constructor (
    private readonly database: DatabaseProtocol,
    private readonly isEmpty: IsEmptyProtocol
  ) {}

  async findUserByEmail (data: Record<string, any>): Promise<IUser | null> {
    const userCollection = this.database.collection('users')

    if (this.isEmpty(data)) return null

    const user = await userCollection.findOne({ email: data.email })

    return user
  }
}

Private method:

export class UserRepository {
  constructor (
    private readonly database: DatabaseProtocol
  ) {}

  async findUserByEmail (data: Record<string, any>): Promise<IUser | null> {
    const userCollection = this.database.collection('users')

    if (this.isEmpty(data)) return null

    const user = await userCollection.findOne({ email: data.email })

    return user
  }

  isEmpty (data: Record<string, any>): boolean {
    return Object.keys(data).length === 0
  }
}

Which option above would be considered the best practice?

about 4 years ago · Juan Pablo Isaza
2 Respuestas
Responde la pregunta

0

There's not really any alternatives implementation you'd want to inject for such helper functions, therefore an interface would be overkill and combersome to use.

If you look at java, they usually have such utility functions in plural classes like Arrays where we can find utilities such as Arrays#asList. JavaScript does the same with for instance Object.entries.

With modules you could just have an objects.ts module and export an isEmpty function to reuse.

One thing to be careful about though is whether "empty" could have different meanings. For instance, perhaps "empty" in another context could mean there could be keys, but they don't count if they reference null or undefined. Therefore, it's important to document the behavior properly.

Furthermore, the empty check is really domain-specific. It seems to represent in your case whether or not there's an email filter present. Therefore, rather than calling isEmpty directly you'd most likely have a private hasEmailCriteria function or something.

However, in the end I also think the signature of findUserByEmail is pretty bad. Why not just accept an email such as findUserByEmail(email: string) instead? Or even better, perhaps findUserByEmail(address: EmailAddress).

about 4 years ago · Juan Pablo Isaza Denunciar

0

Here are my 2 cents:

  1. If the IsEmpty functionality is needed only for UserRepository class then make it a private method of that class.
  2. If the IsEmpty functionality is needed by some (by not all) classed in your program, then go with the Interface implementation: solution you proposed in your question.
  3. If the IsEmpty functionality is used in all your classes (or in the vast majority), then make it a global class with static methods. I know, I know, Uncle Bob does not allow global state (and rightfully so), but this is an exception. This case is a cross-cutting concern (please see my answer here from more details).
about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda