Arquitectura de Software

Domain-Driven Design: Generic Subdomains, cuándo reutilizar soluciones existentes en lugar de construir desde cero.

Evalúa capacidades genéricas con criterios Build vs. Buy y coste total de propiedad. Compara opciones de autenticación, conserva contratos internos con TypeScript y aprende a reutilizar sin delegar seguridad ni decisiones del negocio.

Cargando tus opciones de aprendizaje…

Una tienda que no necesita inventar otra autenticación

La tienda especializada de nuestra colección quiere mejorar sus recomendaciones. Antes de comenzar, el equipo propone construir un sistema completo de identidad: contraseñas, recuperación de acceso, segundo factor, sesiones, gestión de claves y herramientas para responder a incidentes.

Todo eso es importante. Pero, en esta estrategia, los clientes no eligen la tienda porque haya creado una tecnología propia de autenticación.

La pregunta no es si necesitamos autenticar usuarios. La pregunta es qué parte debemos resolver nosotros y qué conviene reutilizar para operar correctamente sin desviar el esfuerzo del negocio.

Los Generic Subdomains, o subdominios genéricos, ayudan a identificar problemas suficientemente conocidos como para evaluar soluciones existentes antes de construir desde cero.

Genérico describe el problema, no su importancia

Un subdominio genérico aborda capacidades que no expresan el conocimiento diferenciador de esta organización y que suelen contar con modelos o soluciones ampliamente utilizados.

Evans recomienda separar esos problemas del conocimiento especializado del núcleo y considerar soluciones disponibles o modelos publicados. También recuerda que, por genéricos que sean, pueden resultar esenciales para el sistema. DDD Reference: Generic Subdomains, página 41 del documento.

La clasificación no implica que una solución concreta encaje. Tampoco obliga a contratar un SaaS.

Puedes reutilizar una biblioteca, operar un componente existente o integrar un servicio administrado. Cada alternativa distribuye de manera diferente desarrollo, operación y responsabilidad.

Comparación breve con las otras categorías

En nuestra tienda, el modelo de recomendación personalizada contiene conocimiento que pretende diferenciar la propuesta. Es candidato a Core Domain.

La operación de devoluciones tiene reglas particulares necesarias para sostenerla. Puede ser un Supporting Subdomain.

La autenticación convencional resuelve una necesidad común a muchos productos. Aquí la tratamos como Generic Subdomain.

No son categorías universales de tecnologías. Para una organización dedicada a identidad digital, algunas capacidades que la tienda compra podrían formar parte de su conocimiento estratégico.

Además, Generic Subdomain no equivale a una carpeta, a un Bounded Context concreto o a un microservicio que debas desplegar.

Complejo, crítico y genérico al mismo tiempo

La autenticación puede requerir conocimientos especializados y tener consecuencias graves cuando falla. No se vuelve sencilla por clasificarla como genérica.

Construir una pantalla de login no equivale a mantener un sistema completo de identidad. Hay que considerar recuperación de acceso, protección de credenciales, sesiones, factores adicionales y tratamiento de intentos abusivos. OWASP recoge controles y riesgos que muestran ese alcance. Authentication Cheat Sheet.

Reutilizar una solución especializada puede evitar implementar parte de esos mecanismos. No elimina la responsabilidad de configurarla correctamente ni de proteger la aplicación que la consume.

La regla práctica es separar dos decisiones: reconocer que un problema no es diferenciador y decidir cómo satisfacer sus requisitos. La primera informa a la segunda; no la resuelve automáticamente.

Build vs. Buy es más que comparar una factura

«Construir» y «comprar» son extremos de una familia de alternativas:

  • Desarrollar y mantener una implementación propia.
  • Integrar una biblioteca mantenida.
  • Operar un producto existente en infraestructura propia.
  • Utilizar una plataforma SaaS o un servicio administrado.
  • Combinar componentes existentes con una adaptación acotada.

Una opción de código abierto puede no tener una licencia comercial de pago y seguir necesitando operación, actualizaciones y personal especializado.

Una opción administrada puede reducir ciertas tareas y añadir dependencia de disponibilidad, límites de uso o decisiones del proveedor.

No hay una respuesta correcta para todas las organizaciones. Hay requisitos, restricciones y costes que deben compararse con evidencia.

Preparar una evaluación antes de mirar demos

Para la tienda, primero identificamos necesidades:

  • Usuarios identificables de forma estable.
  • Recuperación de acceso y factores de autenticación adecuados.
  • Integración con sus aplicaciones.
  • Revocación o invalidación según los requisitos de sesión.
  • Separación entre identidad externa y cuenta comercial local.
  • Operación y respuesta ante fallos.
  • Posibilidad de migrar o reducir dependencia si cambia la situación.

Después se analizan datos tratados, controles de acceso, requisitos aplicables y responsabilidades contractuales. No se deduce cumplimiento normativo de un logotipo o una afirmación comercial.

Este artículo no recomienda proveedores específicos ni presenta precios actuales. Una selección real requiere investigación y pruebas de las alternativas disponibles.

Una matriz cualitativa de evaluación

Criterio              Construcción propia       Solución existente
Tiempo inicial        Implementar mecanismos    Integrar y configurar
Mantenimiento         Responsabilidad interna   Se reparte, no desaparece
Personalización       Mayor control potencial  Depende de capacidades y límites
Seguridad             Mantener conocimientos   Evaluar producto y configuración
Operación             Diseñarla y sostenerla   Depende de quién opera
Salida                Migrar implementación    Migrar datos y dependencias

La matriz no afirma que una columna siempre gane. En particular, control potencial no significa que el equipo tenga capacidad para mantenerlo correctamente.

Primero identifica requisitos que una alternativa no puede incumplir. Después compara costes y ventajas entre las opciones que sí los satisfacen.

El coste total de propiedad

Para comparar, define un horizonte y escenarios de uso realistas. Considera:

Implementación. Desarrollo, integración, pruebas y adaptación de la experiencia de acceso.

Operación. Monitorización, recuperación, soporte, copias y disponibilidad cuando sean responsabilidad propia.

Mantenimiento. Actualizaciones, vulnerabilidades, cambios de APIs y compatibilidad.

Consumo y crecimiento. Usuarios, peticiones, almacenamiento, tráfico y funciones que modifican el coste. La forma de facturación debe verificarse en la oferta evaluada.

Riesgo. Consecuencias de una caída, incidentes y limitaciones que afecten al negocio. No inventes una cifra de probabilidad para completar una hoja.

Salida. Exportación de datos, cambio de identificadores, adaptación de consumidores y posibles procesos de recuperación de cuentas.

Oportunidad. Qué deja de desarrollar el equipo mientras construye una capacidad genérica.

No utilices un coste de desarrollo único para representar años de operación. Tampoco compares una licencia con cero sin incluir el trabajo interno de su alternativa.

Mantener la identidad externa fuera del modelo comercial

Aunque integremos un proveedor, la tienda conserva responsabilidades propias: clientes, permisos comerciales, pedidos y preferencias.

Proveedor de identidad
       |
       | verificación mediante SDK o protocolo documentado
       v
Adaptador de identidad de la tienda
       |
       | identidad externa verificada
       v
Correspondencia con cuenta local
       |
       v
Aplicación y decisiones del negocio

Un identificador externo no tiene por qué ser el identificador del cliente local. Es conveniente registrar la correspondencia con el emisor y el sujeto, según el protocolo elegido.

No vincules automáticamente cuentas solo porque dos proveedores devuelven el mismo correo. Un atributo visible no demuestra por sí solo que deban acceder a la misma cuenta.

Tampoco confundas autenticación con autorización: identificar al usuario no demuestra que pueda consultar un pedido de otra persona o entrar en la intranet.

Un contrato interno pequeño en TypeScript

El siguiente ejemplo es didáctico y orientado a una aplicación Node.js. No implementa login, criptografía ni un protocolo completo.

En identity.ts:

export interface ApplicationIdentity {
  readonly userId: string;
}

export interface IdentityVerifier {
  verify(credential: string): Promise<ApplicationIdentity | null>;
}

export interface ExternalVerificationGateway {
  verifyUserCredential(
    credential: string,
    policy: {
      expectedIssuer: string;
      expectedAudience: string;
    },
  ): Promise<unknown>;
}

export interface LocalAccounts {
  findLinkedAccount(
    issuer: string,
    subject: string,
  ): Promise<{ userId: string; enabled: boolean } | null>;
}

ExternalVerificationGateway es un puerto ilustrativo, no el nombre de una API real. Su implementación debe utilizar el SDK oficial o un protocolo documentado del proveedor.

El contrato de ese puerto exige verificar la credencial de usuario, su propósito y los requisitos del protocolo antes de devolver un resultado. No basta con decodificar un JWT, leer una cookie o confiar en datos del navegador.

Para nuestro ejemplo, el gateway normaliza la respuesta verificada a un objeto con active, issuer, subject, audience y expiresAtSeconds. Esos nombres son una representación didáctica propia, no una afirmación de que todos los proveedores devuelven ese JSON.

Como referencia de una opción real, OAuth Token Introspection permite consultar el estado activo y metadatos de un token mediante un endpoint protegido. Su uso exige aplicar las condiciones del protocolo y no convertir cualquier token activo en identidad de usuario. RFC 7662.

Adaptar el resultado verificado a la cuenta local

El siguiente bloque complementa el archivo anterior:

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null &&
    !Array.isArray(value);
}

export class ExternalIdentityAdapter implements IdentityVerifier {
  constructor(
    private readonly gateway: ExternalVerificationGateway,
    private readonly accounts: LocalAccounts,
    private readonly issuer: string,
    private readonly audience: string,
    private readonly nowSeconds: () => number,
  ) {}

  async verify(credential: string): Promise<ApplicationIdentity | null> {
    if (credential.trim().length === 0) return null;

    const raw = await this.gateway.verifyUserCredential(credential, {
      expectedIssuer: this.issuer,
      expectedAudience: this.audience,
    });

    if (
      !isRecord(raw) ||
      raw.active !== true ||
      raw.issuer !== this.issuer ||
      raw.audience !== this.audience ||
      typeof raw.subject !== "string" ||
      raw.subject.length === 0 ||
      typeof raw.expiresAtSeconds !== "number" ||
      !Number.isSafeInteger(raw.expiresAtSeconds) ||
      raw.expiresAtSeconds <= this.nowSeconds()
    ) {
      return null;
    }

    const account = await this.accounts.findLinkedAccount(
      this.issuer,
      raw.subject,
    );

    if (!account || !account.enabled) return null;
    return { userId: account.userId };
  }
}

Los valores de emisor y audiencia vienen de configuración controlada. El reloj se inyecta para probar límites; en producción debe proceder de una fuente confiable del servidor.

El chequeo de campos no reemplaza la verificación de seguridad del gateway. El adaptador presupone que ese componente ha validado lo que requiere el protocolo, incluyendo firma cuando corresponda, propósito y condiciones de validez.

En nuestro contrato normalizado la audiencia es una cadena única. Si el proveedor utiliza varias audiencias, el gateway debe interpretarlas correctamente y normalizar solo después de comprobar la esperada.

Una credencial válida sin cuenta vinculada no crea una cuenta automáticamente. El alta y la vinculación necesitan un proceso separado que no hemos implementado.

El fallo externo no autoriza el acceso

Si el gateway falla por indisponibilidad, la excepción se propaga. La capa de entrada debe distinguir ese fallo técnico de una credencial inválida, sin permitir acceso como alternativa.

No mostramos persistencia de cuentas, transporte del gateway, caché de verificación ni gestión de sesiones. Esas decisiones requieren configurar e integrar la solución elegida.

La finalidad del fragmento es demostrar que la aplicación recibe userId, no un objeto de proveedor con permisos asumidos.

Una prueba local no certifica la autenticación

Esta prueba propuesta utiliza APIs integradas de Node.js y presupone compilación de TypeScript a JavaScript ESM. No se ha ejecutado como parte del artículo.

import test from "node:test";
import assert from "node:assert/strict";
import { ExternalIdentityAdapter } from "./identity.js";

test("rechaza una credencial expirada sin buscar la cuenta", async () => {
  let accountLookupCalled = false;

  const adapter = new ExternalIdentityAdapter(
    {
      async verifyUserCredential() {
        return {
          active: true,
          issuer: "https://identity.example",
          subject: "external-user-1",
          audience: "shop-api",
          expiresAtSeconds: 100,
        };
      },
    },
    {
      async findLinkedAccount() {
        accountLookupCalled = true;
        return { userId: "user-100", enabled: true };
      },
    },
    "https://identity.example",
    "shop-api",
    () => 100,
  );

  assert.equal(await adapter.verify("test-credential"), null);
  assert.equal(accountLookupCalled, false);
});

Es una prueba del adaptador con un gateway sustituido. No prueba criptografía, comunicación con el proveedor ni configuración real.

La verificación de integración debe cubrir credenciales válidas, inválidas, revocadas o expiradas, emisor y audiencia incorrectos, sujetos no vinculados, cuentas deshabilitadas y fallos del proveedor, según las garantías del sistema elegido.

No registres credenciales, tokens o contraseñas para depurar esas pruebas. Y no utilices usuarios o secretos de producción para experimentar.

Qué cambia al utilizar una solución existente

La tienda deja de implementar ciertos mecanismos especializados, pero conserva trabajo:

  • Elegir y configurar el protocolo apropiado.
  • Proteger secretos y credenciales administrativas.
  • Mantener el adaptador y sus dependencias.
  • Definir permisos de su aplicación.
  • Gestionar vinculación y baja de cuentas.
  • Establecer cómo opera cuando el proveedor no responde.
  • Revisar tratamiento de datos y condiciones aplicables.

Una frontera pequeña puede limitar la propagación del modelo externo. No elimina todas las dependencias: el proceso de recuperación o las opciones de sesión pueden seguir condicionando la experiencia.

No construyas una abstracción universal que prometa intercambiar cualquier proveedor sin esfuerzo. Representa las necesidades que tienes y documenta lo que no es intercambiable.

Cuándo desarrollar puede estar justificado

Un problema genérico no obliga a externalizarse. Puede haber restricciones de operación, integración, conectividad o control de datos que hagan inadecuada una alternativa concreta.

También puede resultar razonable implementar una pieza pequeña cuando integrar un sistema completo añade más coste que resolver el alcance conocido.

Sin embargo, «necesitamos una pantalla distinta» no demuestra que haya que implementar desde cero almacenamiento de contraseñas, recuperación y verificación de tokens.

Distingue personalizar la experiencia, integrar un componente existente y desarrollar una plataforma de identidad completa. La decisión debe responder a necesidades verificables y a la capacidad del equipo para sostenerla.

Reutilizar no impide desarrollar código propio. Construir no obliga a inventar mecanismos criptográficos.

Una decisión Build vs. Buy que pueda revisarse

Una ficha útil para la tienda podría registrar:

Capacidad: autenticación convencional.
Papel estratégico: genérico en la propuesta actual.
Requisitos obligatorios: protocolos, seguridad, datos y operación identificados.
Alternativas: componente existente operado internamente o servicio especializado, frente a desarrollo propio.
Evaluación pendiente: pruebas de integración, costes a distintos volúmenes y procedimiento de salida.
Frontera local: contrato de identidad y cuentas comerciales independientes.
Condiciones de revisión: requisitos no satisfechos, cambios de precio o límites y dificultades de migración.

No es una selección real de proveedor. Para tomarla faltan datos concretos de alternativas y requisitos.

Las condiciones de revisión ayudan a evitar tanto la dependencia olvidada como una migración impulsiva por una sola factura.

Otra aplicación: una plataforma educativa

Una plataforma centrada en práctica y retroalimentación puede utilizar servicios existentes para autenticación, correo y almacenamiento.

Eso permite concentrar parte del desarrollo en los errores conceptuales y las actividades que ayudan a aprender. No convierte las otras capacidades en irrelevantes.

El correo debe respetar destinatarios, permisos y preferencias; aceptar una solicitud de envío no significa que el mensaje haya llegado. El almacenamiento debe aplicar autorización y evitar que archivos privados se publiquen accidentalmente.

La plataforma mantiene reglas educativas propias mientras utiliza mecanismos comunes. Puede conservar contratos locales pequeños para envío y acceso a documentos, sin copiar cada detalle del proveedor.

Si en el futuro vende una tecnología especializada de entrega de correo, la clasificación de esa capacidad puede cambiar. La etiqueta depende de su estrategia, no del nombre de la API.

Tercer ejemplo: reutilizar una biblioteca en vez de un servicio

Un producto necesita generar documentos con un formato conocido. Puede evaluar una biblioteca mantenida antes de construir un motor propio o añadir un servicio remoto.

La biblioteca introduce riesgos de dependencias, actualizaciones y formatos no soportados; el servicio introduce además red, límites y tratamiento externo de documentos.

La alternativa adecuada depende del alcance y los datos. Este ejemplo muestra que Build vs. Buy no se reduce a «nuestro código o SaaS».

Las tres opciones requieren pruebas con documentos reales de prueba y expectativas verificables, no solo una demo sencilla.

Errores que conviene evitar

Genérico significa barato. Una capacidad crítica puede necesitar inversión considerable aunque no diferencie.

El proveedor se ocupa de toda la seguridad. La aplicación sigue siendo responsable de su configuración y permisos.

La biblioteca es gratuita, así que no tiene coste. Hay operación, mantenimiento y dependencia de versiones.

El servicio más conocido encaja por definición. Hay que verificar requisitos y límites concretos.

El SDK define el dominio. Una dependencia externa no debería convertirse automáticamente en modelo de cliente o política comercial.

La IA puede construirlo en una tarde. Generar código no cubre mantenimiento, incidentes o garantías de seguridad.

Utilizar IA como apoyo a la evaluación

Para Cursor, Claude o Codex, proporciona requisitos, restricciones y evidencia disponible.

Para investigar alternativas:

Busca alternativas actuales para esta capacidad genérica. Usa documentación oficial y distingue capacidades verificadas de preguntas pendientes. No inventes precios, certificaciones ni límites. Compara integración, operación, datos y salida según estos requisitos.

Para diseñar la frontera:

Propón un contrato interno mínimo para la identidad que necesita nuestra aplicación. Mantén emisor y sujeto externos separados del usuario local. Usa un SDK o protocolo documentado para verificar credenciales; no implementes criptografía ni aceptes claims decodificados como prueba de identidad.

Para revisar una decisión:

Revisa esta comparación Build vs. Buy. Identifica costes omitidos, dependencias de proveedor y requisitos sin evidencia. Separa hechos, estimaciones e hipótesis. No presentes una puntuación automática como aprobación de seguridad o inversión.

La selección final requiere criterio humano y revisión de los responsables. No envíes datos sensibles o credenciales a herramientas de análisis para completar una comparación.

Ejercicios

1. La pantalla personalizada

El equipo quiere una pantalla de acceso con la identidad visual de la tienda y concluye que debe construir toda la autenticación. ¿Qué alternativas faltan?

2. El token activo

Un servicio devuelve que un token está activo. El código permite acceso a cualquier usuario que lo presente. ¿Qué comprobaciones pueden faltar?

3. La alternativa sin licencia de pago

Un componente existente no cobra por uso. ¿Basta para concluir que su coste total será menor?

Soluciones razonadas

1. Evaluar personalización e integración de soluciones existentes, sin asumir que la interfaz visual exige mecanismos propios de identidad. Hay que comprobar qué permite cada alternativa y qué requisitos reales quedan fuera.

2. Verificar propósito, emisor, destinatario y condiciones del protocolo, además de la correspondencia con una cuenta local habilitada. Un token activo puede no representar una credencial de usuario válida para esta aplicación; identidad tampoco equivale a autorización sobre cualquier recurso.

3. No. Hay que incluir infraestructura, actualizaciones, soporte, seguridad, operación y salida, además del tiempo de implementación. La ausencia de una tarifa no elimina esos costes.

Reutilizar para concentrar mejor el esfuerzo

Identificar un Generic Subdomain permite preguntar si una solución existente cubre la necesidad antes de construirla desde cero.

La decisión es sólida cuando responde a requisitos claros, compara costes completos y conserva una frontera comprensible con el negocio propio.

No se trata de externalizar todo lo común. Se trata de resolverlo con garantías suficientes y una responsabilidad que la organización pueda sostener.

Cargando tus opciones de aprendizaje…

Cargando conversación…

Sigue aprendiendo