Arda Akgür
La mayoría de las ideas en arquitectura de software no aparecen completamente formadas de la nada.
Un principio que un ingeniero aprende toma otra forma cuando se enfrenta a otro problema. A veces una idea pequeña crece durante años —se aplica, se rompe, se rediseña— y al final se convierte en su propio enfoque arquitectónico.
Así empezó la historia de ARC-DDD.
Empecé a desarrollar las primeras versiones de este enfoque mientras trabajaba como Associate Engineer en Australia.
Pero cuando explico de dónde surgió la idea, necesito dar crédito especialmente a una persona:
Victorian Software CEO Adrian Budiman.
Adrian fue la primera persona que me mostró la idea de dividir un sistema en dominios separados horizontalmente, en lugar de pensar en los dominios solo como capas arquitectónicas clásicas.
Esa idea se convirtió en mi punto de partida.
Más adelante llevé este enfoque mucho más lejos en sistemas que construí yo mismo.
Hoy ARC-DDD, o como a veces lo llamo Australian DDD, es el resultado de ese proceso.
El Domain-Driven Design clásico suele dividir un sistema en capas verticales:
Ese enfoque es extremadamente valioso, y ARC-DDD no intenta eliminarlo.
Lo que añadí es un segundo eje.
Mientras el sistema se divide en capas verticales, el modelo de dominio en sí también se divide en bounded contexts horizontales.
Por ejemplo:
Core├── Users├── Events├── Transactions├── Vendors└── PaymentsLa regla fundamental aquí es esta:
Un dominio no debería conocer el mundo completo de otro dominio.
Aquí es donde emerge el verdadero carácter de ARC-DDD.
Uno de los problemas más comunes que veo en sistemas grandes son los modelos de entidad enormes.
Por ejemplo, hay una sola clase User en el sistema.
Con el tiempo, todo se añade a ella:
PasswordPhoneAddressBalanceIBANKYCPermissionsProfileSecurityTransactionsPreferences...Luego el dominio Events empieza a usar el modelo User completo aunque solo quiera mostrar el nombre y la foto del usuario.
El dominio Transactions usa el mismo modelo para el balance y el IBAN.
El sistema Vendor usa el mismo modelo otra vez.
Al final, un sistema que en teoría parece separado por dominios se convierte, en la práctica, en una estructura gigante unida a través de las mismas entidades.
ARC-DDD toma un enfoque diferente aquí.
Por ejemplo:
Users.UserEvents.User2Transactions.User3Estos son modelos de dominio distintos.
Pero pueden representar la misma tabla física de base de datos.
Users.User ──┐Events.User2 ──┼──> Users tableTransactions.User3 ──┘Si el dominio Events solo necesita:
UserIdFirstNameFamilyNamePhotoUser2 contiene solo eso.
Si el dominio Transactions necesita:
UserIdBalanceIBANVerifiedcampos, entonces User3 se define según las necesidades de su propio dominio.
Cada dominio ve el mundo solo hasta donde lo necesita.
Ese es exactamente el enfoque central de ARC-DDD: los bounded contexts están separados y, en lugar de cargar el agregado completo de otro dominio, creas una proyección local al dominio que contiene los campos que necesitas. Cuando es necesario, esa proyección se mapea a la misma tabla física.
Una característica importante de ARC-DDD es que no trata las bases de datos separadas como obligatorias para crear bounded contexts.
Por ejemplo, con Entity Framework Core:
modelBuilder .Entity<User2>() .ToTable("Users") .HasKey(x => x.UserId);User2 en el dominio Events puede seguir enlazándose a la tabla física Users.
Pero como el modelo solo contiene las propiedades que el dominio Events necesita, ese dominio no arrastra campos de usuario innecesarios a su propio modelo.
En la implementación de referencia pública (eski-dev), el ejemplo de Goldtag muestra cómo User, User2 y User3 se enlazan a la misma tabla Users desde distintas perspectivas de dominio. User2 representa los datos y el comportamiento que Events necesita; User3 representa lo que Transactions necesita.
Esto nos da un equilibrio importante:
La base de datos puede compartirse físicamente, pero los modelos de dominio no tienen que compartirse.
Ese es uno de los principios centrales de ARC-DDD.
Esta separación no se detiene en el nivel de carpetas.
Cada bounded context tiene su propia estructura DbContext.
Por ejemplo:
UserDbContextEventsDbContextTransactionDbContextVendorsDbContextEl dominio Events conoce sus propios modelos de proyección.
El dominio Transactions conoce sus propios modelos.
El dominio Users posee el modelo canónico User.
En la implementación de referencia pública (eski-dev), por ejemplo, UserDbContext usa el modelo canónico User, EventsDbContext usa la proyección User2 y TransactionDbContext usa la proyección User3 dentro de su propio contexto.
Por esa razón, un dominio no importa directamente los modelos Core de otro dominio.
Dentro de Events:
using Gold.Core.Users;en lugar de tirar del agregado User completo hacia el sistema, Events crea su propia proyección.
Esta es una duplicación deliberada.
Porque en ARC-DDD, a veces:
Una pequeña cantidad de duplicación es más barata que un acoplamiento fuerte.
Una de las reglas centrales a nivel Core es que las carpetas de dominio no se importan entre sí directamente. Por ejemplo, en lugar de usar Gold.Core.Users, Events crea su propia representación local como User2.
En aplicaciones reales, los bounded contexts no pueden vivir en aislamiento completo.
Una sola operación puede necesitar varios dominios a la vez, como:
ARC-DDD no conecta aquí las capas de dominio entre sí. En su lugar, mueve la composición al borde exterior del sistema.
Por ejemplo:
API | +--> OrderingRepository | +--> PaymentsRepository | +--> UsersRepositoryEn otras palabras, la orquestación entre dominios ocurre en el nivel API o de aplicación.
Los dominios Core no se importan entre sí ni crean una cadena de dependencias.
En eski-dev también, la composición entre dominios se hace del lado de la API. Un controller o un application service puede trabajar con varios repositorios cuando hace falta, pero los dominios Core no están cableados entre sí.
Esta separación se vuelve muy valiosa a medida que el sistema crece.
Si hoy creara un nuevo sistema ARC-DDD, mantendría la estructura básica bastante simple:
Product.CoreProduct.DomainProduct.ApiDirección de dependencias:
Product.Api ↓Product.Domain ↓Product.CoreCore contiene modelos de dominio puros.
No tiene dependencia de Entity Framework ni de infraestructura.
Domain contiene la capa de persistencia, DbContexts y estructuras de repositorio.
Api actúa como composition root y orquesta distintos bounded contexts dentro del mismo caso de uso cuando hace falta.
La dirección de dependencias en la estructura de referencia actual de eski-dev también es Gold.Api → Gold.Domain → Gold.Core. Mantener la capa Core libre de paquetes de infraestructura es una de las reglas fundamentales.
Si hoy tuviera que describir esta arquitectura en una sola frase:
ARC-DDD es un enfoque de arquitectura de software que amplía el DDD clásico por capas con bounded contexts horizontales y proyecciones de entidades específicas de dominio sobre tablas compartidas.
Lo defino de forma más técnica como:
DDD clásico por capas + carpetas de bounded context horizontales en Core + proyecciones de entidades por contexto sobre tablas compartidas.
Pero para mí, la idea más importante detrás de este enfoque es esta:
Un dominio no debería ver todo el conocimiento que existe en el sistema — solo el mundo que necesita para hacer su propio trabajo.
Porque para mí, el origen del enfoque se remonta a Australia.
La idea de separación horizontal de dominios que aprendí de Adrian Budiman mientras trabajaba como Associate Engineer fue el punto de partida.
Más adelante expandí esa idea aplicándola en mis propios sistemas — con modelos de entidad locales al dominio, proyecciones sobre tablas compartidas, DbContexts por dominio y principios de composición controlada entre dominios.
Así que cuando hablo de ARC-DDD, no creo que sea correcto presentarlo como "inventé todo desde cero".
La ingeniería, de todos modos, no suele avanzar así.
Alguien te da una idea fuerte.
La aplicas a otros problemas.
Ves lo que falta.
Añades nuevos principios encima.
Y años después, surge un sistema bastante distinto de la idea original.
Para mí, ARC-DDD se desarrolló exactamente así.
El pensamiento horizontal de dominio que Adrian me mostró fue la chispa.
Mi contribución fue convertir esa chispa en un modelo arquitectónico práctico que hoy llamo ARC-DDD.
ARC-DDD no es solo un enfoque arquitectónico que describo en teoría.
Para quien quiera ver cómo se aplica el enfoque dentro de un sistema real, he publicado públicamente una de mis codebases de desarrollo más antiguas:
GitHub:
https://github.com/Arcnaboo/eski-dev
El repositorio es público, basado en C# y publicado bajo la licencia MIT.
Este repositorio es la Goldtag legacy backend reference implementation pública de ARC-DDD.
El repositorio define explícitamente la implementación de referencia como:
Gold.CoreGold.DomainGold.ApiDentro del repositorio, recomiendo especialmente mirar estos modelos:
Gold.Core/Users/User.csGold.Core/Events/User2.csGold.Core/Transactions/User3.csEstos muestran directamente la idea más importante de ARC-DDD.
El User canónico pertenece al bounded context Users.
Events usa un modelo User2 acotado y moldeado para sus propias necesidades sobre el mismo usuario.
Transactions usa un modelo User3 adecuado a sus propias operaciones.
Puedes ver el mismo enfoque en modelos como Event y Event2, Notification y Notification2. Mira especialmente estos archivos en el repositorio para aprender la implementación de referencia.
Aquí hay que hacer una distinción importante.
Este repositorio lleva las huellas de una codebase de producción antigua y real.
Así que no usaría cada detalle de implementación uno a uno en un sistema que escribiera desde cero hoy.
Por ejemplo, enfoques antiguos de connection strings, algunas estructuras de controller, un uso incompleto de dependency injection y piezas legacy que ya no hacen falta no son ARC-DDD en sí.
Señalo especialmente esta diferencia:
Toma las ideas, moderniza la mecánica.
La idea que permanece:
Bounded contexts horizontales + proyecciones por dominio + DbContexts por dominio.
No los detalles históricos de implementación del código antiguo.
No creo que baste explicar un enfoque arquitectónico solo con diagramas.
La gente necesita poder:
Por eso quiero que tanto la documentación arquitectónica como una implementación de referencia pública real de ARC-DDD sigan siendo accesibles.
Repositorio:
https://github.com/Arcnaboo/eski-dev
Pero no quiero que copies cada línea del repositorio para entender ARC-DDD.
Todo lo contrario.
Separa las partes que vienen de la edad del código.
Mira los límites de dominio.
Mira cómo la misma entidad física se modela de forma distinta en distintos bounded contexts.
Mira cómo se separan los contextos.
Mira por qué Core no importa los modelos de otros dominios.
Porque la esencia de ARC-DDD no es un framework ni una tecnología específica.
Entity Framework puede cambiar.
.NET puede cambiar.
La base de datos puede cambiar.
El patrón repository puede cambiar.
Dentro de años, la mayoría de las herramientas que usamos hoy también pueden haber cambiado.
Pero el principio fundamental no cambia:
Haz que los límites de dominio sean límites reales en el código.
Y muestra a cada dominio solo el mundo que necesita.