Skip to main content
Andrés Carmona Gil
Desarrollador & Creador de Contenido
View all authors

Arquitectura de cartón piedra: cuando el código parece limpio, pero no hay quien lo entienda

· 6 min read
Andrés Carmona Gil
Desarrollador & Creador de Contenido

Arquitectura de cartón piedra: cuando el código parece limpio, pero no hay quien lo entienda

Hay proyectos que, al abrirlos, dan muy buena impresión:

  • Carpetas bien puestas: core, domain, infrastructure, services, helpers
  • Clases con nombres serios: UserService, OrderManager, PaymentHandler
  • Documentación extensa, diagramas, README detallado…

Y sin embargo, cuando intentas seguir cómo funciona realmente el sistema, te encuentras con algo muy distinto: métodos que llaman a otros métodos que llaman a otros métodos, capas que solo delegan, lógica de negocio repartida por todo el repositorio… y tú pensando:

“Esto parece muy arquitectónico, pero entenderlo es un dolor.”

A eso es a lo que me gusta llamar arquitectura de cartón piedra: por fuera parece sólida, pero por dentro no sostiene bien el peso del negocio.


¿Qué es la “arquitectura de cartón piedra”?

No es un término oficial, pero describe muy bien una situación bastante común:

Arquitectura de cartón piedra: cuando se construye una estructura de carpetas y capas que simula una arquitectura limpia, pero en la práctica no ayuda a entender el flujo ni a mantener el sistema.

Algunos síntomas típicos:

  • Abres el proyecto y ves mil capas, pero no sabes dónde vive el negocio.

  • Para seguir un caso de uso sencillo (por ejemplo, crear un pedido) tienes que abrir 8–10 ficheros.

  • La mayoría de las clases son pasamanos:

    • ControllerManagerHandlerServiceProcessorRepository… y cada uno solo delega al siguiente.
  • La lógica de negocio está fragmentada:

    • un trozo en el controller,
    • otro en un helper,
    • otro en el repositorio,
    • otro en un “util” genérico.

Puede haber documentación muy buena… pero si la estructura del código no refleja bien el negocio, la arquitectura no está cumpliendo su función.


¿Por qué pasa esto?

Normalmente se mezclan varias cosas:

  1. Diseñar por carpeta, no por dominio Se copia una estructura “de libro” (o de otro proyecto) sin pensar si encaja con el negocio real.

  2. Confundir “más capas” con “mejor arquitectura”

    • Se añaden capas tipo Manager, Handler, Processor, etc.
    • Pero muchas veces esas capas no encapsulan nada nuevo, solo complican el recorrido.
  3. Cargo cult de patrones Se aplican etiquetas como “Clean Architecture”, “DDD”, “Hexagonal”… pero solo en lo superficial: nombres de carpetas y clases, sin respetar los principios.

  4. Miedo a equivocarse Se crean capas “por si acaso las necesitamos luego”, y al final se acumulan niveles de indirección innecesarios.


Cómo se vive trabajar en un proyecto así

Si has caído en uno de estos proyectos, probablemente te suena:

  • Para hacer un cambio pequeño tienes que tocar código en medio repositorio.
  • No está claro dónde debería ir una nueva regla de negocio.
  • Refactorizar da miedo porque no entiendes bien el flujo completo.
  • La documentación se vuelve obligatoria porque el código, por sí solo, no se explica.

Una arquitectura sana debería ser casi lo contrario:

Aunque no haya documentación perfecta, deberías intuir dónde están las cosas con solo ver la estructura básica.


¿Cómo es una arquitectura que ayuda de verdad?

Una buena arquitectura no va de tener muchas capas, sino de que:

1. El flujo sea fácil de seguir

Ejemplo muy simple:

HTTP Request → Controller → Caso de uso → Repositorios/Adaptadores → Respuesta

Es decir, puedes explicar qué pasa con una frase y unos pocos ficheros.

2. La lógica de negocio esté concentrada en sitios previsibles

Por ejemplo:

  • CreateOrderUseCase
  • ReserveSeatService
  • CalculatePriceDomainService

Y no escondida en helpers genéricos o en métodos random de un repositorio.

3. Las capas existan por una razón clara

Un esquema típico (y suficiente en muchos casos) podría ser:

  • Interface / API: controllers, DTOs, validaciones de entrada.
  • Application: casos de uso, orquestación de procesos.
  • Domain: entidades, reglas de negocio puras.
  • Infrastructure: repositorios, acceso a bases de datos, APIs externas, colas, etc.

La clave es que, si alguien te pregunta:

“¿Dónde está la lógica de reservar un curso?”

puedas responder algo tipo:

“Mira en application/ReserveCourse.ts y en domain/CourseReservation.”

Sin tener que hacer un “buscar en todo el repo”.


Checklist rápido: ¿huele a cartón piedra?

Algunas preguntas útiles cuando revisas un proyecto:

  1. ¿Cuántos ficheros tengo que abrir para seguir un caso de uso sencillo?

    • Si son más de 5–6, sospecha.
  2. ¿Cuántas clases solo delegan sin añadir lógica nueva?

    • Si la mayoría son “pasamanos”, sobran capas.
  3. ¿Sé claramente dónde vive el negocio?

    • ¿Hay un sitio claro para los casos de uso?
  4. ¿La estructura está basada en “features” o en “capas genéricas”?

    • orders/, payments/, users/ suele ser más útil que managers/, helpers/, processors/.
  5. ¿Qué pasa si el “desarrollador héroe” no está?

    • ¿El equipo puede mantener el sistema sin él/ella?

Cuantas más respuestas incómodas, más probable que estés delante de una arquitectura de cartón piedra.


Cómo mejorar sin reescribirlo todo

No siempre puedes tirar el proyecto y empezar de cero (de hecho, casi nunca). Pero sí puedes mejorar poco a poco:

1. Elegir un caso de uso clave y mapearlo

Por ejemplo: “crear pedido”.

  • Dibuja el flujo actual (aunque sea en un papel) desde el endpoint hasta la base de datos.
  • Identifica capas que no aportan nada.

2. Concentrar la lógica de negocio

  • Crea un CreateOrderUseCase o similar.
  • Ve moviendo reglas dispersas (en controller, helpers, etc.) hacia ese caso de uso.

3. Eliminar pasamanos innecesarios

  • Si un Manager solo llama a un Service sin añadir nada, fusiónalos.
  • Menos capas, más claridad.

4. Nombrar por dominio, no por patrón

Mejor:

  • AssignSeatToPassenger
  • CalculateInvoiceTotals

que:

  • SeatManager
  • InvoiceProcessor

5. Documentar el flujo, no solo la arquitectura

Un diagrama sencillo del tipo:

flowchart LR
A[HTTP Request] --> B[OrderController]
B --> C[CreateOrderUseCase]
C --> D[OrderRepository]

vale oro si realmente refleja lo que pasa.


Cerrar el círculo

Al final, la reflexión es sencilla:

  • Arquitectura no es cuántas carpetas tienes, sino cuánto te ayuda a pensar y cambiar el negocio.
  • Documentar está genial, pero la mejor documentación es un código y una estructura que “se explican solos”.
  • Y un desarrollador realmente senior no es solo el que desarrolla rápido, sino el que deja un sistema que cualquier persona del equipo puede entender y seguir ampliando.

Si al abrir un proyecto tienes la sensación de que todo está muy “bonito”, pero no puedes seguir el flujo sin sufrir, probablemente no eres tú: es cartón piedra.

Y la buena noticia es que, en cuanto empiezas a desarrollar ese olfato, también empiezas a diseñar tus propios proyectos de forma distinta: menos postureo, más claridad, más negocio… y menos dolor para el siguiente que venga detrás (aunque ese siguiente seas tú dentro de seis meses).


Si quieres, en otro post podemos hacer el “modo práctico”: coger un caso de uso real tuyo y enseñar un antes vs después de refactor, con código.

Cardboard Architecture: when code looks clean but nobody understands it

· 5 min read
Andrés Carmona Gil
Desarrollador & Creador de Contenido

Cardboard Architecture: when code looks clean but nobody understands it

Some projects give a great first impression when you open them:

  • Well-organized folders: core, domain, infrastructure, services, helpers
  • Classes with serious names: UserService, OrderManager, PaymentHandler
  • Extensive documentation, diagrams, detailed README…

And yet, when you try to follow how the system actually works, you find something very different: methods calling other methods calling other methods, layers that only delegate, business logic scattered across the repository… and you thinking:

“This looks architectural, but understanding it is painful.”

That’s what I like to call cardboard architecture: it looks solid on the outside but doesn’t really hold the weight of the business inside.


What is “cardboard architecture”?

It’s not an official term, but it describes a very common situation well:

Cardboard architecture: when you build a folder-and-layer structure that simulates clean architecture, but in practice doesn’t help you understand the flow or maintain the system.

Typical symptoms:

  • You open the project and see a thousand layers, but you don’t know where the business logic lives.

  • To follow a simple use case (e.g., create an order) you need to open 8–10 files.

  • Most classes are pass-throughs:

    • ControllerManagerHandlerServiceProcessorRepository… and each one just delegates to the next.
  • Business logic is fragmented:

    • a bit in the controller,
    • another in a helper,
    • another in the repository,
    • another in a generic util.

There may be excellent documentation… but if the code structure doesn’t reflect the business, the architecture fails its purpose.


Why does this happen?

Usually several things mix together:

  1. Designing by folder, not by domain Copying a “textbook” structure (or another project) without thinking if it fits the real business.

  2. Confusing “more layers” with “better architecture”

    • Adding layers like Manager, Handler, Processor, etc.
    • But often those layers don’t encapsulate anything new, they just complicate the path.
  3. Cargo-culting patterns Applying labels like “Clean Architecture”, “DDD”, “Hexagonal”… but only superficially: folder and class names, not the principles.

  4. Fear of being wrong Creating layers “in case we need them later”, and ending up with accumulated unnecessary indirection levels.


What it feels like to work on such a project

If you’ve landed in one of these projects, this will sound familiar:

  • To make a small change you have to touch code across half the repo.
  • It’s unclear where a new business rule should live.
  • Refactoring is scary because you don’t fully understand the whole flow.
  • Documentation becomes mandatory because the code alone doesn’t explain itself.

A healthy architecture should be almost the opposite:

Even without perfect docs, you should be able to guess where things live just by looking at the basic structure.


What does an actually helpful architecture look like?

Good architecture isn’t about having many layers, it’s about:

1. The flow being easy to follow

Very simple example:

HTTP Request → Controller → Use Case → Repositories/Adapters → Response

Meaning you can explain what happens in one sentence and a few files.

2. Business logic concentrated in predictable places

For example:

  • CreateOrderUseCase
  • ReserveSeatService
  • CalculatePriceDomainService

Not hidden in generic helpers or random repository methods.

3. Layers existing for a clear reason

A typical (and often sufficient) layout could be:

  • Interface / API: controllers, DTOs, input validation.
  • Application: use cases, process orchestration.
  • Domain: entities, pure business rules.
  • Infrastructure: repositories, DB access, external APIs, queues, etc.

The key is that if someone asks:

“Where is the logic to reserve a course?”

you can answer:

“Look in application/ReserveCourse.ts and domain/CourseReservation.”

without doing a repo-wide search.


Quick checklist: does it smell like cardboard architecture?

Some useful questions when reviewing a project:

  1. How many files do I need to open to follow a simple use case?

    • If more than 5–6, be suspicious.
  2. How many classes only delegate without adding logic?

    • If most are passthroughs, you have too many layers.
  3. Do I clearly know where the business lives?

    • Is there a clear place for use cases?
  4. Is the structure feature-based or generic layers-based?

    • orders/, payments/, users/ is often more useful than managers/, helpers/, processors/.
  5. What happens if the “hero developer” is not available?

    • Can the team maintain the system without them?

The more uncomfortable answers, the more likely you’re facing cardboard architecture.


How to improve without rewriting everything

You rarely can throw away the project and start over (in fact, almost never). But you can improve incrementally:

1. Pick a key use case and map it

Example: “create order”.

  • Draw the current flow (even on paper) from endpoint to DB.
  • Identify layers that contribute nothing.

2. Concentrate business logic

  • Create a CreateOrderUseCase or similar.
  • Move scattered rules (in controllers, helpers, etc.) into that use case.

3. Remove unnecessary pass-throughs

  • If a Manager only calls a Service without adding value, merge them.
  • Fewer layers, more clarity.

4. Name by domain, not by pattern

Better:

  • AssignSeatToPassenger
  • CalculateInvoiceTotals

than:

  • SeatManager
  • InvoiceProcessor

5. Document the flow, not only the architecture

A simple diagram like:

flowchart LR
A[HTTP Request] --> B[OrderController]
B --> C[CreateOrderUseCase]
C --> D[OrderRepository]

is gold if it truly reflects what happens.


Closing the loop

The takeaway is simple:

  • Architecture is not how many folders you have, it’s how much it helps you think about and change the business.
  • Documentation is great, but the best documentation is code and structure that “explain themselves”.
  • A truly senior developer is not just fast, but the one who leaves a system any teammate can understand and extend.

If a project looks pretty but you can’t follow the flow without suffering, it’s probably not you: it’s cardboard.

And the good news: once you develop that sense, you start designing your own projects differently: less posture, more clarity, more business focus… and less pain for whoever comes next (even if that’s you in six months).


If you want, in another post we can do the “practical mode”: take a real use case of yours and show a before vs after refactor, with code.

Mastra vs Agent Framework + Azure AI Foundry

· 7 min read
Andrés Carmona Gil
Desarrollador & Creador de Contenido

Two ways to build production AI agents

In recent months many frameworks for building AI agents have emerged. Two approaches I find particularly interesting are:

  • Mastra, an all-TypeScript agent framework that feels natural if your stack is Node-based.
  • Microsoft Agent Framework + Azure AI Foundry (Azure AI Agents), Microsoft's offering that pairs an orchestration library with a managed agent service on Azure.

At first glance they look like different worlds, but they share a similar mental model: agents that keep conversations via threads/sessions, messages, and runs.

Welcome to my blog!

· 2 min read
Andrés Carmona Gil
Desarrollador & Creador de Contenido

Hello and welcome to my personal blog! 👋

This is the first post of many to come. Here I'll share my experiences, learnings, and reflections about technology, software development, and everything I'm passionate about.