@archi-code/ddd 0.0.0-stage → 3.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/LICENSE +46 -0
  2. package/README.md +2693 -2
  3. package/SKILL.md +104 -0
  4. package/ddd-skills/archi-code-ddd/imports.md +84 -0
  5. package/ddd-skills/archi-code-ddd/layers.md +299 -0
  6. package/ddd-skills/archi-code-ddd/patterns.md +676 -0
  7. package/ddd-skills/archi-code-ddd/what-not-to-do.md +189 -0
  8. package/ddd-skills/archi-code-ddd/where-each-class-goes.md +63 -0
  9. package/ddd-skills/ddd-agregados/GUIA.md +114 -0
  10. package/ddd-skills/ddd-agregados/references/consistencia-eventual.md +159 -0
  11. package/ddd-skills/ddd-agregados/references/ejemplo-ecommerce.md +52 -0
  12. package/ddd-skills/ddd-agregados/references/fabricas.md +196 -0
  13. package/ddd-skills/ddd-agregados/references/implementacion-ts.md +711 -0
  14. package/ddd-skills/ddd-arquitectura/GUIA.md +80 -0
  15. package/ddd-skills/ddd-arquitectura/assets/dependency-cruiser.cjs +92 -0
  16. package/ddd-skills/ddd-arquitectura/references/estilos-adicionales.md +51 -0
  17. package/ddd-skills/ddd-arquitectura/references/estructura-hexagonal-ts.md +352 -0
  18. package/ddd-skills/ddd-arquitectura/references/modulos.md +48 -0
  19. package/ddd-skills/ddd-auditoria-refactorizacion/GUIA.md +85 -0
  20. package/ddd-skills/ddd-auditoria-refactorizacion/references/ejemplo-auditoria.md +316 -0
  21. package/ddd-skills/ddd-auditoria-refactorizacion/references/lista-comprobacion.md +54 -0
  22. package/ddd-skills/ddd-auditoria-refactorizacion/references/plan-migracion.md +73 -0
  23. package/ddd-skills/ddd-auditoria-refactorizacion/scripts/scan_ddd_smells.py +264 -0
  24. package/ddd-skills/ddd-context-mapping/GUIA.md +83 -0
  25. package/ddd-skills/ddd-context-mapping/references/anticorrupcion-ts.md +404 -0
  26. package/ddd-skills/ddd-context-mapping/references/estilos-integracion.md +155 -0
  27. package/ddd-skills/ddd-event-sourcing-cqrs/GUIA.md +92 -0
  28. package/ddd-skills/ddd-event-sourcing-cqrs/references/event-sourcing-ts.md +471 -0
  29. package/ddd-skills/ddd-event-sourcing-cqrs/references/proyecciones-cqrs-ts.md +285 -0
  30. package/ddd-skills/ddd-eventos-dominio/GUIA.md +106 -0
  31. package/ddd-skills/ddd-eventos-dominio/references/diseno-eventos.md +131 -0
  32. package/ddd-skills/ddd-eventos-dominio/references/ejemplo-ecommerce.md +240 -0
  33. package/ddd-skills/ddd-eventos-dominio/references/publicacion-ts.md +526 -0
  34. package/ddd-skills/ddd-orquestador/GUIA.md +40 -0
  35. package/ddd-skills/ddd-orquestador/references/chuleta-reglas.md +35 -0
  36. package/ddd-skills/ddd-repositorios-persistencia/GUIA.md +121 -0
  37. package/ddd-skills/ddd-repositorios-persistencia/references/implementacion-ts.md +296 -0
  38. package/ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md +177 -0
  39. package/ddd-skills/ddd-repositorios-persistencia/references/transacciones-ts.md +294 -0
  40. package/ddd-skills/ddd-servicios-capa-aplicacion/GUIA.md +99 -0
  41. package/ddd-skills/ddd-servicios-capa-aplicacion/references/capa-aplicacion-ts.md +264 -0
  42. package/ddd-skills/ddd-servicios-capa-aplicacion/references/ejemplo-ecommerce.md +324 -0
  43. package/ddd-skills/ddd-servicios-capa-aplicacion/references/servicios-dominio-ts.md +191 -0
  44. package/ddd-skills/ddd-value-objects-entidades/GUIA.md +118 -0
  45. package/ddd-skills/ddd-value-objects-entidades/references/ejemplo-ecommerce.md +87 -0
  46. package/ddd-skills/ddd-value-objects-entidades/references/entidades-ts.md +220 -0
  47. package/ddd-skills/ddd-value-objects-entidades/references/persistencia-valores.md +88 -0
  48. package/ddd-skills/ddd-value-objects-entidades/references/specification-y-diseno-flexible.md +119 -0
  49. package/ddd-skills/ddd-value-objects-entidades/references/validacion.md +96 -0
  50. package/ddd-skills/ddd-value-objects-entidades/references/value-objects-ts.md +377 -0
  51. package/dist/cjs/application/event/event-bus.d.ts +4 -0
  52. package/dist/cjs/application/event/event-bus.js +2 -0
  53. package/dist/cjs/application/event/event-listener.d.ts +4 -0
  54. package/dist/cjs/application/event/event-listener.js +6 -0
  55. package/dist/cjs/application/event/listens-to.d.ts +15 -0
  56. package/dist/cjs/application/event/listens-to.js +28 -0
  57. package/dist/cjs/application/index.d.ts +7 -0
  58. package/dist/cjs/application/index.js +9 -0
  59. package/dist/cjs/application/ports/clock.d.ts +3 -0
  60. package/dist/cjs/application/ports/clock.js +2 -0
  61. package/dist/cjs/application/ports/logger.d.ts +8 -0
  62. package/dist/cjs/application/ports/logger.js +2 -0
  63. package/dist/cjs/application/use-case/use-case.d.ts +6 -0
  64. package/dist/cjs/application/use-case/use-case.js +24 -0
  65. package/dist/cjs/domain/aggregate/aggregate-root.d.ts +14 -0
  66. package/dist/cjs/domain/aggregate/aggregate-root.js +33 -0
  67. package/dist/cjs/domain/entity/entity.d.ts +9 -0
  68. package/dist/cjs/domain/entity/entity.js +19 -0
  69. package/dist/cjs/domain/event/event-base.d.ts +29 -0
  70. package/dist/cjs/domain/event/event-base.js +51 -0
  71. package/dist/cjs/domain/index.d.ts +10 -0
  72. package/dist/cjs/domain/index.js +24 -0
  73. package/dist/cjs/domain/primitive/primitive-types.d.ts +22 -0
  74. package/dist/cjs/domain/primitive/primitive-types.js +2 -0
  75. package/dist/cjs/domain/repository/aggregate-finder.d.ts +4 -0
  76. package/dist/cjs/domain/repository/aggregate-finder.js +2 -0
  77. package/dist/cjs/domain/repository/aggregate-remover.d.ts +4 -0
  78. package/dist/cjs/domain/repository/aggregate-remover.js +2 -0
  79. package/dist/cjs/domain/repository/aggregate-saver.d.ts +4 -0
  80. package/dist/cjs/domain/repository/aggregate-saver.js +2 -0
  81. package/dist/cjs/domain/specification/specification.d.ts +9 -0
  82. package/dist/cjs/domain/specification/specification.js +44 -0
  83. package/dist/cjs/domain/type/abstract-type.d.ts +20 -0
  84. package/dist/cjs/domain/type/abstract-type.js +69 -0
  85. package/dist/cjs/domain/type/array-type.d.ts +16 -0
  86. package/dist/cjs/domain/type/array-type.js +65 -0
  87. package/dist/cjs/domain/type/boolean-type.d.ts +4 -0
  88. package/dist/cjs/domain/type/boolean-type.js +16 -0
  89. package/dist/cjs/domain/type/composite/composite-field.d.ts +19 -0
  90. package/dist/cjs/domain/type/composite/composite-field.js +63 -0
  91. package/dist/cjs/domain/type/composite/composite.d.ts +26 -0
  92. package/dist/cjs/domain/type/composite/composite.js +55 -0
  93. package/dist/cjs/domain/type/date-type.d.ts +18 -0
  94. package/dist/cjs/domain/type/date-type.js +35 -0
  95. package/dist/cjs/domain/type/enum-type.d.ts +7 -0
  96. package/dist/cjs/domain/type/enum-type.js +30 -0
  97. package/dist/cjs/domain/type/equality/value-equality.d.ts +7 -0
  98. package/dist/cjs/domain/type/equality/value-equality.js +41 -0
  99. package/dist/cjs/domain/type/id-type.d.ts +7 -0
  100. package/dist/cjs/domain/type/id-type.js +17 -0
  101. package/dist/cjs/domain/type/immutable/immutable-date.d.ts +4 -0
  102. package/dist/cjs/domain/type/immutable/immutable-date.js +37 -0
  103. package/dist/cjs/domain/type/immutable/immutable.d.ts +4 -0
  104. package/dist/cjs/domain/type/immutable/immutable.js +19 -0
  105. package/dist/cjs/domain/type/index.d.ts +17 -0
  106. package/dist/cjs/domain/type/index.js +33 -0
  107. package/dist/cjs/domain/type/json-type.d.ts +5 -0
  108. package/dist/cjs/domain/type/json-type.js +17 -0
  109. package/dist/cjs/domain/type/number-type.d.ts +4 -0
  110. package/dist/cjs/domain/type/number-type.js +16 -0
  111. package/dist/cjs/domain/type/required/required-mark.d.ts +9 -0
  112. package/dist/cjs/domain/type/required/required-mark.js +13 -0
  113. package/dist/cjs/domain/type/required/required.d.ts +7 -0
  114. package/dist/cjs/domain/type/required/required.js +21 -0
  115. package/dist/cjs/domain/type/string-type.d.ts +4 -0
  116. package/dist/cjs/domain/type/string-type.js +16 -0
  117. package/dist/cjs/domain/type/uuid-type.d.ts +7 -0
  118. package/dist/cjs/domain/type/uuid-type.js +27 -0
  119. package/dist/cjs/domain/type/value-objects/value-objects.d.ts +16 -0
  120. package/dist/cjs/domain/type/value-objects/value-objects.js +70 -0
  121. package/dist/cjs/exceptions/abstract.exception.d.ts +23 -0
  122. package/dist/cjs/exceptions/abstract.exception.js +66 -0
  123. package/dist/cjs/exceptions/application/aggregate-not-found.exception.d.ts +5 -0
  124. package/dist/cjs/exceptions/application/aggregate-not-found.exception.js +17 -0
  125. package/dist/cjs/exceptions/application/application.exception.d.ts +6 -0
  126. package/dist/cjs/exceptions/application/application.exception.js +17 -0
  127. package/dist/cjs/exceptions/application/concurrency.exception.d.ts +5 -0
  128. package/dist/cjs/exceptions/application/concurrency.exception.js +17 -0
  129. package/dist/cjs/exceptions/application/not-allowed.exception.d.ts +5 -0
  130. package/dist/cjs/exceptions/application/not-allowed.exception.js +17 -0
  131. package/dist/cjs/exceptions/brand/exception-brand.d.ts +6 -0
  132. package/dist/cjs/exceptions/brand/exception-brand.js +29 -0
  133. package/dist/cjs/exceptions/domain/domain.exception.d.ts +6 -0
  134. package/dist/cjs/exceptions/domain/domain.exception.js +17 -0
  135. package/dist/cjs/exceptions/domain/required-value.exception.d.ts +4 -0
  136. package/dist/cjs/exceptions/domain/required-value.exception.js +21 -0
  137. package/dist/cjs/exceptions/domain/type-primitive.exception.d.ts +4 -0
  138. package/dist/cjs/exceptions/domain/type-primitive.exception.js +21 -0
  139. package/dist/cjs/exceptions/domain/validation.exception.d.ts +14 -0
  140. package/dist/cjs/exceptions/domain/validation.exception.js +23 -0
  141. package/dist/cjs/exceptions/exception-code.d.ts +15 -0
  142. package/dist/cjs/exceptions/exception-code.js +19 -0
  143. package/dist/cjs/exceptions/index.d.ts +14 -0
  144. package/dist/cjs/exceptions/index.js +30 -0
  145. package/dist/cjs/exceptions/infrastructure/event-delivery.exception.d.ts +5 -0
  146. package/dist/cjs/exceptions/infrastructure/event-delivery.exception.js +20 -0
  147. package/dist/cjs/exceptions/infrastructure/infrastructure.exception.d.ts +6 -0
  148. package/dist/cjs/exceptions/infrastructure/infrastructure.exception.js +17 -0
  149. package/dist/cjs/exceptions/infrastructure/internal-error.exception.d.ts +5 -0
  150. package/dist/cjs/exceptions/infrastructure/internal-error.exception.js +17 -0
  151. package/dist/cjs/exceptions/infrastructure/unknown-event.exception.d.ts +4 -0
  152. package/dist/cjs/exceptions/infrastructure/unknown-event.exception.js +19 -0
  153. package/dist/cjs/exceptions/received/received-value.d.ts +4 -0
  154. package/dist/cjs/exceptions/received/received-value.js +13 -0
  155. package/dist/cjs/infrastructure/clock/fixed-clock.d.ts +8 -0
  156. package/dist/cjs/infrastructure/clock/fixed-clock.js +19 -0
  157. package/dist/cjs/infrastructure/clock/system-clock.d.ts +4 -0
  158. package/dist/cjs/infrastructure/clock/system-clock.js +9 -0
  159. package/dist/cjs/infrastructure/event/domain-event-bus.d.ts +16 -0
  160. package/dist/cjs/infrastructure/event/domain-event-bus.js +60 -0
  161. package/dist/cjs/infrastructure/event/event-channel.d.ts +9 -0
  162. package/dist/cjs/infrastructure/event/event-channel.js +2 -0
  163. package/dist/cjs/infrastructure/event/event-registry.d.ts +9 -0
  164. package/dist/cjs/infrastructure/event/event-registry.js +43 -0
  165. package/dist/cjs/infrastructure/event/in-memory/emitter.d.ts +11 -0
  166. package/dist/cjs/infrastructure/event/in-memory/emitter.js +54 -0
  167. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.d.ts +16 -0
  168. package/dist/cjs/infrastructure/event/in-memory/in-memory-event-channel.js +51 -0
  169. package/dist/cjs/infrastructure/http/http-error-mapper.d.ts +23 -0
  170. package/dist/cjs/infrastructure/http/http-error-mapper.js +63 -0
  171. package/dist/cjs/infrastructure/index.d.ts +16 -0
  172. package/dist/cjs/infrastructure/index.js +21 -0
  173. package/dist/cjs/infrastructure/logging/console-logger.d.ts +17 -0
  174. package/dist/cjs/infrastructure/logging/console-logger.js +37 -0
  175. package/dist/cjs/infrastructure/logging/in-memory-logger.d.ts +15 -0
  176. package/dist/cjs/infrastructure/logging/in-memory-logger.js +27 -0
  177. package/dist/cjs/infrastructure/logging/log-serializer.d.ts +7 -0
  178. package/dist/cjs/infrastructure/logging/log-serializer.js +59 -0
  179. package/dist/cjs/infrastructure/logging/silent-logger.d.ts +7 -0
  180. package/dist/cjs/infrastructure/logging/silent-logger.js +18 -0
  181. package/dist/esm/application/event/event-bus.d.ts +4 -0
  182. package/dist/esm/application/event/event-bus.js +1 -0
  183. package/dist/esm/application/event/event-listener.d.ts +4 -0
  184. package/dist/esm/application/event/event-listener.js +2 -0
  185. package/dist/esm/application/event/listens-to.d.ts +15 -0
  186. package/dist/esm/application/event/listens-to.js +25 -0
  187. package/dist/esm/application/index.d.ts +7 -0
  188. package/dist/esm/application/index.js +3 -0
  189. package/dist/esm/application/ports/clock.d.ts +3 -0
  190. package/dist/esm/application/ports/clock.js +1 -0
  191. package/dist/esm/application/ports/logger.d.ts +8 -0
  192. package/dist/esm/application/ports/logger.js +1 -0
  193. package/dist/esm/application/use-case/use-case.d.ts +6 -0
  194. package/dist/esm/application/use-case/use-case.js +20 -0
  195. package/dist/esm/domain/aggregate/aggregate-root.d.ts +14 -0
  196. package/dist/esm/domain/aggregate/aggregate-root.js +29 -0
  197. package/dist/esm/domain/entity/entity.d.ts +9 -0
  198. package/dist/esm/domain/entity/entity.js +15 -0
  199. package/dist/esm/domain/event/event-base.d.ts +29 -0
  200. package/dist/esm/domain/event/event-base.js +47 -0
  201. package/dist/esm/domain/index.d.ts +10 -0
  202. package/dist/esm/domain/index.js +6 -0
  203. package/dist/esm/domain/primitive/primitive-types.d.ts +22 -0
  204. package/dist/esm/domain/primitive/primitive-types.js +1 -0
  205. package/dist/esm/domain/repository/aggregate-finder.d.ts +4 -0
  206. package/dist/esm/domain/repository/aggregate-finder.js +1 -0
  207. package/dist/esm/domain/repository/aggregate-remover.d.ts +4 -0
  208. package/dist/esm/domain/repository/aggregate-remover.js +1 -0
  209. package/dist/esm/domain/repository/aggregate-saver.d.ts +4 -0
  210. package/dist/esm/domain/repository/aggregate-saver.js +1 -0
  211. package/dist/esm/domain/specification/specification.d.ts +9 -0
  212. package/dist/esm/domain/specification/specification.js +40 -0
  213. package/dist/esm/domain/type/abstract-type.d.ts +20 -0
  214. package/dist/esm/domain/type/abstract-type.js +65 -0
  215. package/dist/esm/domain/type/array-type.d.ts +16 -0
  216. package/dist/esm/domain/type/array-type.js +61 -0
  217. package/dist/esm/domain/type/boolean-type.d.ts +4 -0
  218. package/dist/esm/domain/type/boolean-type.js +12 -0
  219. package/dist/esm/domain/type/composite/composite-field.d.ts +19 -0
  220. package/dist/esm/domain/type/composite/composite-field.js +59 -0
  221. package/dist/esm/domain/type/composite/composite.d.ts +26 -0
  222. package/dist/esm/domain/type/composite/composite.js +52 -0
  223. package/dist/esm/domain/type/date-type.d.ts +18 -0
  224. package/dist/esm/domain/type/date-type.js +29 -0
  225. package/dist/esm/domain/type/enum-type.d.ts +7 -0
  226. package/dist/esm/domain/type/enum-type.js +26 -0
  227. package/dist/esm/domain/type/equality/value-equality.d.ts +7 -0
  228. package/dist/esm/domain/type/equality/value-equality.js +37 -0
  229. package/dist/esm/domain/type/id-type.d.ts +7 -0
  230. package/dist/esm/domain/type/id-type.js +13 -0
  231. package/dist/esm/domain/type/immutable/immutable-date.d.ts +4 -0
  232. package/dist/esm/domain/type/immutable/immutable-date.js +33 -0
  233. package/dist/esm/domain/type/immutable/immutable.d.ts +4 -0
  234. package/dist/esm/domain/type/immutable/immutable.js +15 -0
  235. package/dist/esm/domain/type/index.d.ts +17 -0
  236. package/dist/esm/domain/type/index.js +14 -0
  237. package/dist/esm/domain/type/json-type.d.ts +5 -0
  238. package/dist/esm/domain/type/json-type.js +13 -0
  239. package/dist/esm/domain/type/number-type.d.ts +4 -0
  240. package/dist/esm/domain/type/number-type.js +12 -0
  241. package/dist/esm/domain/type/required/required-mark.d.ts +9 -0
  242. package/dist/esm/domain/type/required/required-mark.js +9 -0
  243. package/dist/esm/domain/type/required/required.d.ts +7 -0
  244. package/dist/esm/domain/type/required/required.js +18 -0
  245. package/dist/esm/domain/type/string-type.d.ts +4 -0
  246. package/dist/esm/domain/type/string-type.js +12 -0
  247. package/dist/esm/domain/type/uuid-type.d.ts +7 -0
  248. package/dist/esm/domain/type/uuid-type.js +23 -0
  249. package/dist/esm/domain/type/value-objects/value-objects.d.ts +16 -0
  250. package/dist/esm/domain/type/value-objects/value-objects.js +66 -0
  251. package/dist/esm/exceptions/abstract.exception.d.ts +23 -0
  252. package/dist/esm/exceptions/abstract.exception.js +62 -0
  253. package/dist/esm/exceptions/application/aggregate-not-found.exception.d.ts +5 -0
  254. package/dist/esm/exceptions/application/aggregate-not-found.exception.js +13 -0
  255. package/dist/esm/exceptions/application/application.exception.d.ts +6 -0
  256. package/dist/esm/exceptions/application/application.exception.js +13 -0
  257. package/dist/esm/exceptions/application/concurrency.exception.d.ts +5 -0
  258. package/dist/esm/exceptions/application/concurrency.exception.js +13 -0
  259. package/dist/esm/exceptions/application/not-allowed.exception.d.ts +5 -0
  260. package/dist/esm/exceptions/application/not-allowed.exception.js +13 -0
  261. package/dist/esm/exceptions/brand/exception-brand.d.ts +6 -0
  262. package/dist/esm/exceptions/brand/exception-brand.js +25 -0
  263. package/dist/esm/exceptions/domain/domain.exception.d.ts +6 -0
  264. package/dist/esm/exceptions/domain/domain.exception.js +13 -0
  265. package/dist/esm/exceptions/domain/required-value.exception.d.ts +4 -0
  266. package/dist/esm/exceptions/domain/required-value.exception.js +17 -0
  267. package/dist/esm/exceptions/domain/type-primitive.exception.d.ts +4 -0
  268. package/dist/esm/exceptions/domain/type-primitive.exception.js +17 -0
  269. package/dist/esm/exceptions/domain/validation.exception.d.ts +14 -0
  270. package/dist/esm/exceptions/domain/validation.exception.js +19 -0
  271. package/dist/esm/exceptions/exception-code.d.ts +15 -0
  272. package/dist/esm/exceptions/exception-code.js +16 -0
  273. package/dist/esm/exceptions/index.d.ts +14 -0
  274. package/dist/esm/exceptions/index.js +14 -0
  275. package/dist/esm/exceptions/infrastructure/event-delivery.exception.d.ts +5 -0
  276. package/dist/esm/exceptions/infrastructure/event-delivery.exception.js +16 -0
  277. package/dist/esm/exceptions/infrastructure/infrastructure.exception.d.ts +6 -0
  278. package/dist/esm/exceptions/infrastructure/infrastructure.exception.js +13 -0
  279. package/dist/esm/exceptions/infrastructure/internal-error.exception.d.ts +5 -0
  280. package/dist/esm/exceptions/infrastructure/internal-error.exception.js +13 -0
  281. package/dist/esm/exceptions/infrastructure/unknown-event.exception.d.ts +4 -0
  282. package/dist/esm/exceptions/infrastructure/unknown-event.exception.js +15 -0
  283. package/dist/esm/exceptions/received/received-value.d.ts +4 -0
  284. package/dist/esm/exceptions/received/received-value.js +9 -0
  285. package/dist/esm/infrastructure/clock/fixed-clock.d.ts +8 -0
  286. package/dist/esm/infrastructure/clock/fixed-clock.js +15 -0
  287. package/dist/esm/infrastructure/clock/system-clock.d.ts +4 -0
  288. package/dist/esm/infrastructure/clock/system-clock.js +5 -0
  289. package/dist/esm/infrastructure/event/domain-event-bus.d.ts +16 -0
  290. package/dist/esm/infrastructure/event/domain-event-bus.js +56 -0
  291. package/dist/esm/infrastructure/event/event-channel.d.ts +9 -0
  292. package/dist/esm/infrastructure/event/event-channel.js +1 -0
  293. package/dist/esm/infrastructure/event/event-registry.d.ts +9 -0
  294. package/dist/esm/infrastructure/event/event-registry.js +39 -0
  295. package/dist/esm/infrastructure/event/in-memory/emitter.d.ts +11 -0
  296. package/dist/esm/infrastructure/event/in-memory/emitter.js +50 -0
  297. package/dist/esm/infrastructure/event/in-memory/in-memory-event-channel.d.ts +16 -0
  298. package/dist/esm/infrastructure/event/in-memory/in-memory-event-channel.js +47 -0
  299. package/dist/esm/infrastructure/http/http-error-mapper.d.ts +23 -0
  300. package/dist/esm/infrastructure/http/http-error-mapper.js +59 -0
  301. package/dist/esm/infrastructure/index.d.ts +16 -0
  302. package/dist/esm/infrastructure/index.js +9 -0
  303. package/dist/esm/infrastructure/logging/console-logger.d.ts +17 -0
  304. package/dist/esm/infrastructure/logging/console-logger.js +33 -0
  305. package/dist/esm/infrastructure/logging/in-memory-logger.d.ts +15 -0
  306. package/dist/esm/infrastructure/logging/in-memory-logger.js +23 -0
  307. package/dist/esm/infrastructure/logging/log-serializer.d.ts +7 -0
  308. package/dist/esm/infrastructure/logging/log-serializer.js +55 -0
  309. package/dist/esm/infrastructure/logging/silent-logger.d.ts +7 -0
  310. package/dist/esm/infrastructure/logging/silent-logger.js +14 -0
  311. package/dist/esm/package.json +3 -0
  312. package/package.json +106 -4
@@ -0,0 +1,189 @@
1
+ # What not to do
2
+
3
+ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
4
+
5
+ - Putting a class in the wrong layer: a repository implementation, a mapper or a driver in the
6
+ domain; a listener or an event bus implementation in the domain; a use case importing
7
+ `/infrastructure`; business rules in an adapter or a controller.
8
+ - A use case that imports, extends or calls another use case, or a base use case with shared
9
+ helpers: move the rule to the aggregate or a domain service, or chain them with an event and a
10
+ listener.
11
+ - Building value objects inside `handle` from raw input, or passing raw strings to aggregates
12
+ from a use case: build them in `toCommand`.
13
+ - A listener without `@ListensTo`, decorated with two events, or one that holds business rules,
14
+ saves aggregates or calls several use cases.
15
+ - A listener consumer name taken from the class (`ListensTo(X, { consumer: this.name })`), shared by
16
+ two listeners or renamed casually: it is the listener's identity in the channel (its queue in a
17
+ broker), so it must be a stable literal, unique per listener.
18
+ - Subscribing listeners by a text name or calling them from a use case: the composition root
19
+ subscribes them with `bus.subscribe(listener)`, which reads their `@ListensTo`.
20
+ - Declaring `eventName()` as a method in an event: declare `static readonly eventName`.
21
+ - Importing the repository contracts from `@archi-code/ddd/application`: they live in `/domain`.
22
+ - Do not invent API: there is no root `@archi-code/ddd` import, no `toPrimitives()` in the base
23
+ classes, no generic `ValueObject` (a value object of several fields is `Composite({...})`), no
24
+ `setNow()`, `addItem()`, `setItem()` or `removeItem()`. When in doubt, check the installed
25
+ README.
26
+ - Writing the suite's base classes in the project (`domain/shared/AggregateRoot.ts`,
27
+ `DomainEvent.ts`, `ValueObject.ts`, an `EventPublisher` port): they come from the package.
28
+ - A computed getter (`get total()`, `get isEditable()`) on an aggregate, a child entity or a
29
+ composite: `PrimitiveTypes` takes it for a field and `toPrimitives()` stops compiling; write a
30
+ method.
31
+ - `ArrayType<OrderLine>` with a composite item: a list of composites is a `Composite` list field
32
+ (`Composite({ items: [OrderLine] })`) or a `OrderLine[]` field of the aggregate.
33
+ - A domain service or a repository received by the constructor of an aggregate: pass the service
34
+ or the loaded data as a method argument.
35
+ - Events named `OrderUpdated` carrying the whole state, named in present tense (`PlaceOrder` as an
36
+ event), carrying value objects, `toPrimitives()` or personal data that consumers do not need.
37
+ - Creating an event inside an aggregate method without the clock it received
38
+ (`new OrderPlaced(id.value, customer.value)`): `occurredOn` becomes the system time and tests stop
39
+ being deterministic.
40
+ - A registry factory that does not pass the JSON as metadata
41
+ (`(json) => new OrderShipped(json.aggregateId)`): the event gets a new `eventId`, a listener
42
+ cannot skip a redelivery, and `fromJSON` throws.
43
+ - Subscribing a listener of an event the `EventRegistry` does not know: `bus.subscribe` refuses it
44
+ with `UnknownEventException`; register every event a listener receives.
45
+ - Writing a second bus for a new technology (a `RabbitMqEventBus` with its own `publish` and
46
+ subscriptions): implement only `EventChannel` and keep `DomainEventBus`, so listeners and use
47
+ cases behave the same on every channel.
48
+ - A use case run by a listener that throws `AggregateNotFoundException` or a business error: the
49
+ channel redelivers it forever; return, or record an event that compensates.
50
+ - Retrying a `ConcurrencyException` with the same loaded aggregate, without a limit, or for a
51
+ command that is not safe to repeat: each attempt loads again and saves again, and only for
52
+ commands safe to repeat. A use case never calls itself nor another use case to retry, and one run
53
+ by a listener never retries: the channel's redelivery tries again.
54
+ - A read query that loads aggregates through `AggregateFinder`, or a repository method that
55
+ returns DTOs: reads go straight to the database through an application interface.
56
+ - Importing `@archi-code/ddd/infrastructure` from domain code, or `application` from the domain.
57
+ - Importing an exception from `@archi-code/ddd/domain`: exceptions live in
58
+ `@archi-code/ddd/exceptions`.
59
+ - Catching errors inside `handle` just to rethrow them as `ApplicationException`: let them
60
+ propagate; `execute` keeps package exceptions and wraps the rest.
61
+ - Calling `bus.publish` before `repository.save`, or publishing without `pullDomainEvents()`.
62
+ - Opening, joining or closing transactions in a use case, a listener or the domain, or passing a
63
+ transaction runner, unit of work, session or connection to them: the repository makes each
64
+ `save`/`remove` atomic and handles everything about persistence.
65
+ - Saving two aggregates in one use case without one of the exceptions IDDD ch. 10 accepts,
66
+ written down as a decision: one use case saves one aggregate once.
67
+ - Changing the use case so events are not lost between `save` and `publish`: the repository stores
68
+ them with the aggregate in the same atomic write (an outbox) and a relay delivers them.
69
+ - A listener that breaks when a channel delivers the same event twice: delivery is at least once;
70
+ skip an `eventId` already processed.
71
+ - Sending `event.toJSON()` of a domain event to another context: translate it to a published,
72
+ versioned contract.
73
+ - Changing the data of a stored or published event without raising `eventVersion` (or the
74
+ contract version).
75
+ - Repository methods with business names or rules (`pendingOlderThan`, `findOverdueInvoices`),
76
+ `nextId()` in a repository, or a use case returning the aggregate to the controller.
77
+ - Throwing `AggregateNotFoundException` from the domain or a repository: the use case throws it when
78
+ `findById` returns `null`; it is an application exception (404).
79
+ - Authorization checks in the aggregate, or business rules in a controller: authorization is the
80
+ use case's (`NotAllowedException`, 403); business rules are the aggregate's.
81
+ - A controller, consumer or CLI importing a repository or an adapter: entry points only call use
82
+ cases; the composition root wires everything.
83
+ - Folders named after patterns (`entities/`, `services/`, `valueobjects/`): name modules after the
84
+ business (`order/`, `invoicing/`).
85
+ - A use case that does several things (`UpdateOrder` that ships, cancels or renames depending on
86
+ the input) or that saves more than one aggregate outside the IDDD ch. 10 exceptions written down
87
+ as an explicit decision: split it into one use case per business action.
88
+ - Technical or CRUD names for use cases (`OrderService`, `UpdateUser`, `SaveOrder`,
89
+ `ProcessPayment` when the business says `CapturePayment`): name them after the business action.
90
+ - Declaring an all-in-one `Repository` interface in the project, or a use case that receives the
91
+ three roles when it only reads or only saves: ask for exactly the roles it uses.
92
+ - Looking for helpers such as `findOrFail` or `saveAndPublish` in `UseCase`: there are none; the
93
+ steps go in `handle`.
94
+ - Using `new Date()` inside use cases or aggregates: inject a `Clock`.
95
+ - An in-memory repository in `src/` or used in production: it lives in `test/support/`.
96
+ - `vo.toString` without parentheses: it is a method; use `vo.toString()`, `` `${vo}` `` or
97
+ `vo.value`.
98
+ - Mutating a VO or its value (`vo.value.setFullYear(...)`, pushing to `vo.value`): values are frozen
99
+ and date setters throw. Build a new VO; for arrays use `withItem`/`withoutItem` and keep the
100
+ returned instance (`this._tags = this._tags.withItem('x')`).
101
+ - Ignoring what `withItem`/`withoutItem` return: the original does not change.
102
+ - Comparing VOs or entities with `===`: use `equals()`.
103
+ - Calling `assertValid()`: it does not exist; the constructor already throws `ValidationException`.
104
+ - Building the value objects of an input one after another with `new` in `toCommand`: the first
105
+ invalid field hides the rest. Use `ValueObjects.build` so every field is reported.
106
+ - Moving a value object's rule to the request schema or the controller (`isEmail` in a DTO
107
+ validator instead of the `Email` value object): the adapter may check format for the user
108
+ (empty, length) [IDDD ch. 4], but the value object owns the rule and always checks it.
109
+ - Catching the `ValidationException` of a value object to keep an invalid value, or creating
110
+ `ValidationException` by hand from `validate()` results (a domain service that collects
111
+ `ValidationIssue`s of an existing object may throw `new ValidationException(issues)`).
112
+ - Sending local date-times without a zone (`'2024-01-31T10:00:00'`) to `DateType`: they are rejected.
113
+ - Passing unconverted values with their static type (`new NumberType('1.5')`): type error; conversion
114
+ is for `any`/`unknown` input.
115
+ - Using `Required(StringType)` directly as a property type instead of a named subclass.
116
+ - Declaring `private readonly _id` + `get id()` in an aggregate: the id goes to `super(id)`.
117
+ - An optional value object (`class Code extends StringType {}`) as the id of an entity or
118
+ aggregate: an id is always required (`IdType` or `Required(...)`); an empty id throws
119
+ `RequiredValueException`.
120
+ - Removing by id without loading the aggregate, or a `remove` with no version condition: `remove`
121
+ receives the loaded aggregate and refuses a stale one with `ConcurrencyException`.
122
+ - Putting listeners or event ports under `application/ports/` or a separate listener folder of the
123
+ package API: the event contract (`EventListener`, `@ListensTo`, `EventBus`) is imported from
124
+ `@archi-code/ddd/application`.
125
+ - Importing `DomainEventBus`, `EventRegistry`, `EventChannel`, `ChannelHandler`,
126
+ `ChannelSubscription` or `Subscription` from `@archi-code/ddd/application`, or using them in a use
127
+ case or a listener: they are infrastructure (`@archi-code/ddd/infrastructure`), used only by the
128
+ composition root and by channel implementations.
129
+ - A child entity as a plain class, with setters, or called from outside its aggregate: it extends
130
+ `Entity<Id>`, holds value objects, and its business methods are called only by its root.
131
+ - Modeling an amount with its currency, a date range or an address as loose fields of the
132
+ aggregate, as a `JsonType`, or as a child entity with an id nobody looks up: use `Composite`.
133
+ - `Required(Composite(...))`, or `Composite({ amount: Required(NumberType) })` without a named
134
+ class: every field class is a named subclass, and every field instance must be given (an
135
+ optional value object holding `null` is fine; an optional composite is a `Money | null` field of
136
+ the aggregate).
137
+ - Mutating a composite or adding state to it (instance fields, setters): it is frozen; return a new
138
+ instance from its methods.
139
+ - Returning a composite from `toPrimitives()` instead of `composite.toPrimitives()`.
140
+ - A specification that loads data through a repository, a generic one without a business name
141
+ (`FieldEquals`), or one used instead of the aggregate to protect an invariant.
142
+ - Raw state in an aggregate (`private _shipped: boolean`, `private _at: Date`, a `string` field):
143
+ wrap it in a value object.
144
+ - A private field without its public getter, or left out of `toPrimitives()`: it compiles and is
145
+ lost when saving (`PrimitiveTypes` does not see private fields, and nothing checks them at run
146
+ time); the round-trip test of each aggregate must cover every field.
147
+ - `toPrimitives()` returning value objects instead of `vo.value`.
148
+ - A repository that reads aggregate fields or builds rows by hand: call `order.toPrimitives()` and
149
+ `Order.fromPrimitives(row)`.
150
+ - Importing a persistence concern (a repository, a driver, a mapper) into domain code: it belongs to
151
+ infrastructure.
152
+ - A repository whose constructor receives an aggregate class, a mapper or
153
+ `{ toPrimitives, fromPrimitives }`: it receives only its technology (a collection, a pool) and
154
+ calls the aggregate's own methods.
155
+ - A generic repository shared by several aggregates: write one per aggregate.
156
+ - Converting an aggregate to or from primitives outside the aggregate (a domain service, a
157
+ hand-written mapper reading its fields): the aggregate owns `toPrimitives()` and
158
+ `static fromPrimitives()`.
159
+ - Declaring a `version` field in an aggregate, or putting it in `toPrimitives()`: the base already
160
+ has it, and the repository stores it next to the primitives.
161
+ - A repository (also the in-memory one of the tests) that saves with `upsert` and no version
162
+ condition, or "last save wins": a stale save must throw `ConcurrencyException`.
163
+ - Forgetting `markPersisted()` after a successful write (the next save of the same object becomes a
164
+ false conflict), or `Order.fromPrimitives(row)` without `.restoreVersion(storedVersion)` (every load looks
165
+ new, and the first save conflicts).
166
+ - Calling `restoreVersion()` or `markPersisted()` from domain or application code: they are for
167
+ repositories.
168
+ - Creating events without the aggregate id (`super()`), or generating `eventId`/`occurredOn`
169
+ yourself for a new event: the base does it.
170
+ - Wrapping or translating an error (`InternalErrorException`, `ConcurrencyException`,
171
+ `NotAllowedException`, `AggregateNotFoundException`) without `{ cause: error }`.
172
+ - `JsonType` with `{}` or with an array: throws `TypePrimitiveException` (it must be a non-empty
173
+ object).
174
+ - `IdType` with `00000000-0000-0000-0000-000000000000` or `ffffffff-ffff-ffff-ffff-ffffffffffff`:
175
+ throws `TypePrimitiveException`.
176
+ - An id class without its brand (`class OrderId extends IdType {}`): it compiles, but a `CustomerId`
177
+ is then accepted where an `OrderId` is expected. Write `class OrderId extends IdType<'OrderId'> {}`.
178
+ - Declaring an aggregate's VOs as `private` without getters and expecting `PrimitiveTypes` to see
179
+ them.
180
+ - Calling `record()` from outside the aggregate (it is `protected`) or forgetting `super(...)` in the
181
+ constructor of an aggregate or event.
182
+ - Importing `@Validations` from `@archi-code/ddd/domain`: it is imported from `@archi-code/validation`.
183
+ - Logging from the domain, or importing a logging library (pino, winston, `console`) in a use case:
184
+ inject the `Logger` port and choose the provider in the composition root.
185
+ - Logging an error and rethrowing it in every layer: the edge that handles it logs it once
186
+ (`HttpErrorMapper` and `DomainEventBus` with a logger already do).
187
+ - Interpolating data into the message (`` `order ${id} failed` ``) or logging passwords, tokens or
188
+ personal data that the entry does not need: keep a stable message and put only what is needed in
189
+ the context.
@@ -0,0 +1,63 @@
1
+ # Where each class goes
2
+
3
+ Part of the `archi-code-ddd` skill: read it only when that skill routes here.
4
+
5
+ | Class | Layer | May depend on | Never |
6
+ | ------------------------------------------------------------ | --------------------------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
7
+ | Value object, composite | domain | other value objects, `@archi-code/validation`, exceptions | mutate, know about storage or transport |
8
+ | Aggregate root (`extends AggregateRoot`) | domain | value objects, child entities, events; a domain service or loaded data as method argument | read a repository, publish, `new Date()`, services received by constructor |
9
+ | Child entity (`extends Entity`) | domain | its local id, value objects, business methods the root calls | setters, being called from outside the root, loaded or saved on its own |
10
+ | Domain event (`OrderPlaced`) | domain | primitives taken from value objects | behavior, value objects or the whole state in its payload |
11
+ | Repository contract (`AggregateFinder`…) | domain | the aggregate | business logic or queries, an all-in-one `Repository` |
12
+ | Domain service | domain | aggregates, value objects, specifications, repository contracts (to read), domain ports | I/O code, transactions, publishing |
13
+ | Domain port to another context or service (`ProductPricing`) | domain | value objects | remote DTOs, I/O code |
14
+ | Use case (`PlaceOrder`) | application | domain, ports, exceptions | import another use case or a listener, return an aggregate |
15
+ | Listener (`ReserveStockOnOrderPlaced`) | application | one use case (by constructor), the event | business rules, saving aggregates, calling several use cases |
16
+ | Port (`EventBus`, `Clock`, `Logger`, your own) | application | domain types | code |
17
+ | Read query (`OrderSummaries`, `StaleOrders`) | application (interface), infrastructure | DTOs and ids, `@archi-code/criteria` | loading aggregates, repositories, business rules |
18
+ | Adapter (repository, event channel, ACL translator, logger provider) | infrastructure | domain, application, exceptions, drivers | business rules |
19
+ | Event wiring (`DomainEventBus`, `EventRegistry`, `EventChannel` port and its implementations) | infrastructure (composition root) | `@archi-code/ddd/infrastructure`, domain events, listeners | being seen by use cases (they only see `EventBus`) or listeners |
20
+ | Entry point (controller, consumer, scheduler, CLI) | interfaces | application, exceptions, `HttpErrorMapper`; domain types only if unavoidable | business rules, repositories or other adapters |
21
+ | Composition root (`composition.ts`) | per context | everything | being imported by any layer |
22
+
23
+ - Dependencies only point inwards: the domain imports only `@archi-code/ddd/domain`,
24
+ `@archi-code/ddd/exceptions` and `@archi-code/validation` (not "nothing external" as the suite
25
+ says: these are the package convention); the application adds `@archi-code/ddd/application`;
26
+ only infrastructure and the composition root import `@archi-code/ddd/infrastructure`, with one
27
+ exception: an entry point may import `HttpErrorMapper` (or receive it from the composition root).
28
+ Entry points (`interfaces/`: controllers, message consumers, schedulers, CLI) call use cases
29
+ only. The composition root of each context is the only place that builds and connects everything
30
+ (repositories, event channel, `DomainEventBus`, `EventRegistry`, listeners) and reads
31
+ `process.env`. `DomainEventBus`, `EventRegistry` and the `EventChannel` port come from
32
+ `@archi-code/ddd/infrastructure`; a use case sees only `EventBus` and a listener only
33
+ `EventListener` + `@ListensTo`, both from `@archi-code/ddd/application`.
34
+ - Layout, with modules named after the ubiquitous language inside each zone, never after patterns
35
+ (`entities/`, `events/`, `valueobjects/`, `listeners/`); a listener lives next to the use case it
36
+ runs; a context never imports another one, only an explicit `shared-kernel/`:
37
+
38
+ ```
39
+ src/contexts/sales/
40
+ ├── domain/order/ Order, OrderId, OrderLine, OrderPlaced, ProductPricing (port)
41
+ ├── application/
42
+ │ ├── place-order/ PlaceOrder
43
+ │ ├── reserve-stock/ ReserveStock, ReserveStockOnOrderPlaced
44
+ │ └── order-summaries/ OrderSummaries (read query)
45
+ ├── infrastructure/
46
+ │ ├── persistence/ MongoOrderRepository, MongoOrderSummaries
47
+ │ ├── messaging/ RabbitMqEventChannel
48
+ │ └── catalog/ HttpProductPricing, CatalogTranslator (ACL)
49
+ ├── interfaces/http/ OrderController
50
+ └── composition.ts
51
+ ```
52
+
53
+ - Check the direction with dependency-cruiser (`tsPreCompilationDeps: true`) and the suite's
54
+ `ddd-skills/ddd-arquitectura/assets/dependency-cruiser.cjs`, which already allows the `@archi-code/ddd`
55
+ layers above and forbids importing `composition.ts`.
56
+ - Import from the layer, never from the package root (there is none): exceptions always come from
57
+ `@archi-code/ddd/exceptions` (never from `/domain`).
58
+ - Logic two use cases share never goes in a use case: a business rule becomes a method of the
59
+ aggregate or a domain service; a technical step becomes a port. When an action must follow
60
+ another, the first records an event and a listener runs the second.
61
+ - Names: use cases are business verbs (`PlaceOrder`), events are facts in the past
62
+ (`OrderPlaced`, never `OrderUpdated` nor present tense), listeners are `<Action>On<Event>`
63
+ (`ReserveStockOnOrderPlaced`, where `<Action>` is the use case it runs).
@@ -0,0 +1,114 @@
1
+ # Agregados
2
+
3
+ > Parte del skill `archi-code-ddd`: léela solo cuando ese skill te dirija aquí. Los ejemplos están escritos con la API de `@archi-code/ddd` (las clases de su `src`) y se copian tal cual; las guías son la fuente de verdad: el paquete solo se aparta de ellas donde lo marca `[Fuera de los libros: convención de @archi-code/ddd]`, y cualquier otra discrepancia es un defecto del paquete.
4
+ >
5
+ > **Cuándo consultar esta guía.** Diseño e implementación de agregados DDD en TypeScript, la guía central del diseño táctico. Cubre las cuatro reglas (proteger invariantes reales dentro del límite, agregados pequeños, referencias por identidad, consistencia eventual fuera del límite), el procedimiento para dimensionar agregados según los plazos de consistencia, la heurística de "¿de quién es el trabajo?", cuándo está justificado romper las reglas, la raíz del agregado, partes como value objects, fábricas, concurrencia optimista, registro de eventos de dominio y tests. Consúltala siempre que haya que decidir qué objetos van juntos, qué se guarda en una transacción, por qué hay conflictos de concurrencia o bloqueos, si un objeto debe contener una colección de otro, cómo coordinar cambios en varias entidades, o cuando aparezca un objeto enorme que carga medio modelo, aunque no se diga "agregado".
6
+
7
+ ## Con @archi-code/ddd
8
+
9
+ [Fuera de los libros: convención de @archi-code/ddd]
10
+
11
+ * La raíz no se escribe: extiende `AggregateRoot<Id, Event>` y llama a `super(id)`; la `version` de la concurrencia optimista está en la base, fuera del estado del agregado, y solo la tocan los repositorios (`restoreVersion()` al cargar, `markPersisted()` tras escribir).
12
+ * Todo el estado son value objects, composites o entidades hijas con getter público; constructor privado, una factoría de negocio y `fromPrimitives`, y `toPrimitives()` con datos planos (las seis reglas de `layers.md`).
13
+ * Las identidades nuevas salen del value object (`OrderId.generate()`), no del repositorio.
14
+ * Los métodos del dominio reciben `clock: { now(): Date }` y lo pasan a los eventos (`{ clock }`).
15
+ * La variante funcional (estado inmutable + funciones que devuelven `[estado, eventos]`) no la da el paquete: necesita clases `AggregateRoot` y `PrimitiveTypes`.
16
+ * El agregado registra sus eventos con `record()` y el caso de uso, tras `await this.orders.save(order)`, los publica con `events.publish(order.pullDomainEvents())`. La aplicación no abre transacciones: la transacción es del repositorio, que hace atómico cada `save`; si un evento no puede perderse, el repositorio lo guarda con el agregado en la misma escritura (outbox) y un proceso aparte lo entrega por un `EventChannel`.
17
+ * Una política se parte en un listener `<Acción>On<Evento>` con `@ListensTo` y el caso de uso que ejecuta.
18
+
19
+ Un agregado es un grupo de entidades y value objects con una entidad raíz que lo nombra y lo controla, y que forma un **límite de consistencia transaccional**: al confirmar la transacción, todo lo de dentro cumple las reglas del negocio [Destilado cap. 5]. No es un grafo de objetos cómodo de navegar; es una decisión sobre consistencia [IDDD cap. 10].
20
+
21
+
22
+ ## Las cuatro reglas
23
+
24
+ [Destilado cap. 5; IDDD cap. 10]
25
+
26
+ 1. **Protege las invariantes reales dentro del límite.** Una invariante es una regla que debe cumplirse siempre, de forma inmediata y atómica. Es negocio quien decide qué tiene que ser consistente al confirmar.
27
+ 2. **Diseña agregados pequeños.** Raíz y los mínimos atributos y value objects necesarios: todos los que deben ser consistentes entre sí, ninguno más. Un caso real que cita IDDD: en torno al 70 % de los agregados de un proyecto tenían solo la raíz con value objects, y el resto dos o tres entidades.
28
+ 3. **Referencia a otros agregados solo por su identidad.** `productId: ProductId`, nunca `product: Product`.
29
+ 4. **Actualiza otros agregados con consistencia eventual.** Un agregado publica un evento de dominio; un suscriptor modifica el otro en **otra** transacción.
30
+
31
+ Corolario operativo: **una transacción modifica una sola instancia de agregado** [Destilado cap. 5; IDDD cap. 10]. Eso implica que cada petición de usuario debería ejecutar un comando sobre un agregado [IDDD cap. 10]. Con el paquete, esa transacción es una escritura atómica por agregado que garantiza el repositorio en su `save`; el caso de uso carga un agregado y lo guarda una vez `[Fuera de los libros: convención de @archi-code/ddd]`.
32
+
33
+ ## Flujo de diseño
34
+
35
+ ### Paso 1. Partir de los candidatos
36
+
37
+ Lista los agregados candidatos con sus comandos y eventos: sustantivos del negocio con ciclo de vida que reciben comandos.
38
+
39
+ ### Paso 2. Dimensionar con el procedimiento de Vernon
40
+
41
+ [Destilado cap. 5]
42
+
43
+ 1. Empieza con **un agregado por entidad**, cada uno solo con su raíz. Ponle lo necesario para identificarlo, encontrarlo y dejarlo en un estado inicial válido.
44
+ 2. Para cada agregado A1, lista qué otros agregados deben actualizarse cuando A1 cambia.
45
+ 3. Para cada reacción, fija **cuánto puede tardar**: inmediatamente, o en N segundos, minutos, horas o días.
46
+ 4. Lo que debe ser **inmediato**: considera fusionar ambas entidades en un único agregado.
47
+ 5. Lo que admite **plazo**: consistencia eventual con eventos de dominio.
48
+
49
+ Desconfía de "inmediato" para todo: con usuarios concurrentes, un agregado grande falla en las transacciones y consume mucha memoria [Destilado cap. 5].
50
+
51
+ ### Paso 3. Desempatar con "¿de quién es el trabajo?"
52
+
53
+ Si no está claro si algo es transaccional o eventual: si es trabajo **del usuario que ejecuta el caso de uso** dejar los datos consistentes, intenta consistencia transaccional (respetando las demás reglas). Si es trabajo de otro usuario o del sistema, eventual [IDDD cap. 10, guía atribuida a Evans].
54
+
55
+ ### Paso 4. Desconfiar de los casos de uso que tocan varios agregados
56
+
57
+ Un caso de uso que pide modificar varios agregados en una transacción puede indicar una invariante que falta (y un concepto nuevo), o simplemente que el caso de uso debe reescribirse con consistencia eventual y un retraso aceptable. Cuidado con fusionar agregados y acabar en uno gigante [IDDD cap. 10].
58
+
59
+ ### Paso 5. Elegir bien la abstracción
60
+
61
+ Modela el lenguaje ubicuo explícitamente. Nada de `ScrumElement` con un `typeName` para cubrir "todos los casos futuros": el lenguaje no coincide con el del negocio, aparecen casos especiales, sobra código y nunca se acierta con el futuro [Destilado cap. 5].
62
+
63
+ ### Paso 6. Implementar
64
+
65
+ Guía completa en `ddd-skills/ddd-agregados/references/implementacion-ts.md`. Lo esencial:
66
+
67
+ * La raíz tiene identidad global; las entidades internas solo identidad local [IDDD cap. 10].
68
+ * Partes internas como **value objects** siempre que se puedan reemplazar enteras [IDDD cap. 10].
69
+ * Comportamiento con nombres del lenguaje; sin setters públicos. Ley de Demeter y "Tell, Don't Ask": los clientes piden a la raíz que haga algo, no navegan por dentro [IDDD cap. 10].
70
+ * **No inyectes repositorios ni servicios en el agregado.** Busca antes lo que necesite (en el servicio de aplicación) y pásaselo al método [IDDD cap. 10].
71
+ * **Concurrencia optimista** con versión en la raíz [IDDD cap. 10]: la `version` de `AggregateRoot`, que el repositorio comprueba al guardar (`ConcurrencyException`).
72
+ * El agregado **registra** sus eventos (`record`); el caso de uso los publica tras guardar con `events.publish(order.pullDomainEvents())` (ver `ddd-skills/ddd-eventos-dominio/GUIA.md`).
73
+ * **Distingue comandos de hechos.** Un comando de usuario puede rechazarse con un error de negocio. Un método que registra un hecho ya ocurrido en otro agregado o contexto es idempotente y no lanza: si el hecho no encaja, registra un evento de negocio para que otra política compense [Destilado cap. 6]. Una excepción en un consumidor de mensajes provoca reintentos sin fin.
74
+ * Creación con **fábricas**: método en la raíz de otro agregado cuando el nuevo nace de él (`customer.placeOrder(...)`), o servicio cuando la creación necesita traducir de otro contexto [IDDD cap. 11; PHP: Factories]. Ver `ddd-skills/ddd-agregados/references/fabricas.md`.
75
+
76
+ ### Paso 7. Coordinar agregados
77
+
78
+ Consistencia eventual dentro y fuera del contexto: suscriptores (`EventListener` + caso de uso), reintentos, compensaciones y procesos. `ddd-skills/ddd-agregados/references/consistencia-eventual.md`.
79
+
80
+ ## Cuándo romper las reglas
81
+
82
+ Solo con un buen motivo. IDDD cita cuatro [IDDD cap. 10]:
83
+
84
+ | Motivo | Condición para que sea aceptable |
85
+ |---|---|
86
+ | Comodidad de interfaz: crear varios agregados en lote | Crear en lote no cambia nada semánticamente frente a crearlos de uno en uno; cada uno protege sus invariantes |
87
+ | Falta de mecanismos técnicos (sin mensajería, temporizadores ni procesos en segundo plano) | Afinidad usuario-agregado (un solo usuario trabaja sobre ese conjunto) y concurrencia optimista |
88
+ | Transacciones globales impuestas (dos fases) | Aun así, intenta no modificar varios agregados del propio contexto |
89
+ | Rendimiento de consultas | Referencia directa a otro agregado, valorando el coste |
90
+
91
+ ## Señales de alarma
92
+
93
+ * Un agregado con colecciones que crecen sin límite (`Product.backlogItems` con miles de elementos) [Destilado cap. 5; IDDD cap. 10].
94
+ * Fallos frecuentes por concurrencia en un agregado concreto.
95
+ * Propiedades que son otro agregado completo (`order.customer.address.city`).
96
+ * Servicios de aplicación que guardan dos o más agregados en el mismo caso de uso "porque sí", o que reciben una dependencia `transactions`/`unitOfWork` para agruparlos.
97
+ * Un agregado que recibe un repositorio por constructor.
98
+ * Setters públicos o un `update(dto)` genérico [Destilado cap. 5].
99
+ * Lógica de negocio en servicios de aplicación o en clases `Helper`/`Utils` [Destilado cap. 5].
100
+ * Relaciones bidireccionales mantenidas solo por comodidad o por la versión de concurrencia [IDDD cap. 10].
101
+
102
+ ## Programación funcional
103
+
104
+ Con un estilo funcional, separar datos y comportamiento no es anemia: el agregado es un tipo inmutable y las funciones devuelven un estado nuevo o un evento. Parte de esta guía orientada a objetos no aplica tal cual [Destilado cap. 5]. `ddd-skills/ddd-agregados/references/implementacion-ts.md` incluye una variante funcional escrita con los value objects y eventos del paquete; el paquete no da su base ni su persistencia `[Fuera de los libros: convención de @archi-code/ddd]`.
105
+
106
+ ## Ejemplo completo
107
+
108
+ `ddd-skills/ddd-agregados/references/ejemplo-ecommerce.md` aplica el procedimiento de dimensionado a Pedido, Pago y Reserva de stock, con la tabla de reacciones y plazos; el código del agregado `Order` está en `ddd-skills/ddd-agregados/references/implementacion-ts.md` y la política que registra la reserva, en `ddd-skills/ddd-agregados/references/consistencia-eventual.md`.
109
+
110
+ ## Siguiente guía
111
+
112
+ * Diseño, publicación y consumo de eventos → `ddd-skills/ddd-eventos-dominio/GUIA.md`.
113
+ * Repositorio del agregado → `ddd-skills/ddd-repositorios-persistencia/GUIA.md`.
114
+ * Casos de uso que cargan, invocan y guardan agregados → `ddd-skills/ddd-servicios-capa-aplicacion/GUIA.md`.
@@ -0,0 +1,159 @@
1
+ # Consistencia eventual entre agregados
2
+
3
+ ## Mecanismo básico
4
+
5
+ Un método de comando del agregado publica (o registra) un evento de dominio. Uno o varios suscriptores asíncronos lo reciben; cada uno carga **otro** agregado y ejecuta su comportamiento en **su propia transacción** [IDDD cap. 10; Destilado cap. 5]. Con el paquete, esa transacción es el `save` atómico del repositorio de ese otro agregado `[Fuera de los libros: convención de @archi-code/ddd]`.
6
+
7
+ El suscriptor puede estar en el mismo contexto (en el ejemplo del libro, `Sprint` reacciona a `BacklogItemCommitted` creando un `CommittedBacklogItem`) o en otro. Para el mismo contexto no hace falta un middleware de mensajería, aunque es fácil usarlo si ya existe [Destilado cap. 5].
8
+
9
+ ## Fallos y reintentos
10
+
11
+ * Si el suscriptor falla por concurrencia, no confirma el mensaje; el mecanismo lo reenvía y se reintenta en una nueva transacción (con el paquete, el caso de uso vuelve a cargar y a guardar en la nueva entrega; no reintenta por su cuenta), hasta lograrlo o hasta un límite de reintentos [IDDD cap. 10].
12
+ * Si falla del todo: compensar o, como mínimo, informar del fallo para intervención [IDDD cap. 10]. Con el paquete, los reintentos y el límite son del `EventChannel` que implementes (por ejemplo, una cola con reintentos y una dead-letter donde quedan los mensajes que agotan el límite); `InMemoryEventChannel` no reintenta: el error del listener llega a quien llamó a `publish` `[Fuera de los libros: convención de @archi-code/ddd]`.
13
+ * El suscriptor debe ser idempotente: la mensajería entrega al menos una vez [Destilado cap. 4].
14
+
15
+ ## No crear asociaciones bidireccionales
16
+
17
+ El evento no está para mantener un puntero inverso sin sentido. En IDDD, `BacklogItem` ya conoce su `SprintId`; el evento sirve para que `Sprint` registre el compromiso con su propio orden, que es un concepto suyo [IDDD cap. 10].
18
+
19
+ ## Políticas: el nombre del lenguaje para "cuando pase X, haz Y"
20
+
21
+ `[Fuera de los libros como término]`
22
+
23
+ Con el paquete, la política se parte en un listener `<Acción>On<Evento>` (con `@ListensTo`) que solo traduce el evento a la entrada de un caso de uso, y ese caso de uso carga, invoca y guarda `[Fuera de los libros: convención de @archi-code/ddd]`. Política de Ventas: cuando Almacén reserva stock, se registra en el pedido (`Order`, `OrderId`, `ReservationId` y `OrderEvent` son los de `references/implementacion-ts.md`).
24
+
25
+ El hecho de Almacén llega por el canal con su contrato publicado; Ventas declara una clase `EventBase` con ese contrato para que `DomainEventBus` lo reconstruya y se lo entregue al listener `[Fuera de los libros: convención de @archi-code/ddd]`:
26
+
27
+ ```ts
28
+ import { EventBase } from '@archi-code/ddd/domain';
29
+ import type { EventMetadata } from '@archi-code/ddd/domain';
30
+
31
+ export class StockReserved extends EventBase {
32
+ static readonly eventName = 'warehouse.stock.StockReserved';
33
+
34
+ constructor(
35
+ stockItemId: string,
36
+ readonly orderId: string,
37
+ readonly reservationId: string,
38
+ metadata?: EventMetadata,
39
+ ) {
40
+ super(stockItemId, metadata);
41
+ }
42
+ }
43
+ ```
44
+
45
+ El caso de uso registra el hecho: un agregado, un `save` atómico, y si el pedido no existe (es de otro canal) vuelve sin error:
46
+
47
+ ```ts
48
+ import { ValueObjects } from '@archi-code/ddd/domain';
49
+ import type { AggregateFinder, AggregateSaver } from '@archi-code/ddd/domain';
50
+ import { UseCase } from '@archi-code/ddd/application';
51
+ import type { Clock, EventBus } from '@archi-code/ddd/application';
52
+
53
+ interface StockReservationInput {
54
+ orderId: string;
55
+ reservationId: string;
56
+ }
57
+
58
+ interface StockReservationCommand {
59
+ orderId: OrderId;
60
+ reservationId: ReservationId;
61
+ }
62
+
63
+ export class RegisterStockReservation extends UseCase<
64
+ StockReservationInput,
65
+ StockReservationCommand,
66
+ void
67
+ > {
68
+ constructor(
69
+ private readonly orders: AggregateFinder<Order> & AggregateSaver<Order>,
70
+ private readonly events: EventBus<OrderEvent>,
71
+ private readonly clock: Clock,
72
+ ) {
73
+ super();
74
+ }
75
+
76
+ protected toCommand(input: StockReservationInput): StockReservationCommand {
77
+ return ValueObjects.build({
78
+ orderId: () => new OrderId(input.orderId),
79
+ reservationId: () => new ReservationId(input.reservationId),
80
+ });
81
+ }
82
+
83
+ protected async handle(command: StockReservationCommand): Promise<void> {
84
+ const order = await this.orders.findById(command.orderId);
85
+ if (order === null) {
86
+ return;
87
+ }
88
+ order.registerStockReservation(command.reservationId, this.clock);
89
+ await this.orders.save(order);
90
+ await this.events.publish(order.pullDomainEvents());
91
+ }
92
+ }
93
+ ```
94
+
95
+ El listener solo traduce el evento a la entrada del caso de uso:
96
+
97
+ ```ts
98
+ import { EventListener, ListensTo } from '@archi-code/ddd/application';
99
+
100
+ @ListensTo(StockReserved, { consumer: 'register-stock-reservation-on-stock-reserved' })
101
+ export class RegisterStockReservationOnStockReserved extends EventListener<StockReserved> {
102
+ constructor(private readonly registerStockReservation: RegisterStockReservation) {
103
+ super();
104
+ }
105
+
106
+ async handle(event: StockReserved): Promise<void> {
107
+ await this.registerStockReservation.execute({
108
+ orderId: event.orderId,
109
+ reservationId: event.reservationId,
110
+ });
111
+ }
112
+ }
113
+ ```
114
+
115
+ La raíz de composición registra el evento y suscribe el listener; en los tests, con los adaptadores en memoria del paquete y el `InMemoryOrderRepository` que el proyecto escribe en `test/support/` (`ddd-skills/ddd-repositorios-persistencia/references/tests-repositorios.md`). En `test/contexts/sales/application/register-stock-reservation.spec.ts`:
116
+
117
+ ```ts
118
+ import {
119
+ DomainEventBus,
120
+ EventRegistry,
121
+ FixedClock,
122
+ InMemoryEventChannel,
123
+ } from '@archi-code/ddd/infrastructure';
124
+ import { InMemoryOrderRepository } from '../../../support/in-memory-order-repository';
125
+
126
+ const orders = new InMemoryOrderRepository();
127
+ const registry = new EventRegistry<OrderEvent | StockReserved>().register(
128
+ StockReserved,
129
+ (json) =>
130
+ new StockReserved(json.aggregateId, json.orderId as string, json.reservationId as string, json),
131
+ );
132
+ const channel = new InMemoryEventChannel();
133
+ const bus = new DomainEventBus<OrderEvent | StockReserved>(channel, registry);
134
+ await bus.subscribe(
135
+ new RegisterStockReservationOnStockReserved(
136
+ new RegisterStockReservation(orders, bus, new FixedClock('2026-03-01T09:30:00Z')),
137
+ ),
138
+ );
139
+ ```
140
+
141
+ En producción se cambia solo el canal (`InMemoryEventChannel` por el de tu broker o tu outbox); los casos de uso y los listeners no cambian.
142
+
143
+ Reglas para políticas:
144
+ * Una política, un agregado modificado.
145
+ * Nombre con el lenguaje del negocio, no `StockReservedHandler`. Con el paquete, el listener se llama `<Acción>On<Evento>`, donde `<Acción>` es el caso de uso que ejecuta (`RegisterStockReservationOnStockReserved`), y su `consumer` es un nombre estable y único, nunca el de la clase.
146
+ * La regla de negocio está en el agregado; la política solo lo invoca.
147
+ * El método invocado no lanza errores de negocio (ver "Comandos frente a hechos" en `references/implementacion-ts.md`). Los errores técnicos (base de datos caída, `ConcurrencyException`) sí se propagan para que la mensajería reintente.
148
+ * El listener es idempotente: si el canal entrega dos veces el mismo `eventId`, el segundo no cambia nada (aquí lo garantiza el agregado, que ignora una reserva cuando ya no está pendiente).
149
+
150
+ ## Plazos y lo que ve el usuario
151
+
152
+ Si el plazo aceptado es "en 30 segundos", el sistema debe comportarse bien durante esos 30 segundos:
153
+ * Estados intermedios explícitos en el modelo (`Pending`) [IDDD cap. 3 trata la indisponibilidad como estado válido].
154
+ * Mensajes al usuario que reflejen el estado ("Estamos confirmando tu pedido").
155
+ * Un temporizador o rastreador de plazo para el caso en que no llegue nunca (ver procesos en `ddd-skills/ddd-context-mapping/GUIA.md`).
156
+
157
+ ## Procesos con varios pasos
158
+
159
+ Cuando hay varios agregados y plazos, un objeto de proceso con su propio estado (confirmación del pedido: esperando reserva y pago, confirmado, fallido, caducado) coordina los pasos [IDDD cap. 13]. Ese objeto es en sí un agregado pequeño: se modifica solo, en su transacción, al recibir cada evento.
@@ -0,0 +1,52 @@
1
+ # Ejemplo: dimensionar los agregados de la tienda online
2
+
3
+ ## Paso 1. Un agregado por entidad
4
+
5
+ Candidatos: `Order` (Ventas), `Payment` (Pagos), `StockItem` con sus reservas (Almacén), `Shipment` (Almacén), `Customer` (Ventas).
6
+
7
+ ## Pasos 2 y 3. Reacciones y plazos
8
+
9
+ | Cuando cambia | Debe reaccionar | Plazo aceptado |
10
+ |---|---|---|
11
+ | Order realizado | StockItem reserva | 1 minuto |
12
+ | Order realizado | Payment autoriza | 1 minuto |
13
+ | StockItem reservado + Payment autorizado | Order confirmado | 30 segundos |
14
+ | Order cancelado | StockItem libera reserva | 10 minutos |
15
+ | Shipment enviado | Payment captura | 1 día |
16
+ | Order realizado | Customer: "último pedido" para marketing | 1 día |
17
+ | Línea de pedido cambia | Total del pedido | **Inmediato** |
18
+
19
+ ## Pasos 4 y 5. Decisión
20
+
21
+ * Líneas y total: inmediato → dentro de `Order` (como value objects).
22
+ * Todo lo demás admite plazo → agregados separados y consistencia eventual.
23
+ * "Último pedido" del cliente: no se toca `Customer` en la transacción del pedido; es una proyección para marketing.
24
+
25
+ Prueba con "¿de quién es el trabajo?": confirmar el pedido no es trabajo del cliente que compra, sino del sistema cuando llegan reserva y pago → eventual. Por eso no hay un comando "confirmar" que alguien invoque (`order.confirm(authorization)`), sino un pedido que se confirma solo al registrar las dos condiciones.
26
+
27
+ ## Resultado
28
+
29
+ ```mermaid
30
+ flowchart LR
31
+ subgraph Ventas
32
+ O["Order<br/>(raíz + OrderLines + ShippingAddress<br/>+ stockReservation + PaymentAuthorization)"]
33
+ C["Customer"]
34
+ end
35
+ subgraph Almacén
36
+ S["StockItem<br/>(raíz + reservas)"]
37
+ SH["Shipment"]
38
+ end
39
+ subgraph Pagos
40
+ P["Payment"]
41
+ end
42
+ O -- OrderPlaced --> S
43
+ O -- OrderPlaced --> P
44
+ S -- StockReserved --> O
45
+ P -- PaymentAuthorized --> O
46
+ O -- OrderCancelled --> S
47
+ SH -- ShipmentDispatched --> P
48
+ ```
49
+
50
+ `StockItem` sí agrupa sus reservas: la invariante "no reservar más unidades de las disponibles" es inmediata y la protege él. Si un mismo `StockItem` sufre muchos conflictos de concurrencia en campañas, la alternativa es partir el stock por almacén o por lote, no meter pedidos dentro.
51
+
52
+ El código de `Order` (sobre `AggregateRoot<OrderId, OrderEvent>`, con `OrderLines`, `ShippingAddress` y `PaymentAuthorization` como `Composite`) está en `ddd-skills/ddd-agregados/references/implementacion-ts.md`. Cada flecha que llega a `Order` es un listener `<Acción>On<Evento>` que ejecuta un caso de uso (`RegisterStockReservationOnStockReserved` → `RegisterStockReservation`, y lo mismo para `PaymentAuthorized` → `RegisterPaymentAuthorization`); el primero está escrito en `ddd-skills/ddd-agregados/references/consistencia-eventual.md`.