Skip to content

Design Patterns

Este documento detalla los patrones de diseño utilizados en el microservicio op-scanner.

El Strategy pattern es un patrón de diseño de comportamiento que permite seleccionar un algoritmo en tiempo de ejecución. Define una familia de algoritmos, encapsula cada uno y los hace intercambiables.

En el microservicio op-scanner, el Strategy pattern se implementa en el caso de uso Scan para manejar diferentes tipos de entidades que pueden ser escaneadas.

Componentes clave:

  1. Context:

    • Clase Scan (src/app/usecase/scan.ts): Contiene una referencia a una estrategia y delega el trabajo a ella.
  2. Strategies:

    • findProduct: Estrategia para encontrar productos por EAN
    • findSupply: Estrategia para encontrar suministros por código de barras
    • findLocation: Estrategia para encontrar ubicaciones por nombre
    • findContainer: Estrategia para encontrar contenedores por nombre
  3. Strategy Selection:

    • El registro finders mapea los tipos de escaneo a sus métodos de búsqueda correspondientes:
      this.finders = {
      [ScanType.Product]: this.findProduct,
      [ScanType.Supply]: this.findSupply,
      [ScanType.Location]: this.findLocation,
      [ScanType.Container]: this.findContainer,
      }
  1. El método invoke recibe un texto para escanear y tipos opcionales para verificar.
  2. Filtra los tipos de escaneo basados en el formato del texto usando el scanTypeCheckerMapper.
  3. Para cada tipo coincidente, selecciona la estrategia de búsqueda apropiada del registro finders.
  4. Ejecuta cada estrategia y devuelve el primer resultado exitoso.
  • Flexibilidad: Se pueden agregar nuevos tipos de escaneo implementando un nuevo método de búsqueda y agregándolo al registro finders.
  • Mantenibilidad: Cada método de búsqueda está enfocado en una sola responsabilidad, haciendo que el código sea más fácil de mantener.
  • Testabilidad: Cada estrategia puede ser probada independientemente.

El microservicio también utiliza patrones de programación funcional a través de la biblioteca @justomx/either.

El Either monad se utiliza para el manejo de errores en toda la aplicación. Representa un valor que puede ser un éxito (Right) o un fracaso (Left).

Componentes clave:

  1. Either Class:

    • Proporciona métodos como map, flatMap y run para trabajar con el resultado.
  2. EitherAsync Class:

    • Extiende el patrón Either para trabajar con operaciones asíncronas.
  3. Ejemplos de uso:

    • En métodos de búsqueda para manejar posibles errores:
      return await EitherAsync.fromPromise(
      this.productService.getByEan(Ean.valueOf(text), country, warehouse)
      )
      .map((product) => new ScanProductInfo(product))
      .run()
  • Type Safety: Los errores se manejan de manera segura en cuanto a tipos.
  • Composition: Las operaciones pueden encadenarse con map y flatMap.
  • Readability: El código muestra claramente el camino feliz y el manejo de errores.

El Value Object pattern se utiliza extensivamente en la capa de dominio para representar objetos inmutables sin identidad.

Componentes clave:

  1. Value Objects:

    • Ean (src/domain/product/valueobjects/ean.ts): Representa un código EAN de producto
    • LocationName (src/domain/location/valueobjects/name.ts): Representa un nombre de ubicación
    • ContainerName (src/domain/container/valueobjects/name.ts): Representa un nombre de contenedor
    • Barcode (src/domain/supply/valueobjects/barcode.ts): Representa un código de barras de suministro
    • Country (src/domain/valueobjects/country.ts): Representa un país
    • Warehouse (src/domain/valueobjects/warehouse.ts): Representa un almacén
  2. Implementación:

    • Los value objects son inmutables
    • Validan sus datos en la creación
    • Encapsulan reglas de dominio relacionadas con sus valores
  • Immutability: Los value objects no pueden cambiarse después de la creación, reduciendo errores.
  • Validation: La lógica de validación está encapsulada en el value object.
  • Domain Logic: Las reglas de negocio relacionadas con el valor están encapsuladas.