Arquitectura de software para devs: dejar de hablar de 'microservicios' y empezar a resolver problemas
Por Equipo Hexadevs · 2 oct 2026 · 4 min de lectura
El término “arquitectura de software” se usa tanto y tan mal que muchos devs junior lo asocian con diagramas bonitos en Notion que nadie mira después. La arquitectura real es cómo está organizado tu código para que cambie sin romperse. Es una decisión de diseño continua, no un PDF que se firma al inicio del proyecto.
La pregunta que la arquitectura responde
Antes de elegir patrón, framework o base de datos, respondé:
¿Cuál es el cambio más probable que va a tener este sistema en los próximos 6 meses?
Si tu app es un CRUD sobre una base de datos, la respuesta probablemente es “más endpoints” o “más tablas”. Si tu sistema procesa pagos, la respuesta es “más pasarelas, más países, más regulaciones”. La arquitectura debe estar optimizada para el cambio más probable, no para el más vistoso.
La arquitectura por capas: lo clásico, bien aplicado
La organización mínima que usamos:
src/routes/— HTTP: parsear request, devolver responsesrc/services/— lógica de negocio: orquestaciónsrc/repositories/— acceso a datos: queries a la DBsrc/domain/— entidades y reglas de dominio purassrc/lib/— utilidades transversales
Las reglas:
- Una capa solo conoce a la inmediatamente inferior. Las rutas conocen a los services. Los services conocen a los repositories. Los repositories no saben nada del mundo exterior.
- El dominio no depende de nada. Las entidades y reglas de negocio son funciones puras, sin framework ni DB.
- Las dependencias apuntan hacia el dominio, no al revés. Es la inversión de dependencias.
// Mal: route con lógica de negocio
app.post("/usuarios", async (req, res) => {
const { email, password } = req.body
const hash = await bcrypt.hash(password, 10)
await db.query("INSERT INTO usuarios (email, password) VALUES ($1, $2)", [email, hash])
res.json({ ok: true })
})
// Bien: route delgada, service con la lógica
app.post("/usuarios", async (req, res) => {
try {
const usuario = await usuarioService.crear(req.body)
res.status(201).json(usuario)
} catch (err) {
if (err instanceof EmailDuplicadoError) {
return res.status(409).json({ error: err.code })
}
throw err
}
})
Arquitectura hexagonal: cuando las capas no alcanzan
Si tu sistema habla con muchas cosas externas (DB, APIs externas, colas, cache), las capas se contaminan rápido. La arquitectura hexagonal (también llamada “ports & adapters”) lo resuelve separando tres responsabilidades:
src/domain/— núcleo: entidades, reglas de negocio y ports (interfaces que el dominio necesita)src/domain/entities/— modelos y reglas purassrc/domain/ports/— contratos, como el repositorio de usuariossrc/application/— casos de uso (orquestación)src/application/services/— coordinan los ports para cumplir un caso de usosrc/infrastructure/— adaptadores concretossrc/infrastructure/db/— implementación del port de base de datossrc/infrastructure/http/— controllerssrc/infrastructure/external/— clientes de APIs externas
La clave: el dominio define ports (qué necesita), y la infraestructura define adapters (cómo lo hace). Si mañana cambiás Postgres por otra DB, solo cambiás el adapter.
// domain/ports/UsuarioRepository.ts
export interface UsuarioRepository {
guardar(usuario: Usuario): Promise<void>
buscarPorEmail(email: string): Promise<Usuario | null>
}
// infrastructure/db/PostgresUsuarioRepository.ts
export class PostgresUsuarioRepository implements UsuarioRepository {
async guardar(usuario: Usuario) {
// implementación con pg
}
async buscarPorEmail(email: string) {
// implementación con pg
}
}
Microservicios: la trampa más cara del rubro
Los microservicios resolvieron un problema real en empresas como Netflix y Amazon a fines de los 2000 y principios de los 2010. Ese problema era: monolitos de millones de líneas que cientos de ingenieros tocaban a la vez, con deploys coordinados durante meses. La solución fue separar servicios chicos, independientes, con deploys independientes.
El problema es que los equipos chicos no tienen ese problema. Si tenés 5 ingenieros y un monolito bien modularizado, los microservicios te van a traer:
- Latencia de red entre servicios.
- Consistencia eventual que es difícil de razonar.
- Deploys coordinados que terminan siendo sincronizados igual.
- Complejidad operativa (múltiples DBs, tracing distribuido, versionado de APIs).
Regla que repetimos: no hagas microservicios hasta que el monolito te esté doliendo de verdad. Y cuando te duela, empezá sacando uno solo.
La guía práctica para elegir
-
¿El sistema es chico y un solo equipo lo mantiene? Elegí monolito modular (capas o hexagonal). Simpleza gana.
-
¿El sistema creció y los deploys se vuelven una coordinación dolorosa? Sacá un servicio, no todos. Empezá por el más aislado.
-
¿Tenés múltiples equipos con deploys independientes y dominios distintos? Ahí sí entran los microservicios, con boundaries claros por dominio (DDD).
-
¿Estás en una startup validando producto? Monolito. Siempre. Cambiá cuando duela.
Lo que no es arquitectura
- La elección de framework (Next, Nest, Express) no es arquitectura, es tooling.
- La estructura de carpetas no es arquitectura, es organización.
- Los diagramas C4 o UML no son arquitectura, son documentación de la arquitectura.
La arquitectura vive en cómo se relacionan los módulos. Si cambiás un módulo y rompés tres no relacionados, hay un problema de arquitectura.
La mejor arquitectura es la que te permite hacer el cambio que tu negocio necesita el mes que viene sin reescribir medio sistema. Ni más ni menos.
Cuando empieces un proyecto nuevo, no te obsesiones con el patrón correcto. Empezá con capas simples. Cuando algo duela, refactorizá hacia el patrón que resuelve ese dolor. La arquitectura emerge de decisiones sucesivas, no del primer commit.