T.
Edificio de cemento moderno blanco bajo cielo azul
Frontend Vue Arquitectura

Arquitectura Feature-Based vs Hexagonal

Dos conceptos diferentes que convinados entre sí generan un arquitectura sólida.

Dos conceptos que operan en dimensiones distintas: cómo agrupar el código y cómo aislar la lógica de negocio de los detalles técnicos. Son dos conceptos que suelen compararse, pero en realidad operan en dimensiones distintas de la organización del software. Uno resuelve cómo agrupar el código; el otro, cómo aislar la lógica de negocio de los detalles técnicos (frameworks, APIs, almacenamiento). Entender la diferencia ayuda a combinarlos correctamente en lugar de tratarlos como alternativas excluyentes.

Este apunte lo explica desde el ecosistema Vue 3 + Composition API, porque es donde este patrón se aplica de forma más natural en frontend: los composables hacen de “dominio” y los componentes .vue de “adaptadores de UI”.

Arquitectura Feature-Based (organización por características)

Organiza el código según funcionalidades del producto, no según tipo de archivo.

src/
  features/
    pedidos/
      components/
        ListaPedidos.vue
        DetallePedido.vue
      composables/
        usePedidos.ts
      pedidos.types.ts
    usuarios/
      components/
        PerfilUsuario.vue
      composables/
        useUsuarios.ts
    pagos/
      ...

Características:

  • Todo lo relacionado con una funcionalidad vive junto (componentes, composables, tipos, tests).
  • Facilita encontrar y modificar código relacionado con una feature concreta.
  • Reduce el acoplamiento entre features distintas.
  • Es más una convención de organización de carpetas que una filosofía arquitectónica profunda.

Problema que resuelve: el caos de organizar por tipo de archivo (components/, composables/, views/ como carpetas globales), donde trabajar en una sola funcionalidad obliga a saltar constantemente entre carpetas dispersas por todo el proyecto.

Cuándo usarla: proyectos pequeños o medianos, con lógica de negocio relativamente simple, o equipos que priorizan velocidad de desarrollo y facilidad de navegación sobre el aislamiento estricto de capas.

Arquitectura Hexagonal (Ports & Adapters)

Su objetivo es aislar la lógica de negocio (dominio) de los detalles técnicos externos: en frontend, esto significa aislar tu lógica de estado y reglas de negocio de la API concreta, del almacenamiento local, o incluso del propio Vue.

src/
  features/
    pedidos/
      domain/          ← lógica pura: entidades y reglas de negocio, sin Vue ni fetch
        pedido.entity.ts
      ports/            ← interfaces: qué necesita el dominio, sin decir cómo
        pedido-repository.port.ts
      adapters/
        http/           ← adaptador que llama a la API REST/GraphQL
          pedido-http.adapter.ts
        local-storage/  ← adaptador alternativo (ej. modo offline o caché)
          pedido-local.adapter.ts
      composables/      ← conecta el dominio con Vue (el "puerto de entrada")
        usePedidos.ts
      components/
        ListaPedidos.vue

Si mañana necesitas modo offline, creas un nuevo adaptador que implemente el mismo puerto y lo inyectas en el composable en vez del adaptador HTTP. El componente .vue no cambia una sola línea, y tampoco cambia la lógica de negocio en domain/.

Características:

  • El dominio no sabe nada de Vue, de fetch, ni de dónde vienen los datos.
  • El composable actúa de puerto de entrada: conecta el dominio con la reactividad de Vue.
  • El componente .vue es solo un adaptador de presentación, intercambiable.
  • Testear las reglas de negocio del dominio no requiere montar ningún componente ni mockear el DOM.

Problema que resuelve: la lógica de negocio mezclada dentro de componentes .vue, que hace difícil testear reglas de negocio sin levantar todo el árbol de componentes, y que acopla tu producto a una API o proveedor concretos.

Cuándo usarla: proyectos con reglas de negocio no triviales (cálculos, validaciones, estados complejos), equipos que necesitan alta testabilidad, o cuando se prevén cambios de fuente de datos (API, GraphQL, local-first, múltiples backends).

Diagrama conceptual

FEATURE-BASED                    HEXAGONAL (dentro de una feature)
(agrupación horizontal)          (capas concéntricas)

┌─────────┐ ┌─────────┐          ┌──────────────────────────┐
│ Pedidos │ │Usuarios │          │   Adaptadores (.vue,      │
│─────────│ │─────────│          │   http, local-storage)    │
│Component│ │Component│          │  ┌──────────────────────┐ │
│Composab.│ │Composab.│          │  │  Composables (puerto) │ │
└─────────┘ └─────────┘          │  │  ┌─────────────────┐  │ │
                                  │  │  │  Domain (puro)  │  │ │
                                  │  │  └─────────────────┘  │ │
                                  │  └──────────────────────┘ │
                                  └──────────────────────────┘

La diferencia clave

AspectoFeature-BasedHexagonal
Qué organizaCómo se agrupan los archivos/carpetasCómo se separan las responsabilidades (negocio vs Vue/API/storage)
Pregunta que responde”¿Dónde está el código de X funcionalidad?""¿Qué tan aislado está mi lógica de negocio de Vue y de la API?”
NivelOrganizacional / estructura de proyectoArquitectónico / dependencias y flujo de datos
Foco principalCohesión por funcionalidad de productoIndependencia del dominio respecto al framework y la infraestructura
OrigenConvención práctica de organizaciónAcuñada por Alistair Cockburn (~2005)
En Vue, se traduce enCarpetas features/pedidos/, features/usuarios/domain/ puro + ports/ + adapters/ + composables/ como puente hacia los componentes

Combinándolas: lo mejor de ambos mundos

No son excluyentes — de hecho, es la combinación más natural en un proyecto Vue real: organizas por feature, y dentro de cada feature, aplicas la separación hexagonal.

src/
  features/
    pedidos/
      domain/
      ports/
      adapters/
      composables/
      components/
    usuarios/
      domain/
      ports/
      adapters/
      composables/
      components/
    pagos/
      domain/
      ports/
      adapters/
      composables/
      components/

Con esta estructura obtienes:

  • Navegabilidad: todo el código de “pedidos” está junto, fácil de localizar como design engineer que trabaja tanto en UI como en lógica.
  • Aislamiento: dentro de “pedidos”, el dominio no depende de fetch, de axios ni de Vue.
  • Composables reutilizables: usePedidos() puede usarse en varios componentes distintos (lista, detalle, dashboard) sin duplicar lógica.
  • Testabilidad real: testeas domain/ con Vitest puro, sin @vue/test-utils, y testeas los composables mockeando el puerto — sin necesidad de red ni backend real.

Esta combinación es especialmente valiosa en proyectos que empiezan simples pero se espera que crezcan: puedes arrancar con componentes y composables planos por feature, e ir introduciendo domain/, ports/ y adapters/ feature por feature, a medida que la lógica de negocio se vuelve más compleja — sin necesidad de una reescritura completa.

Conclusión

  • Feature-based responde a “¿cómo organizo mis carpetas para encontrar las cosas rápido?”
  • Hexagonal responde a “¿cómo protejo mi lógica de negocio de Vue, de la API y del almacenamiento?”

En un proyecto Vue, la combinación natural es: carpetas por feature por fuera, y domain → ports → adapters → composables → componentes por dentro de cada una.